@nebutra/tokens 0.1.1 → 0.1.2

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 (57) hide show
  1. package/.turbo/turbo-build.log +5 -2
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/AGENTS.md +5 -2
  4. package/CHANGELOG.md +23 -0
  5. package/DESIGN.md +30 -8
  6. package/LICENSE +21 -676
  7. package/README.md +41 -8
  8. package/brands/gsap/brand.json +180 -0
  9. package/brands/linear/brand.json +202 -0
  10. package/brands/notion/brand.json +189 -0
  11. package/brands/raycast/brand.json +167 -0
  12. package/brands/stripe/brand.json +183 -0
  13. package/brands/vanta/brand.json +198 -0
  14. package/brands/vercel/brand.json +207 -0
  15. package/package.json +17 -8
  16. package/recipe.css +114 -0
  17. package/scripts/compile-brand-run.ts +79 -0
  18. package/scripts/compile-brand.mjs +25 -0
  19. package/scripts/emit-skins-run.ts +62 -0
  20. package/scripts/emit-skins.mjs +31 -0
  21. package/scripts/sync-styles.mjs +29 -0
  22. package/skins/README.md +151 -0
  23. package/skins/gsap.css +247 -0
  24. package/skins/linear.css +282 -0
  25. package/skins/notion.css +248 -0
  26. package/skins/raycast.css +272 -0
  27. package/skins/stripe.css +248 -0
  28. package/skins/vanta.css +255 -0
  29. package/skins/vercel.css +283 -0
  30. package/src/brand-package/__tests__/compile-refero.test.ts +427 -0
  31. package/src/brand-package/__tests__/emit-css.test.ts +130 -0
  32. package/src/brand-package/apply-brand.ts +92 -0
  33. package/src/brand-package/compile-helpers.ts +170 -0
  34. package/src/brand-package/compile-refero.ts +69 -0
  35. package/src/brand-package/emit-css.ts +468 -0
  36. package/src/brand-package/hex-to-hsl.ts +100 -0
  37. package/src/brand-package/index.ts +71 -0
  38. package/src/brand-package/infer-recipe.ts +198 -0
  39. package/src/brand-package/normalize.ts +295 -0
  40. package/src/brand-package/presets/context.ts +17 -0
  41. package/src/brand-package/presets/generic.ts +214 -0
  42. package/src/brand-package/presets/gsap.ts +146 -0
  43. package/src/brand-package/presets/index.ts +31 -0
  44. package/src/brand-package/presets/linear.ts +183 -0
  45. package/src/brand-package/presets/notion.ts +208 -0
  46. package/src/brand-package/presets/raycast.ts +138 -0
  47. package/src/brand-package/presets/recipe.ts +61 -0
  48. package/src/brand-package/presets/stripe.ts +207 -0
  49. package/src/brand-package/presets/vanta.ts +220 -0
  50. package/src/brand-package/presets/vercel.ts +187 -0
  51. package/src/brand-package/types.ts +285 -0
  52. package/src/brand-package/use-brand.ts +207 -0
  53. package/src/brand-package/validate.ts +108 -0
  54. package/src/index.ts +31 -1
  55. package/src/theme-provider.tsx +5 -3
  56. package/styles.css +153 -69
  57. package/turbo.json +15 -0
package/styles.css CHANGED
@@ -69,6 +69,10 @@
69
69
  --nebutra-gray-800: #262626;
70
70
  --nebutra-gray-900: #171717;
71
71
  --nebutra-gray-950: #0a0a0a;
72
+ --breakpoint-xs: 26.25rem; /** 420px — narrow-phone grid flip */
73
+ --breakpoint-tight: 67.5rem; /** 1080px — dense dashboard header horizontal */
74
+ --breakpoint-shell: 73.75rem; /** 1180px — three-pane shell threshold */
75
+ --breakpoint-3xl: 112.5rem; /** 1800px — 4K / Studio Display friendliness */
72
76
  --radius-none: 0;
73
77
  --radius-sm: 0.25rem;
74
78
  --radius-md: 0.375rem;
@@ -95,22 +99,43 @@
95
99
  --leading-heading: 1.15;
96
100
  --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) */
97
101
  --overlay-faint: rgb(0 0 0 / 0.06); /** Light-mode faint overlay (6% black) */
98
- --font-sans: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
102
+ --font-sans:
103
+ var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
104
+ -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
99
105
  --font-cn: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif;
100
- --font-display: "Geist", "Noto Sans SC", sans-serif;
101
- --font-heading: "Geist", "Noto Sans SC", sans-serif;
102
- --font-mono: "Geist Mono", "Fira Code", ui-monospace, Consolas, "Courier New", monospace;
106
+ --font-display: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
107
+ --font-heading: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
108
+ --font-mono:
109
+ var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
103
110
  --transition-duration: 200ms; /** Default transition duration — 200ms flow rail. */
104
111
  --transition-easing: ease-out; /** ease-out keyword form (CSS-friendly). Equivalent to cubic-bezier(0,0,0.2,1). */
105
112
  --brand-primary: #0033fe; /** Brand primary — 云毓蓝 */
106
113
  --brand-accent: #0bf1c3; /** Brand accent — 云毓青 */
107
114
  --brand-tertiary: #8b5cf6;
108
- --brand-gradient: linear-gradient(135deg, #254bfa 0%, #057963 100%); /** UI brand gradient — usage-adjusted from the locked VI blue/cyan anchors to reduce saturation and preserve white-text contrast. */
109
- --brand-gradient-reverse: linear-gradient(135deg, #057963 0%, #254bfa 100%);
110
- --brand-gradient-vertical: linear-gradient(180deg, #254bfa 0%, #057963 100%);
111
- --brand-gradient-radial: radial-gradient(circle, #057963 0%, #254bfa 100%);
112
- --brand-gradient-logo: linear-gradient(135deg, #0033fe 0%, #0bf1c3 100%); /** Logo/VI standard gradient. Use for brand assets only; product UI should use --brand-gradient. */
113
- --brand-gradient-logo-reverse: linear-gradient(135deg, #0bf1c3 0%, #0033fe 100%);
115
+ --brand-gradient-start: hsl(
116
+ var(--primary)
117
+ ); /** Legacy alias of --primary (shadcn product blue). Prefer bg-primary / Button default. */
118
+ --brand-gradient-end: hsl(
119
+ var(--primary)
120
+ ); /** Legacy alias of --primary. Prefer hover:bg-primary/90. */
121
+ --brand-gradient: hsl(
122
+ var(--primary)
123
+ ); /** Legacy --brand-gradient. Same color as --primary — not a second palette. App CTAs should use Button / bg-primary. */
124
+ --brand-gradient-reverse: hsl(var(--primary)); /** Legacy alias of --primary. */
125
+ --brand-gradient-vertical: hsl(var(--primary)); /** Legacy alias of --primary. */
126
+ --brand-gradient-radial: hsl(var(--primary)); /** Legacy alias of --primary. */
127
+ --brand-gradient-logo: linear-gradient(
128
+ 135deg,
129
+ #0033fe 0%,
130
+ #00a2e9 50%,
131
+ #0bf1c3 100%
132
+ ); /** Logo/VI gradient (云毓蓝→云毓青). Mid stop #00a2e9 is the OKLab perceptual midpoint (avoids sRGB mid-path soft spots). Brand assets only — not product CTAs. */
133
+ --brand-gradient-logo-reverse: linear-gradient(
134
+ 135deg,
135
+ #0bf1c3 0%,
136
+ #00a2e9 50%,
137
+ #0033fe 100%
138
+ ); /** Logo/VI reverse gradient with same OKLab mid stop. */
114
139
  --status-danger: #ef4444;
115
140
  --status-warning: #f59e0b;
116
141
  --status-success: #22c55e;
@@ -339,6 +364,10 @@
339
364
  --nebutra-gray-800: #262626;
340
365
  --nebutra-gray-900: #171717;
341
366
  --nebutra-gray-950: #0a0a0a;
367
+ --breakpoint-xs: 26.25rem; /** 420px — narrow-phone grid flip */
368
+ --breakpoint-tight: 67.5rem; /** 1080px — dense dashboard header horizontal */
369
+ --breakpoint-shell: 73.75rem; /** 1180px — three-pane shell threshold */
370
+ --breakpoint-3xl: 112.5rem; /** 1800px — 4K / Studio Display friendliness */
342
371
  --radius-none: 0;
343
372
  --radius-sm: 0.25rem;
344
373
  --radius-md: 0.375rem;
@@ -364,23 +393,46 @@
364
393
  --leading-display: 1.05;
365
394
  --leading-heading: 1.15;
366
395
  --ring-hairline: inset 0 0 0 1px rgb(255 255 255 / 0.06); /** Hairline override — more visible on dark background */
367
- --overlay-faint: rgb(255 255 255 / 0.06); /** Dark-mode faint overlay (6% white) — inverted from light */
368
- --font-sans: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
396
+ --overlay-faint: rgb(
397
+ 255 255 255 / 0.06
398
+ ); /** Dark-mode faint overlay (6% white) — inverted from light */
399
+ --font-sans:
400
+ var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
401
+ -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
369
402
  --font-cn: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif;
370
- --font-display: "Geist", "Noto Sans SC", sans-serif;
371
- --font-heading: "Geist", "Noto Sans SC", sans-serif;
372
- --font-mono: "Geist Mono", "Fira Code", ui-monospace, Consolas, "Courier New", monospace;
403
+ --font-display: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
404
+ --font-heading: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
405
+ --font-mono:
406
+ var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
373
407
  --transition-duration: 200ms; /** Default transition duration — 200ms flow rail. */
374
408
  --transition-easing: ease-out; /** ease-out keyword form (CSS-friendly). Equivalent to cubic-bezier(0,0,0.2,1). */
375
409
  --brand-primary: #5c7cfa; /** Dark-mode brand primary — visible against dark surfaces (uses --blue-9 = nebutra-blue-400). */
376
410
  --brand-accent: #1af7c8; /** Dark-mode brand accent — visible against dark surfaces (uses --cyan-9 = nebutra-cyan-400). */
377
411
  --brand-tertiary: #8b5cf6;
378
- --brand-gradient: linear-gradient(135deg, #254bfa 0%, #057963 100%); /** UI brand gradient — usage-adjusted from the locked VI blue/cyan anchors to reduce saturation and preserve white-text contrast. */
379
- --brand-gradient-reverse: linear-gradient(135deg, #057963 0%, #254bfa 100%);
380
- --brand-gradient-vertical: linear-gradient(180deg, #254bfa 0%, #057963 100%);
381
- --brand-gradient-radial: radial-gradient(circle, #057963 0%, #254bfa 100%);
382
- --brand-gradient-logo: linear-gradient(135deg, #0033fe 0%, #0bf1c3 100%); /** Logo/VI standard gradient. Use for brand assets only; product UI should use --brand-gradient. */
383
- --brand-gradient-logo-reverse: linear-gradient(135deg, #0bf1c3 0%, #0033fe 100%);
412
+ --brand-gradient-start: hsl(
413
+ var(--primary)
414
+ ); /** Legacy alias of --primary (shadcn product blue). Prefer bg-primary / Button default. */
415
+ --brand-gradient-end: hsl(
416
+ var(--primary)
417
+ ); /** Legacy alias of --primary. Prefer hover:bg-primary/90. */
418
+ --brand-gradient: hsl(
419
+ var(--primary)
420
+ ); /** Legacy --brand-gradient. Same color as --primary — not a second palette. App CTAs should use Button / bg-primary. */
421
+ --brand-gradient-reverse: hsl(var(--primary)); /** Legacy alias of --primary. */
422
+ --brand-gradient-vertical: hsl(var(--primary)); /** Legacy alias of --primary. */
423
+ --brand-gradient-radial: hsl(var(--primary)); /** Legacy alias of --primary. */
424
+ --brand-gradient-logo: linear-gradient(
425
+ 135deg,
426
+ #0033fe 0%,
427
+ #00a2e9 50%,
428
+ #0bf1c3 100%
429
+ ); /** Logo/VI gradient (云毓蓝→云毓青). Mid stop #00a2e9 is the OKLab perceptual midpoint (avoids sRGB mid-path soft spots). Brand assets only — not product CTAs. */
430
+ --brand-gradient-logo-reverse: linear-gradient(
431
+ 135deg,
432
+ #0bf1c3 0%,
433
+ #00a2e9 50%,
434
+ #0033fe 100%
435
+ ); /** Logo/VI reverse gradient with same OKLab mid stop. */
384
436
  --status-danger: #ef4444;
385
437
  --status-warning: #f59e0b;
386
438
  --status-success: #22c55e;
@@ -430,39 +482,39 @@
430
482
  --cyan-10: #4dfcd4;
431
483
  --cyan-11: #80ffe0;
432
484
  --cyan-12: #b3ffec;
433
- --background: 222 47% 4%;
434
- --foreground: 210 40% 98%;
435
- --card: 222 47% 7%;
436
- --card-foreground: 210 40% 98%;
437
- --popover: 222 47% 7%;
438
- --popover-foreground: 210 40% 98%;
485
+ --background: 222 14% 9%;
486
+ --foreground: 210 18% 96%;
487
+ --card: 222 12% 13%;
488
+ --card-foreground: 210 18% 96%;
489
+ --popover: 222 12% 13%;
490
+ --popover-foreground: 210 18% 96%;
439
491
  --primary: 228 90% 72%;
440
- --primary-foreground: 222 47% 4%;
441
- --secondary: 217 33% 17%;
442
- --secondary-foreground: 210 40% 98%;
443
- --muted: 217 33% 17%;
444
- --muted-foreground: 215 20% 65%;
445
- --accent: 228 100% 12%;
446
- --accent-foreground: 228 95% 67%;
447
- --destructive: 0 63% 31%;
448
- --destructive-foreground: 210 40% 98%;
449
- --success: 142 76% 36%;
450
- --success-foreground: 222 47% 4%;
451
- --warning: 38 80% 45%;
452
- --warning-foreground: 222 47% 4%;
453
- --info: 228 95% 67%;
454
- --info-foreground: 222 47% 4%;
455
- --border: 0 0% 14.9%;
456
- --input: 0 0% 14.9%;
492
+ --primary-foreground: 222 14% 9%;
493
+ --secondary: 222 10% 18%;
494
+ --secondary-foreground: 210 18% 96%;
495
+ --muted: 222 10% 17%;
496
+ --muted-foreground: 220 8% 65%;
497
+ --accent: 228 38% 22%;
498
+ --accent-foreground: 228 95% 80%;
499
+ --destructive: 0 63% 38%;
500
+ --destructive-foreground: 210 18% 96%;
501
+ --success: 142 60% 42%;
502
+ --success-foreground: 222 14% 9%;
503
+ --warning: 38 80% 52%;
504
+ --warning-foreground: 222 14% 9%;
505
+ --info: 228 95% 72%;
506
+ --info-foreground: 222 14% 9%;
507
+ --border: 220 8% 22%;
508
+ --input: 220 8% 22%;
457
509
  --ring: 0 0% 88%; /** Focus ring (dark mode) — neutral light gray. Mirrors light-mode neutral foreground choice. */
458
- --sidebar: 222 47% 6%;
459
- --sidebar-foreground: 210 40% 96%;
460
- --sidebar-primary: 228 95% 67%;
461
- --sidebar-primary-foreground: 222 47% 4%;
462
- --sidebar-accent: 228 100% 12%;
463
- --sidebar-accent-foreground: 228 95% 67%;
464
- --sidebar-border: 0 0% 14.9%;
465
- --sidebar-ring: 228 95% 67%;
510
+ --sidebar: 222 16% 8%;
511
+ --sidebar-foreground: 210 18% 94%;
512
+ --sidebar-primary: 228 95% 72%;
513
+ --sidebar-primary-foreground: 222 14% 9%;
514
+ --sidebar-accent: 228 38% 22%;
515
+ --sidebar-accent-foreground: 228 95% 80%;
516
+ --sidebar-border: 220 8% 18%;
517
+ --sidebar-ring: 228 95% 72%;
466
518
  --chart-1: 228 95% 67%;
467
519
  --chart-2: 168 96% 54%;
468
520
  --chart-3: 228 100% 82%;
@@ -737,11 +789,14 @@
737
789
  --duration-cinematic: 500ms;
738
790
 
739
791
  /* Font family — Geist primary stack with CJK fallbacks */
740
- --font-sans: "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
792
+ --font-sans:
793
+ var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
794
+ -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
741
795
  --font-cn: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif;
742
- --font-display: "Geist", "Noto Sans SC", sans-serif;
743
- --font-heading: "Geist", "Noto Sans SC", sans-serif;
744
- --font-mono: "Geist Mono", "Fira Code", ui-monospace, Consolas, "Courier New", monospace;
796
+ --font-display: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
797
+ --font-heading: var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif;
798
+ --font-mono:
799
+ var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
745
800
  }
746
801
 
747
802
  /* ============================================
@@ -749,15 +804,39 @@
749
804
  This file is concatenated into styles.generated.css after the @theme block.
750
805
  ============================================ */
751
806
 
752
- *,
753
- *::before,
754
- *::after {
755
- /* Token-aware default border — resolves via cascade:
756
- :root → light (hsl 240 5.9% 90% ≈ #e4e4e7)
757
- .dark → dark (hsl 0 0% 14.9% ≈ #262626)
758
- Previously hardcoded to the light value, which leaked a near-white border
759
- into every element in dark mode regardless of component. See ADR 2026-05-11. */
760
- border-color: hsl(var(--border));
807
+ /* ─── Edge tokens (2026-05 perf governance) ─────────────────
808
+ Pre-computed alpha values for borders / hairlines / outer glows.
809
+ Replaces ~70 runtime `color-mix(foreground, transparent N%)` calls per
810
+ page that re-evaluated on every paint. Mode-aware via CSS cascade.
811
+ Lives in static/ so it survives Style Dictionary rebuilds.
812
+ ─────────────────────────────────────────────────────────── */
813
+ :root {
814
+ --edge-faint: rgb(0 0 0 / 0.04);
815
+ --edge-soft: rgb(0 0 0 / 0.07);
816
+ --edge-medium: rgb(0 0 0 / 0.12);
817
+ --halo-faint: rgb(0 0 0 / 0.03);
818
+ }
819
+ .dark {
820
+ --edge-faint: rgb(255 255 255 / 0.04);
821
+ --edge-soft: rgb(255 255 255 / 0.06);
822
+ --edge-medium: rgb(255 255 255 / 0.1);
823
+ --halo-faint: rgb(255 255 255 / 0.03);
824
+ }
825
+
826
+ @layer base {
827
+ *,
828
+ *::before,
829
+ *::after {
830
+ /* Token-aware default border — resolves via cascade:
831
+ :root → light (hsl 240 5.9% 90% ≈ #e4e4e7)
832
+ .dark → dark (hsl 0 0% 14.9% ≈ #262626)
833
+ Previously hardcoded to the light value, which leaked a near-white border
834
+ into every element in dark mode regardless of component. See ADR 2026-05-11.
835
+
836
+ Keep this in Tailwind's base layer so component utilities such as
837
+ border-r-transparent can override it. */
838
+ border-color: hsl(var(--border));
839
+ }
761
840
  }
762
841
 
763
842
  body {
@@ -773,7 +852,9 @@ html {
773
852
  scroll-behavior: smooth;
774
853
  }
775
854
 
776
- :focus-visible {
855
+ :focus-visible:not(
856
+ :where(input, textarea, select, [cmdk-input], [role="textbox"], [contenteditable="true"])
857
+ ) {
777
858
  outline: 2px solid hsl(var(--ring) / 0.5);
778
859
  outline-offset: 2px;
779
860
  border-radius: inherit;
@@ -1373,10 +1454,13 @@ html {
1373
1454
  }
1374
1455
 
1375
1456
  /* ─── Reduced Motion ─────────────────────────────────────── */
1457
+ /* Honors OS preference EXCEPT for functional progress indicators
1458
+ * (spinners / pulses / pings). Per WCAG 2.3.3, those are excluded
1459
+ * from the "animation from interactions" reduction requirement. */
1376
1460
  @media (prefers-reduced-motion: reduce) {
1377
- *,
1378
- *::before,
1379
- *::after {
1461
+ *:not(.animate-spin):not(.animate-pulse):not(.animate-ping),
1462
+ *:not(.animate-spin):not(.animate-pulse):not(.animate-ping)::before,
1463
+ *:not(.animate-spin):not(.animate-pulse):not(.animate-ping)::after {
1380
1464
  animation-duration: 0.01ms;
1381
1465
  animation-iteration-count: 1;
1382
1466
  transition-duration: 0.01ms;
package/turbo.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": ["//"],
3
+ "tasks": {
4
+ "build": {
5
+ "dependsOn": ["@nebutra/design-tokens#build"],
6
+ "inputs": [
7
+ "$TURBO_EXTENDS$",
8
+ "../design-tokens/build/css/styles.generated.css",
9
+ "brands/**",
10
+ "scripts/**"
11
+ ],
12
+ "outputs": ["styles.css", "skins/**"]
13
+ }
14
+ }
15
+ }