@nebutra/tokens 0.1.0

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.
package/AGENTS.md ADDED
@@ -0,0 +1,42 @@
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 and semantic CSS variables: `styles.css`
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-theme presets here; those belong in `@nebutra/theme`.
26
+ - Preserve the package boundary between CSS tokens and component usage.
27
+ `@nebutra/ui` and apps should consume the exported CSS surface rather than
28
+ importing token internals.
29
+ - Because `./styles.css` is an exported side-effect file, treat renames or
30
+ variable removals as cross-repo breaking changes even if TypeScript passes.
31
+
32
+ ## Generated And Derived Files
33
+
34
+ - `tsconfig.tsbuildinfo` and compiler artifacts are derived files.
35
+ - Consumer app compiled CSS output is derived from `styles.css`.
36
+
37
+ ## Validation
38
+
39
+ - Token or export changes:
40
+ `pnpm --filter @nebutra/tokens typecheck`
41
+ - When token names or theme IDs change, verify at least one consuming package
42
+ updates with the same change instead of relying on stale compiled CSS.
package/DESIGN.md ADDED
@@ -0,0 +1,333 @@
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 | `ThemeProvider`, `useTheme` from `next-themes` |
13
+
14
+ ---
15
+
16
+ ## 1. Identity
17
+
18
+ This package emits the **runtime CSS variables** consumed by every Nebutra app. Apps import via:
19
+
20
+ ```css
21
+ /* apps/{app}/src/app/globals.css */
22
+ @import "tailwindcss";
23
+ @import "@nebutra/tokens/styles.css";
24
+ ```
25
+
26
+ Tokens are organized in three concentric rings:
27
+
28
+ ```
29
+ Brand scales (raw) → --nebutra-blue-{50..950}, --nebutra-cyan-{50..950}, --nebutra-neutral-{50..950}
30
+ 12-step semantic scales → --neutral-{1..12}, --blue-{1..12}, --cyan-{1..12}
31
+ Aliased semantic tokens → --background, --foreground, --primary, --brand-primary, --status-*
32
+ ```
33
+
34
+ ---
35
+
36
+ ## 2. Tokens
37
+
38
+ ### 2.1 Brand color scales (locked anchors)
39
+
40
+ #### 云毓蓝 (`--nebutra-blue-*`)
41
+
42
+ | Step | Hex |
43
+ |------|-----|
44
+ | 50 | `#F0F4FF` |
45
+ | 100 | `#DBE4FF` |
46
+ | 200 | `#BAC8FF` |
47
+ | 300 | `#91A7FF` |
48
+ | 400 | `#5C7CFA` |
49
+ | **500** | **`#0033FE`** (base) |
50
+ | 600 | `#002AD4` |
51
+ | 700 | `#0021AB` |
52
+ | 800 | `#001882` |
53
+ | 900 | `#000F59` |
54
+ | 950 | `#000830` |
55
+
56
+ #### 云毓青 (`--nebutra-cyan-*`)
57
+
58
+ | Step | Hex |
59
+ |------|-----|
60
+ | 50 | `#E6FFF8` |
61
+ | 100 | `#B3FFEC` |
62
+ | 200 | `#80FFE0` |
63
+ | 300 | `#4DFCD4` |
64
+ | 400 | `#1AF7C8` |
65
+ | **500** | **`#0BF1C3`** (base) |
66
+ | 600 | `#09C9A3` |
67
+ | 700 | `#07A183` |
68
+ | 800 | `#057963` |
69
+ | 900 | `#035143` |
70
+ | 950 | `#012923` |
71
+
72
+ #### Neutral (blue-undertone slate; `--nebutra-neutral-*`)
73
+
74
+ | Step | Hex |
75
+ |------|-----|
76
+ | 50 | `#F8FAFC` |
77
+ | 100 | `#F1F5F9` |
78
+ | 200 | `#E2E8F0` |
79
+ | 300 | `#CBD5E1` |
80
+ | 400 | `#94A3B8` |
81
+ | 500 | `#64748B` |
82
+ | 600 | `#475569` |
83
+ | 700 | `#334155` |
84
+ | 800 | `#1E293B` |
85
+ | 900 | `#0F172A` |
86
+ | 950 | `#020617` |
87
+
88
+ > 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).
89
+
90
+ ### 2.2 12-step functional scales — semantic ladder
91
+
92
+ Each scale obeys this Geist-style mapping:
93
+
94
+ | Steps | Role |
95
+ |-------|------|
96
+ | 1–2 | Backgrounds (app, subtle) |
97
+ | 3–5 | Component bg (default / hover / active) |
98
+ | 6–8 | Borders (subtle / default / hover) |
99
+ | 9–10 | Solid fills (default / hover) |
100
+ | 11 | Low-contrast / secondary text |
101
+ | 12 | High-contrast / primary text |
102
+
103
+ 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).
104
+
105
+ ### 2.3 Brand aliases (canonical for app code)
106
+
107
+ | Token | Resolves to | Use |
108
+ |-------|-------------|-----|
109
+ | `--brand-primary` | `var(--blue-9)` → `#0033FE` | Primary brand |
110
+ | `--brand-accent` | `var(--cyan-9)` → `#0BF1C3` | Accent |
111
+ | `--brand-tertiary` | `#8B5CF6` | Data viz / infra tags only |
112
+ | `--brand-gradient` | `linear-gradient(135deg, blue-500 → cyan-500)` | CTAs, hero text |
113
+
114
+ ### 2.4 Semantic theme tokens (HSL triplets — Tailwind compatible)
115
+
116
+ These are stored as space-separated HSL triplets so Tailwind v4 can consume them via `hsl(var(--token))`.
117
+
118
+ | Token | Light value | Dark value | Tailwind class |
119
+ |------|-------------|-----------|---------------|
120
+ | `--background` | `0 0% 100%` | `222 47% 4%` | `bg-background` |
121
+ | `--foreground` | `222 47% 11%` | `210 40% 98%` | `text-foreground` |
122
+ | `--card` | `0 0% 100%` | `222 47% 7%` | `bg-card` |
123
+ | `--popover` | `0 0% 100%` | `222 47% 7%` | `bg-popover` |
124
+ | `--primary` | `228 85% 56%` | `228 90% 72%` | `bg-primary` |
125
+ | `--secondary` | `210 40% 96%` | `217 33% 17%` | `bg-secondary` |
126
+ | `--muted` | `210 40% 96%` | `217 33% 17%` | `bg-muted` |
127
+ | `--accent` | `228 100% 97%` | `228 100% 12%` | `bg-accent` |
128
+ | `--destructive` | `0 84% 45%` | `0 63% 31%` | `bg-destructive` |
129
+ | `--success` | `142 71% 29%` | `142 76% 36%` | `bg-success` |
130
+ | `--warning` | `38 92% 50%` | `38 80% 45%` | `bg-warning` |
131
+ | `--info` | `228 100% 50%` | `228 95% 67%` | `bg-info` |
132
+ | `--border` | `240 5.9% 90%` | `217 33% 17%` | `border-border` |
133
+ | `--ring` | `228 100% 50%` | `228 95% 67%` | `ring-ring` |
134
+
135
+ ### 2.5 Status colors (direct hex — for SVG, charts, inline)
136
+
137
+ | Token | Hex |
138
+ |-------|-----|
139
+ | `--status-danger` | `#EF4444` |
140
+ | `--status-warning` | `#F59E0B` |
141
+ | `--status-success` | `#10B981` |
142
+ | `--status-info` | `var(--brand-primary)` |
143
+
144
+ ### 2.6 Geist DS scale (interop with 21st.dev / Geist components)
145
+
146
+ `--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).
147
+
148
+ ### 2.7 Layout containers
149
+
150
+ | Token | Value |
151
+ |-------|-------|
152
+ | `--container-text` | `896px` |
153
+ | `--container-content` | `1152px` |
154
+ | `--container-wide` | `1400px` |
155
+
156
+ ### 2.8 Radius scale
157
+
158
+ ```
159
+ --radius-none: 0
160
+ --radius-sm: 0.25rem (4px)
161
+ --radius-md: 0.375rem (6px) ← default --radius
162
+ --radius-lg: 0.5rem (8px)
163
+ --radius-xl: 0.75rem (12px)
164
+ --radius-2xl: 1rem (16px)
165
+ --radius-3xl: 1.5rem (24px)
166
+ --radius-full: 9999px
167
+ ```
168
+
169
+ ### 2.9 Elevation / shadow
170
+
171
+ | Token | Light | Dark |
172
+ |------|-------|------|
173
+ | `--elevation-xs` | `0 1px 2px 0 rgb(0 0 0 / 0.05)` | `0 1px 2px 0 rgb(0 0 0 / 0.3)` |
174
+ | `--elevation-sm` | small card shadow | higher-contrast variant |
175
+ | `--elevation-md` | dropdown shadow | — |
176
+ | `--elevation-lg` | floating panel | — |
177
+ | `--elevation-xl` | modal | — |
178
+ | `--elevation-2xl` | top overlay | — |
179
+ | `--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 |
180
+ | `--elevation-brand-lg` | hero CTA | hero CTA dark |
181
+
182
+ Aliased to Tailwind: `shadow-{xs,sm,md,lg,xl,2xl,brand,brand-lg}`.
183
+
184
+ ### 2.10 Motion
185
+
186
+ Easing:
187
+
188
+ ```
189
+ --ease-in: cubic-bezier(0.4, 0, 1, 1)
190
+ --ease-out: cubic-bezier(0, 0, 0.2, 1)
191
+ --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1)
192
+ --ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275)
193
+ ```
194
+
195
+ Duration:
196
+
197
+ ```
198
+ --duration-micro: 100ms /* hover, focus, toggle, button press */
199
+ --duration-flow: 200ms /* modal, dropdown, tab — default */
200
+ --duration-reveal: 300ms /* slide, expand, accordion, drawer */
201
+ --duration-cinematic: 500ms /* hero entrance, big delight */
202
+ ```
203
+
204
+ Reduced motion: a global `@media (prefers-reduced-motion: reduce)` block in `styles.css` collapses all animation/transition durations to `0.01ms`.
205
+
206
+ ### 2.11 Typography utilities
207
+
208
+ Geist-style named utilities defined via Tailwind v4 `@utility`. Naming pattern: `text-{role}-{px}[-strong|-mono|-tabular]`.
209
+
210
+ | Role | Sizes (px) |
211
+ |------|-----------|
212
+ | `text-heading-{px}` | 72, 64, 56, 48, 40, 32, 24, 20, 16, 14 |
213
+ | `text-button-{px}` | 16, 14, 12 |
214
+ | `text-label-{px}[-strong/-mono/-tabular]` | 20, 18, 16, 14, 13, 12 |
215
+ | `text-copy-{px}[-strong/-mono]` | 24, 20, 18, 16, 14, 13 |
216
+
217
+ Plus `font-cn` (CJK utility, `line-height: 1.75`, word-break-aware). Auto-activates on `<html lang="zh|ja|ko">`.
218
+
219
+ ### 2.12 Chart colors
220
+
221
+ `--chart-{1..5}` — light: blue-500, cyan-500, purple, cyan-300, blue-700. Dark: brighter equivalents.
222
+
223
+ ---
224
+
225
+ ## 3. Patterns
226
+
227
+ ### 3.1 Tailwind class usage (canonical)
228
+
229
+ ```tsx
230
+ // 12-step scale classes
231
+ <div className="bg-neutral-3 text-neutral-12 border-neutral-7" />
232
+ <div className="bg-blue-9 text-cyan-11" />
233
+
234
+ // Semantic Tailwind classes
235
+ <div className="bg-primary text-primary-foreground" />
236
+ <div className="bg-destructive text-destructive-foreground" />
237
+
238
+ // Brand aliases
239
+ <div className="bg-brand-primary text-brand-accent" />
240
+
241
+ // Inline CSS variables (for SVG, recharts, dynamic values)
242
+ <stop stopColor="var(--brand-primary)" />
243
+ <Cell fill="var(--brand-accent)" />
244
+ <div style={{ background: "var(--brand-gradient)" }} />
245
+
246
+ // Arbitrary Tailwind for non-scale tokens
247
+ <div className="text-[color:var(--status-warning)]" />
248
+ ```
249
+
250
+ ### 3.2 ThemeProvider wiring (per app)
251
+
252
+ ```tsx
253
+ // apps/{app}/src/app/layout.tsx
254
+ import { ThemeProvider } from "@nebutra/tokens";
255
+
256
+ <html lang="en" suppressHydrationWarning>
257
+ <body>
258
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
259
+ {children}
260
+ </ThemeProvider>
261
+ </body>
262
+ </html>
263
+ ```
264
+
265
+ ---
266
+
267
+ ## 4. Imports & Conventions
268
+
269
+ ### Allowed
270
+
271
+ ```ts
272
+ import { ThemeProvider, useTheme } from "@nebutra/tokens";
273
+ ```
274
+
275
+ ```css
276
+ @import "@nebutra/tokens/styles.css";
277
+ ```
278
+
279
+ ### Forbidden
280
+
281
+ ```tsx
282
+ // ❌ Hardcoded brand hex
283
+ <div style={{ color: "#0033FE" }} />
284
+
285
+ // ❌ Importing JS color tokens in runtime
286
+ import { colors } from "@nebutra/ui/theme"; // deprecated
287
+
288
+ // ❌ Adding raw hex into Tailwind arbitrary classes for brand colors
289
+ <div className="bg-[#0a0a0a]" /> // → bg-neutral-1
290
+ ```
291
+
292
+ ---
293
+
294
+ ## 5. Theming
295
+
296
+ Light/dark switching: handled by `next-themes` via the `class` attribute. The `.dark` selector inside `styles.css` overrides every semantic token plus inverts the 12-step scales (step 1 = darkest, step 12 = lightest in dark mode).
297
+
298
+ For the **multi-theme product feature** (6 oklch presets), see [`packages/design/theme/DESIGN.md`](../theme/DESIGN.md).
299
+
300
+ ---
301
+
302
+ ## 6. Versioning & Governance
303
+
304
+ | Surface | Status |
305
+ |--------|--------|
306
+ | Brand color hex anchors (`#0033FE`, `#0BF1C3`, `--nebutra-neutral-{50..950}`) | **Locked** |
307
+ | 12-step semantic ladder positions (1–12 meaning) | **Locked** |
308
+ | Container widths | **Locked** |
309
+ | Radius scale values | **Locked** |
310
+ | Status color hex | **Locked** |
311
+ | Adding a new semantic token | Extensible — must come with both light + dark values and Tailwind binding |
312
+ | Adding a new typography utility | Extensible — must follow `text-{role}-{px}` naming |
313
+ | Removing/renaming any token | **Forbidden** without a codemod (`scripts/codemod-tokens.ts`) |
314
+
315
+ ### Governance scripts
316
+
317
+ ```bash
318
+ pnpm --filter @nebutra/tokens typecheck
319
+ node scripts/codemod-tokens.ts # migrate hex → tokens
320
+ pnpm tsx scripts/validate-ui-governance-policy.ts
321
+ ```
322
+
323
+ ---
324
+
325
+ ## 7. Open questions / review notes
326
+
327
+ - 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.
328
+ - `--ds-*` Geist colors duplicate some 12-step scales — verify whether they can be consolidated when 21st.dev components are migrated.
329
+
330
+ ---
331
+
332
+ ← back to [root DESIGN.md](../../DESIGN.md) ·
333
+ peer specs: [brand](../brand/DESIGN.md) · [theme](../theme/DESIGN.md) · [ui](../ui/DESIGN.md)