@nebutra/tokens 0.1.3 → 3.0.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/CHANGELOG.md +16 -0
- package/brands/README.md +66 -0
- package/brands/cosmos/DESIGN.md +356 -0
- package/brands/cosmos/brand.json +209 -0
- package/brands/cosmos/refero-tokens.json +312 -0
- package/brands/gsap/brand.json +1 -27
- package/brands/linear/DESIGN.md +478 -0
- package/brands/linear/brand.json +2 -19
- package/brands/notion/brand.json +2 -28
- package/brands/raycast/brand.json +2 -17
- package/brands/stripe/brand.json +1 -16
- package/brands/vanta/brand.json +1 -38
- package/brands/vercel/brand.json +1 -18
- package/dist/brand-package/index.d.ts +13 -7
- package/dist/brand-package/index.js +2290 -34
- package/dist/brand-package/index.js.map +1 -1
- package/dist/brand-package/use-brand.d.ts +1 -1
- package/dist/brand-package/use-brand.js +957 -5
- package/dist/brand-package/use-brand.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2417 -22
- package/dist/index.js.map +1 -1
- package/dist/{use-brand-C8d4DPqE.d.ts → use-brand-ClmvK_lC.d.ts} +19 -3
- package/dist/values.d.ts +1192 -0
- package/dist/values.js +518 -0
- package/dist/values.js.map +1 -0
- package/package.json +16 -4
- package/recipe.css +36 -2
- package/skins/cosmos.css +239 -0
- package/skins/gsap.css +32 -20
- package/skins/linear.css +54 -17
- package/skins/notion.css +36 -25
- package/skins/raycast.css +37 -19
- package/skins/stripe.css +34 -15
- package/skins/vanta.css +34 -30
- package/skins/vercel.css +55 -19
- package/styles.css +492 -149
- package/dist/chunk-ALJTP5HL.js +0 -881
- package/dist/chunk-ALJTP5HL.js.map +0 -1
- package/dist/chunk-RGUJQOLH.js +0 -1582
- package/dist/chunk-RGUJQOLH.js.map +0 -1
package/styles.css
CHANGED
|
@@ -47,17 +47,17 @@
|
|
|
47
47
|
--nebutra-cyan-800: #057963;
|
|
48
48
|
--nebutra-cyan-900: #035143;
|
|
49
49
|
--nebutra-cyan-950: #012923;
|
|
50
|
-
--nebutra-neutral-50: #
|
|
51
|
-
--nebutra-neutral-100: #
|
|
52
|
-
--nebutra-neutral-200: #
|
|
53
|
-
--nebutra-neutral-300: #
|
|
54
|
-
--nebutra-neutral-400: #
|
|
55
|
-
--nebutra-neutral-500: #
|
|
56
|
-
--nebutra-neutral-600: #
|
|
57
|
-
--nebutra-neutral-700: #
|
|
58
|
-
--nebutra-neutral-800: #
|
|
59
|
-
--nebutra-neutral-900: #
|
|
60
|
-
--nebutra-neutral-950: #
|
|
50
|
+
--nebutra-neutral-50: #f9fafb;
|
|
51
|
+
--nebutra-neutral-100: #f3f4f6;
|
|
52
|
+
--nebutra-neutral-200: #e4e5e8;
|
|
53
|
+
--nebutra-neutral-300: #d2d4d8;
|
|
54
|
+
--nebutra-neutral-400: #9fa1a5;
|
|
55
|
+
--nebutra-neutral-500: #717377;
|
|
56
|
+
--nebutra-neutral-600: #515357;
|
|
57
|
+
--nebutra-neutral-700: #3f4044;
|
|
58
|
+
--nebutra-neutral-800: #26272a;
|
|
59
|
+
--nebutra-neutral-900: #18191c;
|
|
60
|
+
--nebutra-neutral-950: #0b0c0d;
|
|
61
61
|
--nebutra-gray-50: #fafafa;
|
|
62
62
|
--nebutra-gray-100: #f5f5f5;
|
|
63
63
|
--nebutra-gray-200: #e5e5e5;
|
|
@@ -90,28 +90,97 @@
|
|
|
90
90
|
--duration-reveal: 300ms; /** Content unveil: slide-in, expand, accordion, drawer */
|
|
91
91
|
--duration-cinematic: 500ms; /** Hero-grade entrance: landing reveal, large delight moments */
|
|
92
92
|
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
93
|
-
--ease-out: cubic-bezier(0,
|
|
93
|
+
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
94
94
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
95
95
|
--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
96
96
|
--tracking-display: -0.04em; /** Hero H1 */
|
|
97
97
|
--tracking-heading: -0.03em; /** Section H2/H3 */
|
|
98
98
|
--tracking-tight: -0.015em; /** Body emphasis */
|
|
99
|
+
--tracking-label: 0.07em; /** Uppercase micro-labels. 0.07em is Vercel's measured eyebrow value (0.071em); open tracking keeps small caps legible. */
|
|
99
100
|
--leading-display: 1.05;
|
|
100
101
|
--leading-heading: 1.15;
|
|
102
|
+
--type-2xs-size: 0.6875rem;
|
|
103
|
+
--type-2xs-leading: 1.45;
|
|
104
|
+
--type-2xs-tracking: 0.01em;
|
|
105
|
+
--type-xs-size: 0.75rem;
|
|
106
|
+
--type-xs-leading: 1.4;
|
|
107
|
+
--type-xs-tracking: 0.005em;
|
|
108
|
+
--type-ui-size: 0.8125rem;
|
|
109
|
+
--type-ui-leading: 1.45;
|
|
110
|
+
--type-ui-tracking: 0em;
|
|
111
|
+
--type-sm-size: 0.875rem;
|
|
112
|
+
--type-sm-leading: 1.5;
|
|
113
|
+
--type-sm-tracking: 0em;
|
|
114
|
+
--type-base-size: 1rem;
|
|
115
|
+
--type-base-leading: 1.6;
|
|
116
|
+
--type-base-tracking: -0.005em;
|
|
117
|
+
--type-lg-size: 1.125rem;
|
|
118
|
+
--type-lg-leading: 1.55;
|
|
119
|
+
--type-lg-tracking: -0.01em;
|
|
120
|
+
--type-xl-size: 1.25rem;
|
|
121
|
+
--type-xl-leading: 1.45;
|
|
122
|
+
--type-xl-tracking: -0.012em;
|
|
123
|
+
--type-2xl-size: 1.5rem;
|
|
124
|
+
--type-2xl-leading: 1.33;
|
|
125
|
+
--type-2xl-tracking: -0.015em;
|
|
126
|
+
--type-3xl-size: 1.875rem;
|
|
127
|
+
--type-3xl-leading: 1.25;
|
|
128
|
+
--type-3xl-tracking: -0.02em;
|
|
129
|
+
--type-4xl-size: 2.25rem;
|
|
130
|
+
--type-4xl-leading: 1.15;
|
|
131
|
+
--type-4xl-tracking: -0.022em;
|
|
132
|
+
--type-5xl-size: 3rem;
|
|
133
|
+
--type-5xl-leading: 1.08;
|
|
134
|
+
--type-5xl-tracking: -0.028em;
|
|
135
|
+
--type-6xl-size: 3.75rem;
|
|
136
|
+
--type-6xl-leading: 1.04;
|
|
137
|
+
--type-6xl-tracking: -0.032em;
|
|
138
|
+
--type-7xl-size: 4.5rem;
|
|
139
|
+
--type-7xl-leading: 1;
|
|
140
|
+
--type-7xl-tracking: -0.035em;
|
|
141
|
+
--type-8xl-size: 6rem;
|
|
142
|
+
--type-8xl-leading: 1;
|
|
143
|
+
--type-8xl-tracking: -0.04em;
|
|
144
|
+
--type-9xl-size: 8rem;
|
|
145
|
+
--type-9xl-leading: 1;
|
|
146
|
+
--type-9xl-tracking: -0.045em;
|
|
147
|
+
--type-display-size: 1.25rem;
|
|
148
|
+
--type-display-leading: 1.45;
|
|
149
|
+
--type-display-tracking: -0.012em;
|
|
150
|
+
--type-body-size: 0.875rem;
|
|
151
|
+
--type-body-leading: 1.5;
|
|
152
|
+
--type-body-tracking: 0em;
|
|
153
|
+
--type-label-size: 0.75rem;
|
|
154
|
+
--type-label-leading: 1.4;
|
|
155
|
+
--type-label-tracking: 0.005em;
|
|
156
|
+
--type-meta-size: 0.6875rem;
|
|
157
|
+
--type-meta-leading: 1.45;
|
|
158
|
+
--type-meta-tracking: 0.01em;
|
|
159
|
+
--layer-raised: 10; /** Within a component: an element lifted over its siblings. */
|
|
160
|
+
--layer-sticky: 20; /** Sticky headers and table heads. */
|
|
161
|
+
--layer-floating: 30; /** Floating toolbars and canvas chrome anchored to content. */
|
|
162
|
+
--layer-nav: 40; /** App navigation and the shell's top bar. */
|
|
163
|
+
--layer-drawer: 60; /** Mobile navigation drawers and their toggles. */
|
|
164
|
+
--layer-banner: 90; /** Page-level banners: cookie consent, announcements. */
|
|
165
|
+
--layer-skip-link: 100; /** The skip link — above page chrome whenever it is focused. */
|
|
166
|
+
--layer-panel: 200; /** App-owned floating panels that are not overlays (locale switcher, search flyouts). */
|
|
167
|
+
--layer-backdrop: 1040; /** Overlay tier. Mirrors overlayZIndex in @nebutra/ui; a test keeps them equal. */
|
|
168
|
+
--layer-modal: 1050; /** Dialogs and sheets. */
|
|
169
|
+
--layer-popover: 1060; /** Popovers, menus, selects. */
|
|
170
|
+
--layer-tooltip: 1070; /** Tooltips. */
|
|
171
|
+
--layer-devtools: 9999; /** Developer and debug surfaces only — never product UI. */
|
|
101
172
|
--ring-hairline: inset 0 0 0 1px rgb(255 255 255 / 0.04); /** Etched-glass hairline (light mode value, dark mode override in themes/dark.json) */
|
|
102
173
|
--overlay-faint: rgb(0 0 0 / 0.06); /** Light-mode faint overlay (6% black) */
|
|
103
174
|
--font-sans:
|
|
104
|
-
var(--font-geist-sans), "Geist", var(--font-
|
|
105
|
-
|
|
175
|
+
var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei",
|
|
176
|
+
-apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
|
106
177
|
--font-cn:
|
|
107
|
-
var(--font-geist-sans), "Geist", var(--font-
|
|
108
|
-
|
|
178
|
+
var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei",
|
|
179
|
+
sans-serif; /** CJK-locale stack — same order as sans, minus the Latin-only system faces */
|
|
109
180
|
--font-display:
|
|
110
|
-
var(--font-
|
|
111
|
-
sans-serif;
|
|
181
|
+
var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
|
|
112
182
|
--font-heading:
|
|
113
|
-
var(--font-
|
|
114
|
-
sans-serif;
|
|
183
|
+
var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
|
|
115
184
|
--font-mono:
|
|
116
185
|
var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
117
186
|
--brand-primary: #0033fe; /** Brand primary — 云毓蓝 */
|
|
@@ -155,17 +224,17 @@
|
|
|
155
224
|
--motion-duration-cinematic: 500ms;
|
|
156
225
|
--focus-ring: 0 0 0 2px hsl(var(--ring) / 0.4); /** Single-layer translucent focus ring (2026 pattern). Legacy box-shadow consumers stay compatible; modern surfaces should prefer the global :focus-visible outline rule in static/base.css. */
|
|
157
226
|
--neutral-1: #ffffff; /** app background */
|
|
158
|
-
--neutral-2: #
|
|
159
|
-
--neutral-3: #
|
|
160
|
-
--neutral-4: #
|
|
161
|
-
--neutral-5: #
|
|
162
|
-
--neutral-6: #
|
|
163
|
-
--neutral-7: #
|
|
164
|
-
--neutral-8: #
|
|
165
|
-
--neutral-9: #
|
|
166
|
-
--neutral-10: #
|
|
167
|
-
--neutral-11: #
|
|
168
|
-
--neutral-12: #
|
|
227
|
+
--neutral-2: #f9fafb; /** subtle background */
|
|
228
|
+
--neutral-3: #f3f4f6; /** component bg default */
|
|
229
|
+
--neutral-4: #e4e5e8; /** component bg hover */
|
|
230
|
+
--neutral-5: #d2d4d8; /** component bg active */
|
|
231
|
+
--neutral-6: #b9bbbf; /** subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp step5 → step9 at t=0.25. The $value here is a placeholder anchor only; the preprocessor overwrites it. */
|
|
232
|
+
--neutral-7: #a0a2a6; /** default border — DERIVED: OKLab lerp step5 → step9 at t=0.50. */
|
|
233
|
+
--neutral-8: #888a8e; /** hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75. */
|
|
234
|
+
--neutral-9: #717377; /** solid fill */
|
|
235
|
+
--neutral-10: #515357; /** solid fill hover */
|
|
236
|
+
--neutral-11: #3f4044; /** secondary text */
|
|
237
|
+
--neutral-12: #18191c; /** primary text */
|
|
169
238
|
--blue-1: #fafcfe; /** app tint — L* 99.01, C 0.003, hue 264. Radix blue-1 sits at L* 99.31 C 0.003. */
|
|
170
239
|
--blue-2: #f2f6fd; /** subtle background — L* 97.21, C 0.010. */
|
|
171
240
|
--blue-3: #e7eefc; /** component background default — L* 94.79, C 0.020. */
|
|
@@ -192,20 +261,20 @@
|
|
|
192
261
|
--cyan-11: #027a61; /** secondary text — L* 51.64, C 0.100. 5.17:1 on step 1. */
|
|
193
262
|
--cyan-12: #004133; /** primary text — L* 33.45, C 0.065. 11.35:1 on step 1. */
|
|
194
263
|
--cyan-contrast: #0d2b25; /** Label ink for the bright fill tier. Absolute near-black, identical in both modes, carrying a trace of the scale hue. A bright fill needs an explicit dark ink; comparing white against the scale's own step 12 picks white in dark mode, where step 12 is light, and measures 1.52:1. 10.35:1 on step 9, 9.40:1 on step 10. */
|
|
195
|
-
--background:
|
|
196
|
-
--foreground:
|
|
264
|
+
--background: 210 20% 98%; /** House canvas. Light is gray-50 under white cards (two-surface layering, as Vercel #fafafa/#fff); dark is OKLCH L 0.16, inside the near-black band every reference measures (Linear L 0.14, Raycast #040506). */
|
|
265
|
+
--foreground: 225 7.7% 10.2%;
|
|
197
266
|
--card: 0 0% 100%;
|
|
198
|
-
--card-foreground:
|
|
267
|
+
--card-foreground: 225 7.7% 10.2%;
|
|
199
268
|
--popover: 0 0% 100%;
|
|
200
|
-
--popover-foreground:
|
|
201
|
-
--primary:
|
|
269
|
+
--popover-foreground: 225 7.7% 10.2%;
|
|
270
|
+
--primary: 225 7.7% 10.2%; /** House action fill = ink, not blue (owner decision 2026-09-24). Every measured reference ships a solid near-black primary (Vercel #171717, Figma #000) and keeps its one saturated hue for a single signal. Blue now lives on --ring (focus, selection) only. */
|
|
202
271
|
--primary-foreground: 0 0% 100%;
|
|
203
|
-
--secondary:
|
|
204
|
-
--secondary-foreground:
|
|
205
|
-
--muted:
|
|
206
|
-
--muted-foreground:
|
|
207
|
-
--accent:
|
|
208
|
-
--accent-foreground:
|
|
272
|
+
--secondary: 220 14.3% 95.9%;
|
|
273
|
+
--secondary-foreground: 225 7.7% 10.2%;
|
|
274
|
+
--muted: 220 14.3% 95.9%;
|
|
275
|
+
--muted-foreground: 220 2.6% 45.5%;
|
|
276
|
+
--accent: 220 14.3% 95.9%;
|
|
277
|
+
--accent-foreground: 225 7.7% 10.2%; /** Softer brand blue — desaturated to match --primary, avoids 100% saturation glare on focus/accent */
|
|
209
278
|
--destructive: 0 84% 45%;
|
|
210
279
|
--destructive-foreground: 0 0% 100%;
|
|
211
280
|
--destructive-strong: 0 84% 45%; /** Red as FOREGROUND. In light mode --destructive already passes (5.42:1 on white), so this equals it; the token exists so consumers reference one name across themes. In DARK, --destructive is 0 63% 38% = 2.15:1 on --card, unreadable as text — see dark.json. Mirrors --warning-strong. */
|
|
@@ -217,17 +286,17 @@
|
|
|
217
286
|
--warning-strong: 38 92% 30%; /** Amber as FOREGROUND on a light surface. --warning is a fill: at 38 92% 50% it is 2.04:1 against --neutral-2, so it is unreadable as text or as an icon stroke. This darkened step is 5.17:1, which sits between --success (4.92) and --destructive (5.19) so the three status colours read with the same weight. Use --warning for fills with --warning-foreground on top; use this for amber text and icons. Destructive and success need no equivalent — they already pass AA as foreground. */
|
|
218
287
|
--info: 228 85% 56%;
|
|
219
288
|
--info-foreground: 0 0% 100%;
|
|
220
|
-
--border:
|
|
221
|
-
--input:
|
|
222
|
-
--ring: 222
|
|
223
|
-
--sidebar: 210
|
|
224
|
-
--sidebar-foreground:
|
|
225
|
-
--sidebar-primary: 228 85% 56
|
|
289
|
+
--border: 225 8% 90.2%;
|
|
290
|
+
--input: 225 8% 90.2%;
|
|
291
|
+
--ring: 222.8 85% 55.7%; /** The House blue. Focus rings, selection and links — the one saturated hue on a product screen. #2e65ee light / #6b93f5 dark. */
|
|
292
|
+
--sidebar: 210 20% 98%;
|
|
293
|
+
--sidebar-foreground: 225 7.7% 10.2%;
|
|
294
|
+
--sidebar-primary: 225 7.7% 10.2%; /** Current-page accent in the sidebar (icon tint + edge). Same value as --primary: the sidebar carried the superseded 228 85% 56% (#2f56ee, C 0.232), which the action-blue measurement trimmed to C 0.215. */
|
|
226
295
|
--sidebar-primary-foreground: 0 0% 100%;
|
|
227
|
-
--sidebar-accent:
|
|
228
|
-
--sidebar-accent-foreground:
|
|
229
|
-
--sidebar-border:
|
|
230
|
-
--sidebar-ring:
|
|
296
|
+
--sidebar-accent: 220 14.3% 95.9%;
|
|
297
|
+
--sidebar-accent-foreground: 225 7.7% 10.2%;
|
|
298
|
+
--sidebar-border: 225 8% 90.2%;
|
|
299
|
+
--sidebar-ring: 222.8 85% 55.7%;
|
|
231
300
|
--chart-1: 228 100% 50%;
|
|
232
301
|
--chart-2: 168 91% 49%;
|
|
233
302
|
--chart-3: 263 70% 66%;
|
|
@@ -376,17 +445,17 @@
|
|
|
376
445
|
--nebutra-cyan-800: #057963;
|
|
377
446
|
--nebutra-cyan-900: #035143;
|
|
378
447
|
--nebutra-cyan-950: #012923;
|
|
379
|
-
--nebutra-neutral-50: #
|
|
380
|
-
--nebutra-neutral-100: #
|
|
381
|
-
--nebutra-neutral-200: #
|
|
382
|
-
--nebutra-neutral-300: #
|
|
383
|
-
--nebutra-neutral-400: #
|
|
384
|
-
--nebutra-neutral-500: #
|
|
385
|
-
--nebutra-neutral-600: #
|
|
386
|
-
--nebutra-neutral-700: #
|
|
387
|
-
--nebutra-neutral-800: #
|
|
388
|
-
--nebutra-neutral-900: #
|
|
389
|
-
--nebutra-neutral-950: #
|
|
448
|
+
--nebutra-neutral-50: #f9fafb;
|
|
449
|
+
--nebutra-neutral-100: #f3f4f6;
|
|
450
|
+
--nebutra-neutral-200: #e4e5e8;
|
|
451
|
+
--nebutra-neutral-300: #d2d4d8;
|
|
452
|
+
--nebutra-neutral-400: #9fa1a5;
|
|
453
|
+
--nebutra-neutral-500: #717377;
|
|
454
|
+
--nebutra-neutral-600: #515357;
|
|
455
|
+
--nebutra-neutral-700: #3f4044;
|
|
456
|
+
--nebutra-neutral-800: #26272a;
|
|
457
|
+
--nebutra-neutral-900: #18191c;
|
|
458
|
+
--nebutra-neutral-950: #0b0c0d;
|
|
390
459
|
--nebutra-gray-50: #fafafa;
|
|
391
460
|
--nebutra-gray-100: #f5f5f5;
|
|
392
461
|
--nebutra-gray-200: #e5e5e5;
|
|
@@ -419,30 +488,99 @@
|
|
|
419
488
|
--duration-reveal: 300ms; /** Content unveil: slide-in, expand, accordion, drawer */
|
|
420
489
|
--duration-cinematic: 500ms; /** Hero-grade entrance: landing reveal, large delight moments */
|
|
421
490
|
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
422
|
-
--ease-out: cubic-bezier(0,
|
|
491
|
+
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
423
492
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
424
493
|
--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
425
494
|
--tracking-display: -0.04em; /** Hero H1 */
|
|
426
495
|
--tracking-heading: -0.03em; /** Section H2/H3 */
|
|
427
496
|
--tracking-tight: -0.015em; /** Body emphasis */
|
|
497
|
+
--tracking-label: 0.07em; /** Uppercase micro-labels. 0.07em is Vercel's measured eyebrow value (0.071em); open tracking keeps small caps legible. */
|
|
428
498
|
--leading-display: 1.05;
|
|
429
499
|
--leading-heading: 1.15;
|
|
500
|
+
--type-2xs-size: 0.6875rem;
|
|
501
|
+
--type-2xs-leading: 1.45;
|
|
502
|
+
--type-2xs-tracking: 0.01em;
|
|
503
|
+
--type-xs-size: 0.75rem;
|
|
504
|
+
--type-xs-leading: 1.4;
|
|
505
|
+
--type-xs-tracking: 0.005em;
|
|
506
|
+
--type-ui-size: 0.8125rem;
|
|
507
|
+
--type-ui-leading: 1.45;
|
|
508
|
+
--type-ui-tracking: 0em;
|
|
509
|
+
--type-sm-size: 0.875rem;
|
|
510
|
+
--type-sm-leading: 1.5;
|
|
511
|
+
--type-sm-tracking: 0em;
|
|
512
|
+
--type-base-size: 1rem;
|
|
513
|
+
--type-base-leading: 1.6;
|
|
514
|
+
--type-base-tracking: -0.005em;
|
|
515
|
+
--type-lg-size: 1.125rem;
|
|
516
|
+
--type-lg-leading: 1.55;
|
|
517
|
+
--type-lg-tracking: -0.01em;
|
|
518
|
+
--type-xl-size: 1.25rem;
|
|
519
|
+
--type-xl-leading: 1.45;
|
|
520
|
+
--type-xl-tracking: -0.012em;
|
|
521
|
+
--type-2xl-size: 1.5rem;
|
|
522
|
+
--type-2xl-leading: 1.33;
|
|
523
|
+
--type-2xl-tracking: -0.015em;
|
|
524
|
+
--type-3xl-size: 1.875rem;
|
|
525
|
+
--type-3xl-leading: 1.25;
|
|
526
|
+
--type-3xl-tracking: -0.02em;
|
|
527
|
+
--type-4xl-size: 2.25rem;
|
|
528
|
+
--type-4xl-leading: 1.15;
|
|
529
|
+
--type-4xl-tracking: -0.022em;
|
|
530
|
+
--type-5xl-size: 3rem;
|
|
531
|
+
--type-5xl-leading: 1.08;
|
|
532
|
+
--type-5xl-tracking: -0.028em;
|
|
533
|
+
--type-6xl-size: 3.75rem;
|
|
534
|
+
--type-6xl-leading: 1.04;
|
|
535
|
+
--type-6xl-tracking: -0.032em;
|
|
536
|
+
--type-7xl-size: 4.5rem;
|
|
537
|
+
--type-7xl-leading: 1;
|
|
538
|
+
--type-7xl-tracking: -0.035em;
|
|
539
|
+
--type-8xl-size: 6rem;
|
|
540
|
+
--type-8xl-leading: 1;
|
|
541
|
+
--type-8xl-tracking: -0.04em;
|
|
542
|
+
--type-9xl-size: 8rem;
|
|
543
|
+
--type-9xl-leading: 1;
|
|
544
|
+
--type-9xl-tracking: -0.045em;
|
|
545
|
+
--type-display-size: 1.25rem;
|
|
546
|
+
--type-display-leading: 1.45;
|
|
547
|
+
--type-display-tracking: -0.012em;
|
|
548
|
+
--type-body-size: 0.875rem;
|
|
549
|
+
--type-body-leading: 1.5;
|
|
550
|
+
--type-body-tracking: 0em;
|
|
551
|
+
--type-label-size: 0.75rem;
|
|
552
|
+
--type-label-leading: 1.4;
|
|
553
|
+
--type-label-tracking: 0.005em;
|
|
554
|
+
--type-meta-size: 0.6875rem;
|
|
555
|
+
--type-meta-leading: 1.45;
|
|
556
|
+
--type-meta-tracking: 0.01em;
|
|
557
|
+
--layer-raised: 10; /** Within a component: an element lifted over its siblings. */
|
|
558
|
+
--layer-sticky: 20; /** Sticky headers and table heads. */
|
|
559
|
+
--layer-floating: 30; /** Floating toolbars and canvas chrome anchored to content. */
|
|
560
|
+
--layer-nav: 40; /** App navigation and the shell's top bar. */
|
|
561
|
+
--layer-drawer: 60; /** Mobile navigation drawers and their toggles. */
|
|
562
|
+
--layer-banner: 90; /** Page-level banners: cookie consent, announcements. */
|
|
563
|
+
--layer-skip-link: 100; /** The skip link — above page chrome whenever it is focused. */
|
|
564
|
+
--layer-panel: 200; /** App-owned floating panels that are not overlays (locale switcher, search flyouts). */
|
|
565
|
+
--layer-backdrop: 1040; /** Overlay tier. Mirrors overlayZIndex in @nebutra/ui; a test keeps them equal. */
|
|
566
|
+
--layer-modal: 1050; /** Dialogs and sheets. */
|
|
567
|
+
--layer-popover: 1060; /** Popovers, menus, selects. */
|
|
568
|
+
--layer-tooltip: 1070; /** Tooltips. */
|
|
569
|
+
--layer-devtools: 9999; /** Developer and debug surfaces only — never product UI. */
|
|
430
570
|
--ring-hairline: inset 0 0 0 1px rgb(255 255 255 / 0.06); /** Hairline override — more visible on dark background */
|
|
431
571
|
--overlay-faint: rgb(
|
|
432
572
|
255 255 255 / 0.06
|
|
433
573
|
); /** Dark-mode faint overlay (6% white) — inverted from light */
|
|
434
574
|
--font-sans:
|
|
435
|
-
var(--font-geist-sans), "Geist", var(--font-
|
|
436
|
-
|
|
575
|
+
var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei",
|
|
576
|
+
-apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
|
437
577
|
--font-cn:
|
|
438
|
-
var(--font-geist-sans), "Geist", var(--font-
|
|
439
|
-
|
|
578
|
+
var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei",
|
|
579
|
+
sans-serif; /** CJK-locale stack — same order as sans, minus the Latin-only system faces */
|
|
440
580
|
--font-display:
|
|
441
|
-
var(--font-
|
|
442
|
-
sans-serif;
|
|
581
|
+
var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
|
|
443
582
|
--font-heading:
|
|
444
|
-
var(--font-
|
|
445
|
-
sans-serif;
|
|
583
|
+
var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
|
|
446
584
|
--font-mono:
|
|
447
585
|
var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
448
586
|
--brand-primary: #5c7cfa; /** Dark-mode brand primary — the identity blue lifted to nebutra-blue-400 so it stays visible on dark surfaces. This is the identity alias, not the functional fill: the dark solid fill is --blue-9 (#396ae2, white label 4.85:1). The two are deliberately separate now that the VI hex is no longer pinned into ramp slot 9. */
|
|
@@ -485,18 +623,18 @@
|
|
|
485
623
|
--motion-duration-reveal: 300ms;
|
|
486
624
|
--motion-duration-cinematic: 500ms;
|
|
487
625
|
--focus-ring: 0 0 0 2px hsl(var(--ring) / 0.4); /** Single-layer translucent focus ring (2026 pattern). Legacy box-shadow consumers stay compatible; modern surfaces should prefer the global :focus-visible outline rule in static/base.css. */
|
|
488
|
-
--neutral-1: #
|
|
489
|
-
--neutral-2: #
|
|
490
|
-
--neutral-3: #
|
|
491
|
-
--neutral-4: #
|
|
492
|
-
--neutral-5: #
|
|
626
|
+
--neutral-1: #0b0c0d;
|
|
627
|
+
--neutral-2: #18191c;
|
|
628
|
+
--neutral-3: #26272a;
|
|
629
|
+
--neutral-4: #3f4044;
|
|
630
|
+
--neutral-5: #515357;
|
|
493
631
|
--neutral-6: #666666; /** subtle border — DERIVED: OKLab lerp step5 → step9 at t=0.25, chroma forced to 0. The achromatic result is deliberate and preserves the documented intent of color.nebutra-gray ('used where blue undertone clashes — primarily dark-mode borders, dividers, elevation hairlines'). What was broken was the LIGHTNESS: gray-900/800/700 are darker than the slate background tier they sit above, so the border read as a hole rather than a hairline. */
|
|
494
|
-
--neutral-7: #
|
|
632
|
+
--neutral-7: #797979; /** default border — DERIVED: OKLab lerp step5 → step9 at t=0.50, chroma 0. */
|
|
495
633
|
--neutral-8: #8d8d8d; /** hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75, chroma 0. */
|
|
496
|
-
--neutral-9: #
|
|
497
|
-
--neutral-10: #
|
|
498
|
-
--neutral-11: #
|
|
499
|
-
--neutral-12: #
|
|
634
|
+
--neutral-9: #9fa1a5;
|
|
635
|
+
--neutral-10: #d2d4d8;
|
|
636
|
+
--neutral-11: #e4e5e8;
|
|
637
|
+
--neutral-12: #f9fafb;
|
|
500
638
|
--blue-1: #030815; /** app tint — L* 13.59, C 0.031, hue 264. */
|
|
501
639
|
--blue-2: #060f25; /** subtle background — L* 17.43, C 0.048. */
|
|
502
640
|
--blue-3: #09183b; /** component background default — L* 22.04, C 0.071. */
|
|
@@ -523,20 +661,20 @@
|
|
|
523
661
|
--cyan-11: #90fbdb; /** secondary text — L* 91.46, C 0.110. 15.68:1 on step 1. */
|
|
524
662
|
--cyan-12: #bdfde8; /** primary text — L* 94.54, C 0.070. 16.95:1 on step 1. */
|
|
525
663
|
--cyan-contrast: #0d2b25; /** Label ink for the bright fill tier. Absolute near-black, identical in both modes. 10.35:1 on step 9, 11.34:1 on step 10. */
|
|
526
|
-
--background:
|
|
527
|
-
--foreground:
|
|
528
|
-
--card:
|
|
529
|
-
--card-foreground:
|
|
530
|
-
--popover:
|
|
531
|
-
--popover-foreground:
|
|
532
|
-
--primary:
|
|
533
|
-
--primary-foreground:
|
|
534
|
-
--secondary:
|
|
535
|
-
--secondary-foreground:
|
|
536
|
-
--muted:
|
|
537
|
-
--muted-foreground:
|
|
538
|
-
--accent:
|
|
539
|
-
--accent-foreground: 228
|
|
664
|
+
--background: 220 11.1% 5.3%; /** House canvas. Light is gray-50 under white cards (two-surface layering, as Vercel #fafafa/#fff); dark is OKLCH L 0.16, inside the near-black band every reference measures (Linear L 0.14, Raycast #040506). */
|
|
665
|
+
--foreground: 228 23.8% 95.9%;
|
|
666
|
+
--card: 220 7.3% 8%;
|
|
667
|
+
--card-foreground: 228 23.8% 95.9%;
|
|
668
|
+
--popover: 225 7.7% 10.2%;
|
|
669
|
+
--popover-foreground: 228 23.8% 95.9%;
|
|
670
|
+
--primary: 228 23.8% 95.9%; /** House action fill = ink, not blue (owner decision 2026-09-24). Every measured reference ships a solid near-black primary (Vercel #171717, Figma #000) and keeps its one saturated hue for a single signal. Blue now lives on --ring (focus, selection) only. */
|
|
671
|
+
--primary-foreground: 220 11.1% 5.3%; /** White, matching light mode and matching --blue-contrast. It was near-black (222 14% 9%) when --primary was a LIGHT fill at L* 68.3, where dark ink read 6.20:1. Once --primary became a deep fill at L* 55.9 the pairing inverted and measured 3.74:1, below AA — the fill moved and the ink did not. The deep-fill tier takes white in both modes; --blue-contrast says the same thing, and they must agree because --primary and --blue-9 are now the same value. */
|
|
672
|
+
--secondary: 225 6.2% 12.5%;
|
|
673
|
+
--secondary-foreground: 228 23.8% 95.9%;
|
|
674
|
+
--muted: 225 6.2% 12.5%;
|
|
675
|
+
--muted-foreground: 228 2.6% 62.5%;
|
|
676
|
+
--accent: 225 6.2% 12.5%;
|
|
677
|
+
--accent-foreground: 228 23.8% 95.9%;
|
|
540
678
|
--destructive: 0 63% 38%;
|
|
541
679
|
--destructive-foreground: 210 18% 96%;
|
|
542
680
|
--destructive-strong: 0 80% 66%; /** Red as FOREGROUND on a dark surface. --destructive (0 63% 38%) is 2.15:1 on --card here — a fill only. Lightened to 5.18:1 on --card / 5.61:1 on --neutral-1, which sits alongside the light-mode 5.42:1 so red reads with the same weight in both themes. Use --destructive for fills with --destructive-foreground on top; use this for red text and icons. */
|
|
@@ -548,17 +686,17 @@
|
|
|
548
686
|
--warning-strong: 38 80% 52%; /** Amber as foreground on a dark surface. Unlike light mode this needs no adjustment — 38 80% 52% is already 7.98:1 against --neutral-1 — so it deliberately equals --warning. The token exists in both themes so consumers can reference one name. */
|
|
549
687
|
--info: 228 95% 72%;
|
|
550
688
|
--info-foreground: 222 14% 9%;
|
|
551
|
-
--border: 220
|
|
552
|
-
--input: 220
|
|
553
|
-
--ring:
|
|
554
|
-
--sidebar:
|
|
555
|
-
--sidebar-foreground:
|
|
556
|
-
--sidebar-primary: 228 95% 72
|
|
557
|
-
--sidebar-primary-foreground:
|
|
558
|
-
--sidebar-accent:
|
|
559
|
-
--sidebar-accent-foreground: 228
|
|
560
|
-
--sidebar-border:
|
|
561
|
-
--sidebar-ring:
|
|
689
|
+
--border: 220 3.6% 16.3%;
|
|
690
|
+
--input: 220 3.6% 16.3%;
|
|
691
|
+
--ring: 222.6 87.3% 69%; /** The House blue. Focus rings, selection and links — the one saturated hue on a product screen. #2e65ee light / #6b93f5 dark. */
|
|
692
|
+
--sidebar: 220 11.1% 5.3%;
|
|
693
|
+
--sidebar-foreground: 228 23.8% 95.9%;
|
|
694
|
+
--sidebar-primary: 228 23.8% 95.9%; /** Current-page accent in the sidebar. = blue-11 (#89aaee, L* 74.0, C 0.106), the ramp's measured secondary-text tier on dark (8.62:1 on step 1). Was 228 95% 72% — 95% chroma sat outside every measured band and matched no ramp step. */
|
|
695
|
+
--sidebar-primary-foreground: 220 11.1% 5.3%;
|
|
696
|
+
--sidebar-accent: 225 6.2% 12.5%;
|
|
697
|
+
--sidebar-accent-foreground: 228 23.8% 95.9%;
|
|
698
|
+
--sidebar-border: 225 6.2% 12.5%;
|
|
699
|
+
--sidebar-ring: 222.6 87.3% 69%;
|
|
562
700
|
--chart-1: 228 95% 67%;
|
|
563
701
|
--chart-2: 168 96% 54%;
|
|
564
702
|
--chart-3: 228 100% 82%;
|
|
@@ -719,6 +857,12 @@
|
|
|
719
857
|
--color-success-foreground: hsl(var(--success-foreground));
|
|
720
858
|
--color-warning: hsl(var(--warning));
|
|
721
859
|
--color-warning-foreground: hsl(var(--warning-foreground));
|
|
860
|
+
/* Status colours as INK (text, icon fill/stroke). --destructive / --warning /
|
|
861
|
+
--success are fills: amber is 2.0:1 as text in light, red 2.5:1 in dark.
|
|
862
|
+
text-destructive-strong etc. read at ~5:1 in both themes. */
|
|
863
|
+
--color-destructive-strong: hsl(var(--destructive-strong));
|
|
864
|
+
--color-warning-strong: hsl(var(--warning-strong));
|
|
865
|
+
--color-success-strong: hsl(var(--success-strong));
|
|
722
866
|
--color-info: hsl(var(--info));
|
|
723
867
|
--color-info-foreground: hsl(var(--info-foreground));
|
|
724
868
|
--color-border: hsl(var(--border));
|
|
@@ -834,6 +978,22 @@
|
|
|
834
978
|
--radius-3xl: 1.5rem;
|
|
835
979
|
--radius-full: 9999px;
|
|
836
980
|
|
|
981
|
+
/* Containers — the page-width contract from CLAUDE.md, as real utilities.
|
|
982
|
+
max-w-text (reading), max-w-content (pricing/blog), max-w-wide (feature bento, navbar).
|
|
983
|
+
|
|
984
|
+
These values also reach :root through the semantic layer, so var(--container-wide) keeps
|
|
985
|
+
working; what was missing was the Tailwind side. Without it there is no max-w-wide to reach
|
|
986
|
+
for, and 92 call sites across apps/** had typed max-w-wide instead — a decided number
|
|
987
|
+
living in a className, where changing the token cannot move it.
|
|
988
|
+
|
|
989
|
+
Literals rather than var(--container-wide): Tailwind emits an @theme entry into its own
|
|
990
|
+
layer, so a same-named reference becomes self-referential and survives only because the
|
|
991
|
+
semantic :root happens to come later in the cascade. Duplicating the number and pinning it
|
|
992
|
+
with a parity test is the honest trade — see design-tokens' container parity test. */
|
|
993
|
+
--container-text: 896px;
|
|
994
|
+
--container-content: 1152px;
|
|
995
|
+
--container-wide: 1400px;
|
|
996
|
+
|
|
837
997
|
/* Shadows */
|
|
838
998
|
--shadow-xs: var(--elevation-xs);
|
|
839
999
|
--shadow-sm: var(--elevation-sm);
|
|
@@ -873,15 +1033,90 @@
|
|
|
873
1033
|
(No backticks in this block — it is inside a JS template literal.) */
|
|
874
1034
|
--ease-in: var(--motion-ease-in);
|
|
875
1035
|
--ease-out: var(--motion-ease-out);
|
|
1036
|
+
--ease-brand: var(--motion-ease-brand);
|
|
876
1037
|
--ease-in-out: var(--motion-ease-in-out);
|
|
877
1038
|
--ease-spring: var(--motion-ease-spring);
|
|
878
1039
|
|
|
1040
|
+
/* Type scale — see core.json:type. Indirect so a Brand Package can retune size,
|
|
1041
|
+
leading and tracking; each text-* utility sets all three. */
|
|
1042
|
+
--text-2xs: var(--type-2xs-size);
|
|
1043
|
+
--text-2xs--line-height: var(--type-2xs-leading);
|
|
1044
|
+
--text-2xs--letter-spacing: var(--type-2xs-tracking);
|
|
1045
|
+
--text-xs: var(--type-xs-size);
|
|
1046
|
+
--text-xs--line-height: var(--type-xs-leading);
|
|
1047
|
+
--text-xs--letter-spacing: var(--type-xs-tracking);
|
|
1048
|
+
--text-ui: var(--type-ui-size);
|
|
1049
|
+
--text-ui--line-height: var(--type-ui-leading);
|
|
1050
|
+
--text-ui--letter-spacing: var(--type-ui-tracking);
|
|
1051
|
+
--text-sm: var(--type-sm-size);
|
|
1052
|
+
--text-sm--line-height: var(--type-sm-leading);
|
|
1053
|
+
--text-sm--letter-spacing: var(--type-sm-tracking);
|
|
1054
|
+
--text-base: var(--type-base-size);
|
|
1055
|
+
--text-base--line-height: var(--type-base-leading);
|
|
1056
|
+
--text-base--letter-spacing: var(--type-base-tracking);
|
|
1057
|
+
--text-lg: var(--type-lg-size);
|
|
1058
|
+
--text-lg--line-height: var(--type-lg-leading);
|
|
1059
|
+
--text-lg--letter-spacing: var(--type-lg-tracking);
|
|
1060
|
+
--text-xl: var(--type-xl-size);
|
|
1061
|
+
--text-xl--line-height: var(--type-xl-leading);
|
|
1062
|
+
--text-xl--letter-spacing: var(--type-xl-tracking);
|
|
1063
|
+
--text-2xl: var(--type-2xl-size);
|
|
1064
|
+
--text-2xl--line-height: var(--type-2xl-leading);
|
|
1065
|
+
--text-2xl--letter-spacing: var(--type-2xl-tracking);
|
|
1066
|
+
--text-3xl: var(--type-3xl-size);
|
|
1067
|
+
--text-3xl--line-height: var(--type-3xl-leading);
|
|
1068
|
+
--text-3xl--letter-spacing: var(--type-3xl-tracking);
|
|
1069
|
+
--text-4xl: var(--type-4xl-size);
|
|
1070
|
+
--text-4xl--line-height: var(--type-4xl-leading);
|
|
1071
|
+
--text-4xl--letter-spacing: var(--type-4xl-tracking);
|
|
1072
|
+
--text-5xl: var(--type-5xl-size);
|
|
1073
|
+
--text-5xl--line-height: var(--type-5xl-leading);
|
|
1074
|
+
--text-5xl--letter-spacing: var(--type-5xl-tracking);
|
|
1075
|
+
--text-6xl: var(--type-6xl-size);
|
|
1076
|
+
--text-6xl--line-height: var(--type-6xl-leading);
|
|
1077
|
+
--text-6xl--letter-spacing: var(--type-6xl-tracking);
|
|
1078
|
+
--text-7xl: var(--type-7xl-size);
|
|
1079
|
+
--text-7xl--line-height: var(--type-7xl-leading);
|
|
1080
|
+
--text-7xl--letter-spacing: var(--type-7xl-tracking);
|
|
1081
|
+
--text-8xl: var(--type-8xl-size);
|
|
1082
|
+
--text-8xl--line-height: var(--type-8xl-leading);
|
|
1083
|
+
--text-8xl--letter-spacing: var(--type-8xl-tracking);
|
|
1084
|
+
--text-9xl: var(--type-9xl-size);
|
|
1085
|
+
--text-9xl--line-height: var(--type-9xl-leading);
|
|
1086
|
+
--text-9xl--letter-spacing: var(--type-9xl-tracking);
|
|
1087
|
+
--text-display: var(--type-display-size);
|
|
1088
|
+
--text-display--line-height: var(--type-display-leading);
|
|
1089
|
+
--text-display--letter-spacing: var(--type-display-tracking);
|
|
1090
|
+
--text-body: var(--type-body-size);
|
|
1091
|
+
--text-body--line-height: var(--type-body-leading);
|
|
1092
|
+
--text-body--letter-spacing: var(--type-body-tracking);
|
|
1093
|
+
--text-label: var(--type-label-size);
|
|
1094
|
+
--text-label--line-height: var(--type-label-leading);
|
|
1095
|
+
--text-label--letter-spacing: var(--type-label-tracking);
|
|
1096
|
+
--text-meta: var(--type-meta-size);
|
|
1097
|
+
--text-meta--line-height: var(--type-meta-leading);
|
|
1098
|
+
--text-meta--letter-spacing: var(--type-meta-tracking);
|
|
1099
|
+
|
|
879
1100
|
/* Motion — Durations (four-rail; see core.json:duration). Names denote intent, not relative speed. */
|
|
880
1101
|
--duration-micro: var(--motion-duration-micro);
|
|
881
1102
|
--duration-flow: var(--motion-duration-flow);
|
|
882
1103
|
--duration-reveal: var(--motion-duration-reveal);
|
|
883
1104
|
--duration-cinematic: var(--motion-duration-cinematic);
|
|
884
1105
|
|
|
1106
|
+
/* The same four rails on the namespace Tailwind reads for duration-*.
|
|
1107
|
+
--duration-* is the variable other CSS consumes; the utility class is built
|
|
1108
|
+
from --transition-duration-*, and only that one. Without these,
|
|
1109
|
+
duration-micro and duration-reveal are class names that generate nothing:
|
|
1110
|
+
they were in use in the component library and on the marketing site, and
|
|
1111
|
+
every one of them silently fell back to the 150ms default. */
|
|
1112
|
+
--transition-duration-micro: var(--motion-duration-micro);
|
|
1113
|
+
--transition-duration-flow: var(--motion-duration-flow);
|
|
1114
|
+
--transition-duration-reveal: var(--motion-duration-reveal);
|
|
1115
|
+
--transition-duration-cinematic: var(--motion-duration-cinematic);
|
|
1116
|
+
|
|
1117
|
+
/* Likewise for ease-*: the utility namespace is --ease-* already, so the four
|
|
1118
|
+
above are reachable as ease-brand / ease-spring without further mapping. */
|
|
1119
|
+
|
|
885
1120
|
/* Spacing — DO NOT register --spacing-sm|md|lg|xl|2xl here.
|
|
886
1121
|
Tailwind v4 puts every --spacing-{key} on the shared size rail, so
|
|
887
1122
|
max-w-sm / max-w-2xl (and width/height/gap utilities with the same keys)
|
|
@@ -893,22 +1128,21 @@
|
|
|
893
1128
|
--spacing-{step} alias. --spacing itself (0.25rem arithmetic base) stays
|
|
894
1129
|
untouched. See static/base.css. */
|
|
895
1130
|
|
|
896
|
-
/* Font family — Geist keeps Latin + numerals,
|
|
897
|
-
CJK, system CJK faces behind
|
|
898
|
-
|
|
899
|
-
the two in sync
|
|
1131
|
+
/* Font family — Geist keeps Latin + numerals for body/UI, DM Sans sets Latin
|
|
1132
|
+
display and headings, self-hosted MiSans takes CJK, system CJK faces behind
|
|
1133
|
+
it. Order is the design decision; see the fontFamily $description in
|
|
1134
|
+
tokens/core.json. Mirror of those tokens — keep the two in sync
|
|
1135
|
+
(verify:parity compares them). */
|
|
900
1136
|
--font-sans:
|
|
901
|
-
var(--font-geist-sans), "Geist", var(--font-
|
|
902
|
-
|
|
1137
|
+
var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei",
|
|
1138
|
+
-apple-system, BlinkMacSystemFont, system-ui, sans-serif;
|
|
903
1139
|
--font-cn:
|
|
904
|
-
var(--font-geist-sans), "Geist", var(--font-
|
|
905
|
-
"PingFang SC", "Microsoft YaHei", sans-serif;
|
|
906
|
-
--font-display:
|
|
907
|
-
var(--font-geist-sans), "Geist", var(--font-vivo-sans-sc, "vivo Sans SC"), "Noto Sans SC",
|
|
1140
|
+
var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei",
|
|
908
1141
|
sans-serif;
|
|
1142
|
+
--font-display:
|
|
1143
|
+
var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
|
|
909
1144
|
--font-heading:
|
|
910
|
-
var(--font-
|
|
911
|
-
sans-serif;
|
|
1145
|
+
var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
|
|
912
1146
|
--font-mono:
|
|
913
1147
|
var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
914
1148
|
}
|
|
@@ -938,9 +1172,71 @@
|
|
|
938
1172
|
─────────────────────────────────────────────────────────── */
|
|
939
1173
|
:root {
|
|
940
1174
|
--motion-ease-in: cubic-bezier(0.4, 0, 1, 1);
|
|
941
|
-
--motion-ease-out: cubic-bezier(0,
|
|
1175
|
+
--motion-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
|
942
1176
|
--motion-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
|
943
1177
|
--motion-ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
1178
|
+
/* The signature: a long, strong deceleration — most of the distance is
|
|
1179
|
+
covered in the first fifth of the time, then it settles. It has existed as
|
|
1180
|
+
`brandEasing.brand` in @nebutra/brand since the motion language was
|
|
1181
|
+
written, usable only from JS, so every CSS transition on every marketing
|
|
1182
|
+
surface fell back to Tailwind's default curve instead. That default is
|
|
1183
|
+
what "the hovers feel stiff" is: a near-linear ramp at 150ms. */
|
|
1184
|
+
--motion-ease-brand: cubic-bezier(0.16, 1, 0.3, 1);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/* ─── Category rail ──────────────────────────────────────────
|
|
1188
|
+
Four accents for taxonomy — the monorepo tree's four lanes, and
|
|
1189
|
+
anywhere else a small set of peers has to be told apart.
|
|
1190
|
+
|
|
1191
|
+
Derived, not picked. Hand-picking is how the file tree ended up
|
|
1192
|
+
with fifteen hues across twenty-five rows, three of them reused
|
|
1193
|
+
on unrelated rows: a colour that encodes nothing a reader can
|
|
1194
|
+
learn and follows no design language.
|
|
1195
|
+
|
|
1196
|
+
Each accent is a position on the background→foreground axis,
|
|
1197
|
+
tinted a quarter of the way toward the action fill rotated by
|
|
1198
|
+
72°. Two axes, both always live:
|
|
1199
|
+
|
|
1200
|
+
• the position carries contrast. That axis is non-degenerate
|
|
1201
|
+
in every language by definition — it is the axis that makes
|
|
1202
|
+
body text readable — so a monochrome language still gets
|
|
1203
|
+
four distinct tones. Vercel and Notion anchor on black,
|
|
1204
|
+
where rotating hue returns the same colour four times.
|
|
1205
|
+
• the rotation carries hue separation for the languages that
|
|
1206
|
+
have chroma to give.
|
|
1207
|
+
|
|
1208
|
+
Two earlier constructions failed here and are worth naming.
|
|
1209
|
+
Offsetting lightness from the anchor and clamping collapses when
|
|
1210
|
+
the anchor already sits near the band edge — Raycast's fill is
|
|
1211
|
+
near-white, so all four pinned to one value. A fixed lightness
|
|
1212
|
+
ladder cannot serve both canvases: Raycast and GSAP are dark by
|
|
1213
|
+
default in light mode, so the `.dark` class is not set and an
|
|
1214
|
+
absolute ladder is upside down for them.
|
|
1215
|
+
|
|
1216
|
+
Measured across all seven languages plus factory: lowest contrast
|
|
1217
|
+
3.50:1 against each canvas, closest pair 30/255 apart.
|
|
1218
|
+
─────────────────────────────────────────────────────────── */
|
|
1219
|
+
:root {
|
|
1220
|
+
--category-1: color-mix(
|
|
1221
|
+
in oklab,
|
|
1222
|
+
color-mix(in oklab, hsl(var(--foreground)) 55%, hsl(var(--background))) 75%,
|
|
1223
|
+
oklch(from hsl(var(--primary)) l c h)
|
|
1224
|
+
);
|
|
1225
|
+
--category-2: color-mix(
|
|
1226
|
+
in oklab,
|
|
1227
|
+
color-mix(in oklab, hsl(var(--foreground)) 67%, hsl(var(--background))) 75%,
|
|
1228
|
+
oklch(from hsl(var(--primary)) l c calc(h + 72))
|
|
1229
|
+
);
|
|
1230
|
+
--category-3: color-mix(
|
|
1231
|
+
in oklab,
|
|
1232
|
+
color-mix(in oklab, hsl(var(--foreground)) 79%, hsl(var(--background))) 75%,
|
|
1233
|
+
oklch(from hsl(var(--primary)) l c calc(h + 144))
|
|
1234
|
+
);
|
|
1235
|
+
--category-4: color-mix(
|
|
1236
|
+
in oklab,
|
|
1237
|
+
color-mix(in oklab, hsl(var(--foreground)) 91%, hsl(var(--background))) 75%,
|
|
1238
|
+
oklch(from hsl(var(--primary)) l c calc(h + 216))
|
|
1239
|
+
);
|
|
944
1240
|
}
|
|
945
1241
|
|
|
946
1242
|
/* ─── Spacing source rail ────────────────────────────────────
|
|
@@ -992,6 +1288,20 @@
|
|
|
992
1288
|
border-r-transparent can override it. */
|
|
993
1289
|
border-color: hsl(var(--border));
|
|
994
1290
|
}
|
|
1291
|
+
|
|
1292
|
+
/* Headings take the display face. Layered (base) so any utility — font-*,
|
|
1293
|
+
font-semibold, a text-heading-* recipe — still wins; zero-specificity
|
|
1294
|
+
:where() so a component's own heading styles are never fought. The face
|
|
1295
|
+
and weight are variables, so a Brand Package retunes both. */
|
|
1296
|
+
:where(h1, h2, h3, h4, h5, h6) {
|
|
1297
|
+
font-family: var(--font-heading);
|
|
1298
|
+
font-weight: var(--font-weight-heading, 500);
|
|
1299
|
+
line-height: 1.25;
|
|
1300
|
+
}
|
|
1301
|
+
/* This is the ONLY heading rule. @nebutra/ui fonts.css and the web and
|
|
1302
|
+
landing globals each re-declared one (font-sans at 600), and whichever
|
|
1303
|
+
loaded last won: landing's h1 shipped in Geist 600 after the 2026-09-25
|
|
1304
|
+
decision set headings to DM Sans 500. */
|
|
995
1305
|
}
|
|
996
1306
|
|
|
997
1307
|
body {
|
|
@@ -1003,8 +1313,15 @@ body {
|
|
|
1003
1313
|
"calt" 1;
|
|
1004
1314
|
}
|
|
1005
1315
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1316
|
+
/* Smooth scrolling is an enhancement, never the default the whole document
|
|
1317
|
+
* inherits: it hijacks every programmatic scroll (route changes, focus jumps,
|
|
1318
|
+
* scrollIntoView) and turns them into long animated sweeps. The preference
|
|
1319
|
+
* gate makes the intent explicit; the reduced-motion block below forces
|
|
1320
|
+
* `auto` for users who ask for less motion. */
|
|
1321
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1322
|
+
html {
|
|
1323
|
+
scroll-behavior: smooth;
|
|
1324
|
+
}
|
|
1008
1325
|
}
|
|
1009
1326
|
|
|
1010
1327
|
/*
|
|
@@ -1026,7 +1343,7 @@ html {
|
|
|
1026
1343
|
:focus-visible:not(
|
|
1027
1344
|
:where(input, textarea, select, [cmdk-input], [role="textbox"], [contenteditable="true"])
|
|
1028
1345
|
) {
|
|
1029
|
-
outline: 2px solid hsl(var(--ring)
|
|
1346
|
+
outline: 2px solid hsl(var(--ring));
|
|
1030
1347
|
outline-offset: 2px;
|
|
1031
1348
|
border-radius: inherit;
|
|
1032
1349
|
}
|
|
@@ -1046,24 +1363,6 @@ html {
|
|
|
1046
1363
|
}
|
|
1047
1364
|
}
|
|
1048
1365
|
|
|
1049
|
-
@keyframes accordion-down {
|
|
1050
|
-
from {
|
|
1051
|
-
height: 0;
|
|
1052
|
-
}
|
|
1053
|
-
to {
|
|
1054
|
-
height: var(--radix-accordion-content-height);
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
@keyframes accordion-up {
|
|
1059
|
-
from {
|
|
1060
|
-
height: var(--radix-accordion-content-height);
|
|
1061
|
-
}
|
|
1062
|
-
to {
|
|
1063
|
-
height: 0;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
1366
|
@keyframes fade-in {
|
|
1068
1367
|
from {
|
|
1069
1368
|
opacity: 0;
|
|
@@ -1307,6 +1606,34 @@ html {
|
|
|
1307
1606
|
font-weight: 500;
|
|
1308
1607
|
}
|
|
1309
1608
|
|
|
1609
|
+
/* Tracking utilities over the tracking tokens — the scale itself is
|
|
1610
|
+
* Tailwind's, so per-size letter-spacing has to be applied where a heading
|
|
1611
|
+
* is rendered. Exposing the token as a utility keeps that application in
|
|
1612
|
+
* one vocabulary instead of every surface re-deriving an em value. */
|
|
1613
|
+
@utility tracking-display {
|
|
1614
|
+
letter-spacing: var(--tracking-display);
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
@utility tracking-heading {
|
|
1618
|
+
letter-spacing: var(--tracking-heading);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
@utility tracking-label {
|
|
1622
|
+
letter-spacing: var(--tracking-label);
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
@utility tracking-tight-body {
|
|
1626
|
+
letter-spacing: var(--tracking-tight);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
@utility leading-display {
|
|
1630
|
+
line-height: var(--leading-display);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
@utility leading-heading {
|
|
1634
|
+
line-height: var(--leading-heading);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1310
1637
|
@utility text-label-20-strong {
|
|
1311
1638
|
font-family: var(--font-sans);
|
|
1312
1639
|
font-size: 1.25rem;
|
|
@@ -1631,18 +1958,29 @@ html {
|
|
|
1631
1958
|
|
|
1632
1959
|
/*
|
|
1633
1960
|
* flex-col + items-center:
|
|
1634
|
-
* 1) Direct children (incl. AnimateIn wrappers) get
|
|
1635
|
-
*
|
|
1636
|
-
* to min-content.
|
|
1961
|
+
* 1) Direct children (incl. AnimateIn wrappers) get min-width 0 so they can
|
|
1962
|
+
* wrap instead of forcing the column to min-content.
|
|
1637
1963
|
* 2) Nested prose (p/h*) under those columns takes width:100% so Chinese
|
|
1638
1964
|
* copy does not collapse to a 1-glyph strip. We deliberately do NOT set
|
|
1639
1965
|
* max-width here — leave Tailwind max-w-* / max-w-[…] in control.
|
|
1966
|
+
*
|
|
1967
|
+
* (1) used to also set `max-width: 100%`, which took that control away from
|
|
1968
|
+
* every utility it was written to defer to. `:is(html:lang(zh)) :is(.flex
|
|
1969
|
+
* .flex-col.items-center) > *` scores three classes; `.max-w-[20rem]` scores
|
|
1970
|
+
* one, so the cap lost — on CJK locales only. The footer newsletter field was
|
|
1971
|
+
* capped at 20rem in the markup and rendered 670px wide on /zh-Hans while the
|
|
1972
|
+
* English page was correct, which is the shape this class of bug always takes:
|
|
1973
|
+
* a global that only fires under :lang().
|
|
1974
|
+
*
|
|
1975
|
+
* min-width: 0 is what actually prevents the one-glyph strip. max-width: 100%
|
|
1976
|
+
* was doing nothing for it — a flex column child cannot exceed the line box
|
|
1977
|
+
* unless it is given an explicit larger width, and if it is, that is a
|
|
1978
|
+
* deliberate local decision this file should not silently overrule.
|
|
1640
1979
|
*/
|
|
1641
1980
|
:is(html:lang(zh), html:lang(ja), html:lang(ko))
|
|
1642
1981
|
:is(.flex.flex-col.items-center, .inline-flex.flex-col.items-center)
|
|
1643
1982
|
> * {
|
|
1644
1983
|
box-sizing: border-box;
|
|
1645
|
-
max-width: 100%;
|
|
1646
1984
|
min-width: 0;
|
|
1647
1985
|
}
|
|
1648
1986
|
|
|
@@ -1729,16 +2067,21 @@ html {
|
|
|
1729
2067
|
}
|
|
1730
2068
|
|
|
1731
2069
|
/* ─── Reduced Motion ─────────────────────────────────────── */
|
|
1732
|
-
/*
|
|
1733
|
-
*
|
|
1734
|
-
*
|
|
2070
|
+
/* Reduce, don't remove (WCAG 2.3.3, Apple HIG): movement and layout
|
|
2071
|
+
* animation stop, while opacity/colour transitions stay short so a state
|
|
2072
|
+
* change still reads as a change instead of an instant swap. Restricting
|
|
2073
|
+
* `transition-property` is what kills transform/height transitions without
|
|
2074
|
+
* touching the feedback that aids comprehension. Spinners and pulses are
|
|
2075
|
+
* functional progress indicators and keep their motion. */
|
|
1735
2076
|
@media (prefers-reduced-motion: reduce) {
|
|
1736
2077
|
*:not(.animate-spin):not(.animate-pulse):not(.animate-ping),
|
|
1737
2078
|
*:not(.animate-spin):not(.animate-pulse):not(.animate-ping)::before,
|
|
1738
2079
|
*:not(.animate-spin):not(.animate-pulse):not(.animate-ping)::after {
|
|
1739
2080
|
animation-duration: 0.01ms;
|
|
1740
2081
|
animation-iteration-count: 1;
|
|
1741
|
-
transition-
|
|
2082
|
+
transition-property:
|
|
2083
|
+
opacity, color, background-color, border-color, outline-color, fill, stroke, box-shadow;
|
|
2084
|
+
transition-duration: 120ms;
|
|
1742
2085
|
scroll-behavior: auto;
|
|
1743
2086
|
}
|
|
1744
2087
|
}
|