@nebutra/tokens 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/brands/gsap/brand.json +17 -64
  2. package/brands/linear/brand.json +18 -57
  3. package/brands/notion/brand.json +18 -62
  4. package/brands/raycast/brand.json +17 -64
  5. package/brands/stripe/brand.json +16 -72
  6. package/brands/vanta/brand.json +16 -63
  7. package/brands/vercel/brand.json +16 -50
  8. package/dist/brand-package/index.d.ts +123 -0
  9. package/dist/brand-package/index.js +61 -0
  10. package/dist/brand-package/index.js.map +1 -0
  11. package/dist/brand-package/use-brand.d.ts +2 -0
  12. package/dist/brand-package/use-brand.js +12 -0
  13. package/dist/brand-package/use-brand.js.map +1 -0
  14. package/dist/chunk-ALJTP5HL.js +881 -0
  15. package/dist/chunk-ALJTP5HL.js.map +1 -0
  16. package/dist/chunk-RGUJQOLH.js +1582 -0
  17. package/dist/chunk-RGUJQOLH.js.map +1 -0
  18. package/dist/index.d.ts +81 -0
  19. package/dist/index.js +187 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/use-brand-C8d4DPqE.d.ts +371 -0
  22. package/package.json +34 -9
  23. package/recipe.css +322 -1
  24. package/skins/README.md +2 -4
  25. package/skins/gsap.css +31 -119
  26. package/skins/linear.css +33 -117
  27. package/skins/notion.css +27 -117
  28. package/skins/raycast.css +32 -119
  29. package/skins/stripe.css +27 -129
  30. package/skins/vanta.css +27 -122
  31. package/skins/vercel.css +30 -111
  32. package/styles.css +388 -113
  33. package/.turbo/turbo-build.log +0 -8
  34. package/.turbo/turbo-typecheck.log +0 -4
  35. package/AGENTS.md +0 -45
  36. package/DESIGN.md +0 -357
  37. package/scripts/compile-brand-run.ts +0 -79
  38. package/scripts/compile-brand.mjs +0 -25
  39. package/scripts/emit-skins-run.ts +0 -62
  40. package/scripts/emit-skins.mjs +0 -31
  41. package/scripts/sync-styles.mjs +0 -29
  42. package/src/brand-package/__tests__/compile-refero.test.ts +0 -427
  43. package/src/brand-package/__tests__/emit-css.test.ts +0 -130
  44. package/src/brand-package/apply-brand.ts +0 -92
  45. package/src/brand-package/compile-helpers.ts +0 -170
  46. package/src/brand-package/compile-refero.ts +0 -69
  47. package/src/brand-package/emit-css.ts +0 -468
  48. package/src/brand-package/hex-to-hsl.ts +0 -100
  49. package/src/brand-package/index.ts +0 -71
  50. package/src/brand-package/infer-recipe.ts +0 -198
  51. package/src/brand-package/normalize.ts +0 -295
  52. package/src/brand-package/presets/context.ts +0 -17
  53. package/src/brand-package/presets/generic.ts +0 -214
  54. package/src/brand-package/presets/gsap.ts +0 -146
  55. package/src/brand-package/presets/index.ts +0 -31
  56. package/src/brand-package/presets/linear.ts +0 -183
  57. package/src/brand-package/presets/notion.ts +0 -208
  58. package/src/brand-package/presets/raycast.ts +0 -138
  59. package/src/brand-package/presets/recipe.ts +0 -61
  60. package/src/brand-package/presets/stripe.ts +0 -207
  61. package/src/brand-package/presets/vanta.ts +0 -220
  62. package/src/brand-package/presets/vercel.ts +0 -187
  63. package/src/brand-package/types.ts +0 -285
  64. package/src/brand-package/use-brand.ts +0 -207
  65. package/src/brand-package/validate.ts +0 -108
  66. package/src/index.ts +0 -67
  67. package/src/theme-provider.tsx +0 -246
  68. package/tsconfig.json +0 -11
  69. package/turbo.json +0 -15
@@ -1,8 +0,0 @@
1
-
2
- > @nebutra/tokens@0.1.2 build /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/design/tokens
3
- > node scripts/sync-styles.mjs && node scripts/emit-skins.mjs
4
-
5
- Formatted 1 file in 9ms. No fixes applied.
6
- styles.css refreshed from @nebutra/design-tokens
7
- emit-skins: 7 skins from brand.json → skins/*.css
8
- gsap, linear, notion, raycast, stripe, vanta, vercel
@@ -1,4 +0,0 @@
1
-
2
- > @nebutra/tokens@0.1.2 typecheck /Users/tseka_luk/Documents/Nebutra-SaaS-Lab/Nebutra-Sailor/packages/design/tokens
3
- > tsc --noEmit
4
-
package/AGENTS.md DELETED
@@ -1,45 +0,0 @@
1
- # AGENTS.md — packages/tokens
2
-
3
- Execution contract for Nebutra's runtime design-token package.
4
-
5
- ## Scope
6
-
7
- Applies to everything under `packages/design/tokens/`.
8
-
9
- This package is the canonical runtime token layer for Nebutra apps. It owns the
10
- base semantic CSS variables and the light/dark theme-provider surface.
11
-
12
- ## Source Of Truth
13
-
14
- - Public package surface and exports: `package.json`, `src/index.ts`
15
- - Canonical runtime token definitions: `styles.css` (generated from `@nebutra/design-tokens` via `sync-styles.mjs`; keep `verify:parity` at 100%)
16
-
17
- ## Contract Boundaries
18
-
19
- - Treat `styles.css` as the single source of truth for runtime token names,
20
- semantic aliases, and Tailwind-consumable CSS variables. Do not redefine core
21
- token names in apps or sibling packages.
22
- - Keep `THEME_IDS` and `DEFAULT_THEME` in `src/index.ts` aligned with the
23
- actual light/dark behavior implemented in `styles.css`.
24
- - This package owns the base light/dark token system only. Do not move named
25
- product-mood presets here. **Design-language catalog & global swap** belong in
26
- `@nebutra/theme` (`languages.json`, `applyLanguage`, `skins.css`). Brand Package
27
- *engine* (compile / emit / normalize) stays in this package (`brand-package/`).
28
- Multi-mood oklch catalogs were deleted — never reintroduce a second root `@theme` primary.
29
- - Preserve the package boundary between CSS tokens and component usage.
30
- `@nebutra/ui` and apps should consume the exported CSS surface rather than
31
- importing token internals.
32
- - Because `./styles.css` is an exported side-effect file, treat renames or
33
- variable removals as cross-repo breaking changes even if TypeScript passes.
34
-
35
- ## Generated And Derived Files
36
-
37
- - `tsconfig.tsbuildinfo` and compiler artifacts are derived files.
38
- - Consumer app compiled CSS output is derived from `styles.css`.
39
-
40
- ## Validation
41
-
42
- - Token or export changes:
43
- `pnpm --filter @nebutra/tokens typecheck`
44
- - When token names or theme IDs change, verify at least one consuming package
45
- updates with the same change instead of relying on stale compiled CSS.
package/DESIGN.md DELETED
@@ -1,357 +0,0 @@
1
- # `@nebutra/tokens` — Design Spec
2
-
3
- > Runtime token layer of the Nebutra-Sailor design system.
4
- > Part of the [root DESIGN.md](../../DESIGN.md). Spec format: `design-md@2026.05`.
5
-
6
- | Field | Value |
7
- |------|------|
8
- | Package | `@nebutra/tokens` |
9
- | Status | **★ Source of truth for runtime apps** |
10
- | Source file | `packages/design/tokens/styles.css` |
11
- | Tailwind integration | Tailwind v4 `@theme inline` block in same file |
12
- | Re-exports | Custom `ThemeProvider` / `useTheme` (cookie + class; not next-themes) |
13
-
14
- ---
15
-
16
- ## 1. Identity
17
-
18
- This package emits the **runtime CSS variables** consumed by every Nebutra app.
19
-
20
- ### App consumption contract (package-owned)
21
-
22
- Apps should **not** hand-roll monorepo `@source` paths. Import the UI entry:
23
-
24
- ```css
25
- /* Simple apps */
26
- @import "@nebutra/ui/styles/preset.css";
27
-
28
- /* Or: tokens yourself + package-owned source scan */
29
- @import "tailwindcss";
30
- @import "@nebutra/tokens/styles.css";
31
- @import "@nebutra/ui/styles/sources.css";
32
- ```
33
-
34
- | Layer | Entry | Role |
35
- |-------|-------|------|
36
- | Tokens | `@nebutra/tokens/styles.css` | CSS variables + `@theme` only |
37
- | UI sources | `@nebutra/ui/styles/sources.css` | Tailwind `@source` for CVA classes (owned by UI package) |
38
- | UI preset | `@nebutra/ui/styles/preset.css` | tailwind + tokens + sources + fonts |
39
-
40
- Tokens alone never style `Input`/`Button` — utilities come from scanning UI.
41
-
42
- Tokens are organized in three concentric rings:
43
-
44
- ```
45
- Brand scales (raw) → --nebutra-blue-{50..950}, --nebutra-cyan-{50..950}, --nebutra-neutral-{50..950}
46
- 12-step semantic scales → --neutral-{1..12}, --blue-{1..12}, --cyan-{1..12}
47
- Aliased semantic tokens → --background, --foreground, --primary, --brand-primary, --status-*
48
- ```
49
-
50
- ---
51
-
52
- ## 2. Tokens
53
-
54
- ### 2.1 Brand color scales (locked anchors)
55
-
56
- #### 云毓蓝 (`--nebutra-blue-*`)
57
-
58
- | Step | Hex |
59
- |------|-----|
60
- | 50 | `#F0F4FF` |
61
- | 100 | `#DBE4FF` |
62
- | 200 | `#BAC8FF` |
63
- | 300 | `#91A7FF` |
64
- | 400 | `#5C7CFA` |
65
- | **500** | **`#0033FE`** (base) |
66
- | 600 | `#002AD4` |
67
- | 700 | `#0021AB` |
68
- | 800 | `#001882` |
69
- | 900 | `#000F59` |
70
- | 950 | `#000830` |
71
-
72
- #### 云毓青 (`--nebutra-cyan-*`)
73
-
74
- | Step | Hex |
75
- |------|-----|
76
- | 50 | `#E6FFF8` |
77
- | 100 | `#B3FFEC` |
78
- | 200 | `#80FFE0` |
79
- | 300 | `#4DFCD4` |
80
- | 400 | `#1AF7C8` |
81
- | **500** | **`#0BF1C3`** (base) |
82
- | 600 | `#09C9A3` |
83
- | 700 | `#07A183` |
84
- | 800 | `#057963` |
85
- | 900 | `#035143` |
86
- | 950 | `#012923` |
87
-
88
- #### Neutral (blue-undertone slate; `--nebutra-neutral-*`)
89
-
90
- | Step | Hex |
91
- |------|-----|
92
- | 50 | `#F8FAFC` |
93
- | 100 | `#F1F5F9` |
94
- | 200 | `#E2E8F0` |
95
- | 300 | `#CBD5E1` |
96
- | 400 | `#94A3B8` |
97
- | 500 | `#64748B` |
98
- | 600 | `#475569` |
99
- | 700 | `#334155` |
100
- | 800 | `#1E293B` |
101
- | 900 | `#0F172A` |
102
- | 950 | `#020617` |
103
-
104
- > Wide-gamut: when the display supports `display-p3`, `--nebutra-brand-blue` and `--nebutra-brand-cyan` upgrade automatically to richer P3 values (`color(display-p3 …)` syntax).
105
-
106
- ### 2.2 12-step functional scales — semantic ladder
107
-
108
- Each scale obeys this Geist-style mapping:
109
-
110
- | Steps | Role |
111
- |-------|------|
112
- | 1–2 | Backgrounds (app, subtle) |
113
- | 3–5 | Component bg (default / hover / active) |
114
- | 6–8 | Borders (subtle / default / hover) |
115
- | 9–10 | Solid fills (default / hover) |
116
- | 11 | Low-contrast / secondary text |
117
- | 12 | High-contrast / primary text |
118
-
119
- Scales: `--neutral-{1..12}`, `--blue-{1..12}`, `--cyan-{1..12}`. Light values are derived from the brand scales above; dark values invert the order so `step 1 = darkest, step 12 = lightest`. Full mapping table is in [root DESIGN.md §2.1](../../DESIGN.md#21-color--12-step-functional-scales).
120
-
121
- ### 2.3 Brand aliases (canonical for app code)
122
-
123
- | Token | Resolves to | Use |
124
- |-------|-------------|-----|
125
- | `--brand-primary` | `var(--blue-9)` → `#0033FE` | Primary brand |
126
- | `--brand-accent` | `var(--cyan-9)` → `#0BF1C3` | Accent |
127
- | `--brand-tertiary` | `#8B5CF6` | Data viz / infra tags only |
128
- | `--primary` | `228 85% 56%` → soft blue for UI | **Canonical product CTA** (`bg-primary`, `Button` default) |
129
- | `--brand-primary` | `#0033FE` | VI 云毓蓝 — logo / identity |
130
- | `--brand-gradient` | `hsl(var(--primary))` | **Legacy alias of `--primary`**, not a separate color. Prefer `Button` / `bg-primary`. |
131
- | `--brand-gradient-logo` | `linear-gradient(135deg, #0033FE → #00A2E9 → #0BF1C3)` | Logo/VI only (OKLab mid stop) |
132
-
133
- **CTA rule:** use existing `@nebutra/ui` `Button` (`bg-primary`). Do not invent parallel “action” colors. `#0033FE` is identity; UI softens via `--primary` already defined in `themes/light.json`.
134
-
135
- **Logo gradient / dirty-mid rule:** `--brand-gradient-logo` must keep **three hex stops** ending in `#0033fe` → `#00a2e9` → `#0bf1c3` (see `semantic.json` + `verify-brand-token-sync`). Mid `#00a2e9` is the OKLab midpoint — prevents soft/gray mids on large VI surfaces. Product chrome must not reintroduce multi-stop blue→cyan as button fills. Full guidance: [`packages/design/brand/DESIGN.md` §2.2](../brand/DESIGN.md#22-brand-gradient).
136
-
137
- ### 2.4 Semantic theme tokens (HSL triplets — Tailwind compatible)
138
-
139
- These are stored as space-separated HSL triplets so Tailwind v4 can consume them via `hsl(var(--token))`.
140
-
141
- | Token | Light value | Dark value | Tailwind class |
142
- |------|-------------|-----------|---------------|
143
- | `--background` | `0 0% 100%` | `222 47% 4%` | `bg-background` |
144
- | `--foreground` | `222 47% 11%` | `210 40% 98%` | `text-foreground` |
145
- | `--card` | `0 0% 100%` | `222 47% 7%` | `bg-card` |
146
- | `--popover` | `0 0% 100%` | `222 47% 7%` | `bg-popover` |
147
- | `--primary` | `228 85% 56%` | `228 90% 72%` | `bg-primary` |
148
- | `--secondary` | `210 40% 96%` | `217 33% 17%` | `bg-secondary` |
149
- | `--muted` | `210 40% 96%` | `217 33% 17%` | `bg-muted` |
150
- | `--accent` | `228 100% 97%` | `228 100% 12%` | `bg-accent` |
151
- | `--destructive` | `0 84% 45%` | `0 63% 31%` | `bg-destructive` |
152
- | `--success` | `142 71% 29%` | `142 76% 36%` | `bg-success` |
153
- | `--warning` | `38 92% 50%` | `38 80% 45%` | `bg-warning` |
154
- | `--info` | `228 100% 50%` | `228 95% 67%` | `bg-info` |
155
- | `--border` | `240 5.9% 90%` | `217 33% 17%` | `border-border` |
156
- | `--ring` | `228 100% 50%` | `228 95% 67%` | `ring-ring` |
157
-
158
- ### 2.5 Status colors (direct hex — for SVG, charts, inline)
159
-
160
- | Token | Hex |
161
- |-------|-----|
162
- | `--status-danger` | `#EF4444` |
163
- | `--status-warning` | `#F59E0B` |
164
- | `--status-success` | `#10B981` |
165
- | `--status-info` | `var(--brand-primary)` |
166
-
167
- ### 2.6 Geist DS scale (interop with 21st.dev / Geist components)
168
-
169
- `--ds-blue-{200,700,900}`, `--ds-red-*`, `--ds-amber-*`, `--ds-green-*`, `--ds-teal-*`, `--ds-purple-*`, `--ds-pink-*`, `--ds-gray-{100..1000}`, `--ds-background-100`, plus accent gradients `--ds-trial-*`, `--ds-turbo-*`. Stored as oklch (with sRGB hsla fallback for legacy engines).
170
-
171
- ### 2.7 Layout containers
172
-
173
- | Token | Value |
174
- |-------|-------|
175
- | `--container-text` | `896px` |
176
- | `--container-content` | `1152px` |
177
- | `--container-wide` | `1400px` |
178
-
179
- ### 2.8 Radius scale
180
-
181
- ```
182
- --radius-none: 0
183
- --radius-sm: 0.25rem (4px)
184
- --radius-md: 0.375rem (6px) ← default --radius
185
- --radius-lg: 0.5rem (8px)
186
- --radius-xl: 0.75rem (12px)
187
- --radius-2xl: 1rem (16px)
188
- --radius-3xl: 1.5rem (24px)
189
- --radius-full: 9999px
190
- ```
191
-
192
- ### 2.9 Elevation / shadow
193
-
194
- | Token | Light | Dark |
195
- |------|-------|------|
196
- | `--elevation-xs` | `0 1px 2px 0 rgb(0 0 0 / 0.05)` | `0 1px 2px 0 rgb(0 0 0 / 0.3)` |
197
- | `--elevation-sm` | small card shadow | higher-contrast variant |
198
- | `--elevation-md` | dropdown shadow | — |
199
- | `--elevation-lg` | floating panel | — |
200
- | `--elevation-xl` | modal | — |
201
- | `--elevation-2xl` | top overlay | — |
202
- | `--elevation-brand` | `0 0 0 1px rgb(0 51 254 / 0.15), 0 4px 20px -2px rgb(0 51 254 / 0.2)` | uses `#5C7CFA` glow |
203
- | `--elevation-brand-lg` | hero CTA | hero CTA dark |
204
-
205
- Aliased to Tailwind: `shadow-{xs,sm,md,lg,xl,2xl,brand,brand-lg}`.
206
-
207
- ### 2.10 Motion
208
-
209
- Easing:
210
-
211
- ```
212
- --ease-in: cubic-bezier(0.4, 0, 1, 1)
213
- --ease-out: cubic-bezier(0, 0, 0.2, 1)
214
- --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1)
215
- --ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275)
216
- ```
217
-
218
- Duration:
219
-
220
- ```
221
- --duration-micro: 100ms /* hover, focus, toggle, button press */
222
- --duration-flow: 200ms /* modal, dropdown, tab — default */
223
- --duration-reveal: 300ms /* slide, expand, accordion, drawer */
224
- --duration-cinematic: 500ms /* hero entrance, big delight */
225
- ```
226
-
227
- Reduced motion: a global `@media (prefers-reduced-motion: reduce)` block in `styles.css` collapses all animation/transition durations to `0.01ms`.
228
-
229
- ### 2.11 Typography utilities
230
-
231
- Geist-style named utilities defined via Tailwind v4 `@utility`. Naming pattern: `text-{role}-{px}[-strong|-mono|-tabular]`.
232
-
233
- | Role | Sizes (px) |
234
- |------|-----------|
235
- | `text-heading-{px}` | 72, 64, 56, 48, 40, 32, 24, 20, 16, 14 |
236
- | `text-button-{px}` | 16, 14, 12 |
237
- | `text-label-{px}[-strong/-mono/-tabular]` | 20, 18, 16, 14, 13, 12 |
238
- | `text-copy-{px}[-strong/-mono]` | 24, 20, 18, 16, 14, 13 |
239
-
240
- Plus `font-cn` (CJK utility, `line-height: 1.75`, word-break-aware). Auto-activates on `<html lang="zh|ja|ko">`.
241
-
242
- ### 2.12 Chart colors
243
-
244
- `--chart-{1..5}` — light: blue-500, cyan-500, purple, cyan-300, blue-700. Dark: brighter equivalents.
245
-
246
- ---
247
-
248
- ## 3. Patterns
249
-
250
- ### 3.1 Tailwind class usage (canonical)
251
-
252
- ```tsx
253
- // 12-step scale classes
254
- <div className="bg-neutral-3 text-neutral-12 border-neutral-7" />
255
- <div className="bg-blue-9 text-cyan-11" />
256
-
257
- // Semantic Tailwind classes
258
- <div className="bg-primary text-primary-foreground" />
259
- <div className="bg-destructive text-destructive-foreground" />
260
-
261
- // Brand aliases
262
- <div className="bg-brand-primary text-brand-accent" />
263
-
264
- // Inline CSS variables (for SVG, recharts, dynamic values)
265
- <stop stopColor="var(--brand-primary)" />
266
- <Cell fill="var(--brand-accent)" />
267
- <div style={{ background: "var(--brand-gradient)" }} />
268
-
269
- // Arbitrary Tailwind for non-scale tokens
270
- <div className="text-[color:var(--status-warning)]" />
271
- ```
272
-
273
- ### 3.2 ThemeProvider wiring (per app)
274
-
275
- ```tsx
276
- // apps/{app}/src/app/layout.tsx
277
- import { ThemeProvider } from "@nebutra/tokens";
278
-
279
- <html lang="en" suppressHydrationWarning>
280
- <body>
281
- <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
282
- {children}
283
- </ThemeProvider>
284
- </body>
285
- </html>
286
- ```
287
-
288
- ---
289
-
290
- ## 4. Imports & Conventions
291
-
292
- ### Allowed
293
-
294
- ```ts
295
- import { ThemeProvider, useTheme } from "@nebutra/tokens";
296
- ```
297
-
298
- ```css
299
- @import "@nebutra/tokens/styles.css";
300
- ```
301
-
302
- ### Forbidden
303
-
304
- ```tsx
305
- // ❌ Hardcoded brand hex
306
- <div style={{ color: "#0033FE" }} />
307
-
308
- // ❌ Importing JS color tokens in runtime
309
- import { colors } from "@nebutra/ui/theme"; // deprecated
310
-
311
- // ❌ Adding raw hex into Tailwind arbitrary classes for brand colors
312
- <div className="bg-[#0a0a0a]" /> // → bg-neutral-1
313
- ```
314
-
315
- ---
316
-
317
- ## 5. Theming
318
-
319
- Light/dark switching: handled by `@nebutra/tokens` `ThemeProvider` via the `class` attribute (`class="dark"`). The `.dark` selector inside `styles.css` (generated from design-tokens) overrides every semantic token plus inverts the 12-step scales (step 1 = darkest, step 12 = lightest in dark mode).
320
-
321
- For **design-language swap** (Brand Packages / `applyLanguage`), see [`packages/design/theme/DESIGN.md`](../theme/DESIGN.md).
322
-
323
- ---
324
-
325
- ## 6. Versioning & Governance
326
-
327
- | Surface | Status |
328
- |--------|--------|
329
- | Brand color hex anchors (`#0033FE`, `#0BF1C3`, `--nebutra-neutral-{50..950}`) | **Locked** |
330
- | UI CTA color | Use `--primary` / `Button`; `--brand-gradient` is a legacy alias of `--primary` |
331
- | 12-step semantic ladder positions (1–12 meaning) | **Locked** |
332
- | Container widths | **Locked** |
333
- | Radius scale values | **Locked** |
334
- | Status color hex | **Locked** |
335
- | Adding a new semantic token | Extensible — must come with both light + dark values and Tailwind binding |
336
- | Adding a new typography utility | Extensible — must follow `text-{role}-{px}` naming |
337
- | Removing/renaming any token | **Forbidden** without a codemod (`scripts/codemod-tokens.ts`) |
338
-
339
- ### Governance scripts
340
-
341
- ```bash
342
- pnpm --filter @nebutra/tokens typecheck
343
- node scripts/codemod-tokens.ts # migrate hex → tokens
344
- pnpm tsx scripts/validate-ui-governance-policy.ts
345
- ```
346
-
347
- ---
348
-
349
- ## 7. Open questions / review notes
350
-
351
- - The HSL semantic tokens (`--primary` etc.) and the 12-step scales (`--blue-9` etc.) have separate value definitions. The `--primary` light value `228 85% 56%` (#254bfa) is intentionally *softer* than `--blue-9` (#0033FE) for Shadcn-style emphasis. Document this rationale once more in §2.4.
352
- - `--ds-*` Geist colors duplicate some 12-step scales — verify whether they can be consolidated when 21st.dev components are migrated.
353
-
354
- ---
355
-
356
- ← back to [root DESIGN.md](../../DESIGN.md) ·
357
- peer specs: [brand](../brand/DESIGN.md) · [theme](../theme/DESIGN.md) · [ui](../ui/DESIGN.md)
@@ -1,79 +0,0 @@
1
- import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
- import { basename, join, resolve } from "node:path";
3
- import { compileReferoTokens } from "../src/brand-package/index";
4
-
5
- function parseArgs(argv: string[]) {
6
- let input: string | null = null;
7
- let id: string | undefined;
8
- let name: string | undefined;
9
- let out: string | undefined;
10
- for (let i = 0; i < argv.length; i++) {
11
- const a = argv[i];
12
- if (a === "--id") id = argv[++i];
13
- else if (a === "--name") name = argv[++i];
14
- else if (a === "--out") out = argv[++i];
15
- else if (!a.startsWith("-")) input = resolve(a);
16
- }
17
- return { input, id, name, out };
18
- }
19
-
20
- const args = parseArgs(process.argv.slice(2));
21
- if (!args.input) {
22
- process.stderr.write(
23
- "Usage: compile-brand.mjs <dir|tokens.json> [--id id] [--name name] [--out dir]\n",
24
- );
25
- process.exit(1);
26
- }
27
-
28
- const st = statSync(args.input);
29
- let tokens: Record<string, unknown>;
30
- let designMd = "";
31
- let hintId = "custom";
32
-
33
- if (st.isDirectory()) {
34
- const tokensPath = join(args.input, "tokens.json");
35
- try {
36
- tokens = JSON.parse(readFileSync(tokensPath, "utf8"));
37
- } catch {
38
- throw new Error(`No tokens.json in ${args.input}`);
39
- }
40
- const designPath = join(args.input, "DESIGN.md");
41
- try {
42
- designMd = readFileSync(designPath, "utf8");
43
- } catch {
44
- designMd = "";
45
- }
46
- hintId = basename(args.input).toLowerCase();
47
- } else {
48
- tokens = JSON.parse(readFileSync(args.input, "utf8"));
49
- hintId = basename(args.input, ".json").toLowerCase();
50
- }
51
-
52
- const result = compileReferoTokens({
53
- tokens,
54
- designMd,
55
- id: args.id ?? hintId,
56
- name: args.name,
57
- });
58
-
59
- const packageRoot = resolve(import.meta.dirname, "..");
60
- const outDir = args.out ? resolve(args.out) : join(packageRoot, "brands", result.brand.id);
61
- mkdirSync(outDir, { recursive: true });
62
- // brand.json is SSOT — skin CSS only under skins/<id>.css (via emit-skins)
63
- writeFileSync(join(outDir, "brand.json"), `${JSON.stringify(result.brand, null, 2)}\n`);
64
- mkdirSync(join(packageRoot, "skins"), { recursive: true });
65
- writeFileSync(join(packageRoot, "skins", `${result.brand.id}.css`), result.css);
66
-
67
- process.stdout.write(
68
- `${JSON.stringify(
69
- {
70
- out: outDir,
71
- id: result.brand.id,
72
- recipe: result.brand.recipe.buttonDefault,
73
- warnings: result.warnings,
74
- note: "brand.json is SSOT; re-run emit-skins after hand-edits to brand.json",
75
- },
76
- null,
77
- 2,
78
- )}\n`,
79
- );
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Compile Refero export → Brand Package + skin CSS
4
- *
5
- * node packages/design/tokens/scripts/compile-brand.mjs ~/Desktop/GSAP --id gsap
6
- */
7
- import { spawnSync } from "node:child_process";
8
- import { existsSync } from "node:fs";
9
- import { dirname, join, resolve } from "node:path";
10
- import { fileURLToPath } from "node:url";
11
-
12
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
13
- const repoRoot = resolve(packageRoot, "../../..");
14
- const tsx = join(repoRoot, "node_modules/.bin/tsx");
15
- const runner = join(packageRoot, "scripts/compile-brand-run.ts");
16
-
17
- const r = spawnSync(
18
- existsSync(tsx) ? tsx : "npx",
19
- existsSync(tsx) ? [runner, ...process.argv.slice(2)] : ["tsx", runner, ...process.argv.slice(2)],
20
- {
21
- stdio: "inherit",
22
- cwd: packageRoot,
23
- },
24
- );
25
- process.exit(r.status ?? 1);
@@ -1,62 +0,0 @@
1
- /**
2
- * Re-emit all skin CSS files from brands/<id>/brand.json (SSOT).
3
- */
4
- import { spawnSync } from "node:child_process";
5
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
6
- import { join, resolve } from "node:path";
7
- import { emitBrandCss, normalizeBrandPackage } from "../src/brand-package/index.ts";
8
-
9
- const packageRoot = resolve(import.meta.dirname, "..");
10
- const repoRoot = resolve(packageRoot, "../../..");
11
- const brandsDir = join(packageRoot, "brands");
12
- const skinsDir = join(packageRoot, "skins");
13
- const biomeBin = join(repoRoot, "node_modules/.bin/biome");
14
-
15
- if (!existsSync(brandsDir)) {
16
- throw new Error(`Missing brands dir: ${brandsDir}`);
17
- }
18
-
19
- mkdirSync(skinsDir, { recursive: true });
20
-
21
- const ids = readdirSync(brandsDir, { withFileTypes: true })
22
- .filter((d) => d.isDirectory())
23
- .map((d) => d.name)
24
- .sort();
25
-
26
- const written: string[] = [];
27
- const cssPaths: string[] = [];
28
-
29
- for (const id of ids) {
30
- const brandPath = join(brandsDir, id, "brand.json");
31
- if (!existsSync(brandPath)) {
32
- process.stderr.write(`skip ${id}: no brand.json\n`);
33
- continue;
34
- }
35
- const raw = JSON.parse(readFileSync(brandPath, "utf8"));
36
- const brand = normalizeBrandPackage({ ...raw, id: raw.id || id });
37
- const css = emitBrandCss(brand, { mode: "global" });
38
-
39
- // Single publish path: skins/<id>.css (no brands/<id>/skin.css mirror)
40
- const outPath = join(skinsDir, `${brand.id}.css`);
41
- writeFileSync(outPath, css);
42
- cssPaths.push(outPath);
43
- written.push(brand.id);
44
- }
45
-
46
- // Post-format so CI "governance lint guards" (biome check) stays green after
47
- // typecheck/build re-emits skins in the same workspace.
48
- if (cssPaths.length > 0 && existsSync(biomeBin)) {
49
- const fmt = spawnSync(biomeBin, ["format", "--write", ...cssPaths], {
50
- cwd: repoRoot,
51
- encoding: "utf8",
52
- });
53
- if (fmt.status !== 0) {
54
- process.stderr.write(fmt.stderr || fmt.stdout || "biome format failed on skins\n");
55
- process.exit(fmt.status ?? 1);
56
- }
57
- }
58
-
59
- process.stdout.write(
60
- `emit-skins: ${written.length} skins from brand.json → skins/*.css\n` +
61
- ` ${written.join(", ")}\n`,
62
- );
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Skin CSS SSOT pipeline.
4
- *
5
- * Source of truth: brands/<id>/brand.json
6
- * Generated (do not hand-edit):
7
- * - skins/<id>.css (single publish path for @import / cssImport)
8
- *
9
- * Catalog: packages/design/theme/skins.css via theme scripts/sync-skins.mjs
10
- *
11
- * Usage: node scripts/emit-skins.mjs
12
- */
13
- import { spawnSync } from "node:child_process";
14
- import { existsSync } from "node:fs";
15
- import { dirname, join, resolve } from "node:path";
16
- import { fileURLToPath } from "node:url";
17
-
18
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
19
- const repoRoot = resolve(packageRoot, "../../..");
20
- const tsx = join(repoRoot, "node_modules/.bin/tsx");
21
- const runner = join(packageRoot, "scripts/emit-skins-run.ts");
22
-
23
- const r = spawnSync(
24
- existsSync(tsx) ? tsx : "npx",
25
- existsSync(tsx) ? [runner, ...process.argv.slice(2)] : ["tsx", runner, ...process.argv.slice(2)],
26
- {
27
- stdio: "inherit",
28
- cwd: packageRoot,
29
- },
30
- );
31
- process.exit(r.status ?? 1);
@@ -1,29 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { copyFileSync, existsSync } from "node:fs";
3
- import { dirname, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
7
- const repoRoot = resolve(packageRoot, "..", "..", "..");
8
- const designTokensRoot = resolve(packageRoot, "..", "design-tokens");
9
- const generatedStylesPath = resolve(designTokensRoot, "build", "css", "styles.generated.css");
10
- const runtimeStylesPath = resolve(packageRoot, "styles.css");
11
- const biomeBin = resolve(
12
- repoRoot,
13
- "node_modules",
14
- ".bin",
15
- process.platform === "win32" ? "biome.cmd" : "biome",
16
- );
17
-
18
- if (!existsSync(generatedStylesPath)) {
19
- throw new Error(
20
- `Missing generated runtime token CSS at ${generatedStylesPath}. Run \`pnpm --filter @nebutra/design-tokens build\` before syncing @nebutra/tokens.`,
21
- );
22
- }
23
-
24
- copyFileSync(generatedStylesPath, runtimeStylesPath);
25
- execFileSync(biomeBin, ["format", "--write", runtimeStylesPath], {
26
- cwd: repoRoot,
27
- stdio: "inherit",
28
- });
29
- process.stdout.write("styles.css refreshed from @nebutra/design-tokens\n");