@nebutra/design-tokens 2.0.0 → 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/build/ts/light.js CHANGED
@@ -24,17 +24,17 @@ export const ColorNebutraCyan700 = "#07a183";
24
24
  export const ColorNebutraCyan800 = "#057963";
25
25
  export const ColorNebutraCyan900 = "#035143";
26
26
  export const ColorNebutraCyan950 = "#012923";
27
- export const ColorNebutraNeutral50 = "#f8fafc";
28
- export const ColorNebutraNeutral100 = "#f1f5f9";
29
- export const ColorNebutraNeutral200 = "#e2e8f0";
30
- export const ColorNebutraNeutral300 = "#cbd5e1";
31
- export const ColorNebutraNeutral400 = "#94a3b8";
32
- export const ColorNebutraNeutral500 = "#64748b";
33
- export const ColorNebutraNeutral600 = "#475569";
34
- export const ColorNebutraNeutral700 = "#334155";
35
- export const ColorNebutraNeutral800 = "#1e293b";
36
- export const ColorNebutraNeutral900 = "#0f172a";
37
- export const ColorNebutraNeutral950 = "#020617";
27
+ export const ColorNebutraNeutral50 = "#f9fafb";
28
+ export const ColorNebutraNeutral100 = "#f3f4f6";
29
+ export const ColorNebutraNeutral200 = "#e4e5e8";
30
+ export const ColorNebutraNeutral300 = "#d2d4d8";
31
+ export const ColorNebutraNeutral400 = "#9fa1a5";
32
+ export const ColorNebutraNeutral500 = "#717377";
33
+ export const ColorNebutraNeutral600 = "#515357";
34
+ export const ColorNebutraNeutral700 = "#3f4044";
35
+ export const ColorNebutraNeutral800 = "#26272a";
36
+ export const ColorNebutraNeutral900 = "#18191c";
37
+ export const ColorNebutraNeutral950 = "#0b0c0d";
38
38
  export const ColorNebutraGray50 = "#fafafa";
39
39
  export const ColorNebutraGray100 = "#f5f5f5";
40
40
  export const ColorNebutraGray200 = "#e5e5e5";
@@ -82,18 +82,89 @@ export const EasingSpring = "cubic-bezier(0.175, 0.885, 0.32, 1.275)";
82
82
  export const TrackingDisplay = "-0.04em"; // Hero H1
83
83
  export const TrackingHeading = "-0.03em"; // Section H2/H3
84
84
  export const TrackingTight = "-0.015em"; // Body emphasis
85
+ export const TrackingLabel = "0.07em"; // Uppercase micro-labels. 0.07em is Vercel's measured eyebrow value (0.071em); open tracking keeps small caps legible.
85
86
  export const LeadingDisplay = "1.05";
86
87
  export const LeadingHeading = "1.15";
88
+ export const Type2xsSize = "0.6875rem";
89
+ export const Type2xsLeading = "1.45";
90
+ export const Type2xsTracking = "0.01em";
91
+ export const TypeXsSize = "0.75rem";
92
+ export const TypeXsLeading = "1.4";
93
+ export const TypeXsTracking = "0.005em";
94
+ export const TypeUiSize = "0.8125rem";
95
+ export const TypeUiLeading = "1.45";
96
+ export const TypeUiTracking = "0em";
97
+ export const TypeSmSize = "0.875rem";
98
+ export const TypeSmLeading = "1.5";
99
+ export const TypeSmTracking = "0em";
100
+ export const TypeBaseSize = "1rem";
101
+ export const TypeBaseLeading = "1.6";
102
+ export const TypeBaseTracking = "-0.005em";
103
+ export const TypeLgSize = "1.125rem";
104
+ export const TypeLgLeading = "1.55";
105
+ export const TypeLgTracking = "-0.01em";
106
+ export const TypeXlSize = "1.25rem";
107
+ export const TypeXlLeading = "1.45";
108
+ export const TypeXlTracking = "-0.012em";
109
+ export const Type2xlSize = "1.5rem";
110
+ export const Type2xlLeading = "1.33";
111
+ export const Type2xlTracking = "-0.015em";
112
+ export const Type3xlSize = "1.875rem";
113
+ export const Type3xlLeading = "1.25";
114
+ export const Type3xlTracking = "-0.02em";
115
+ export const Type4xlSize = "2.25rem";
116
+ export const Type4xlLeading = "1.15";
117
+ export const Type4xlTracking = "-0.022em";
118
+ export const Type5xlSize = "3rem";
119
+ export const Type5xlLeading = "1.08";
120
+ export const Type5xlTracking = "-0.028em";
121
+ export const Type6xlSize = "3.75rem";
122
+ export const Type6xlLeading = "1.04";
123
+ export const Type6xlTracking = "-0.032em";
124
+ export const Type7xlSize = "4.5rem";
125
+ export const Type7xlLeading = "1";
126
+ export const Type7xlTracking = "-0.035em";
127
+ export const Type8xlSize = "6rem";
128
+ export const Type8xlLeading = "1";
129
+ export const Type8xlTracking = "-0.04em";
130
+ export const Type9xlSize = "8rem";
131
+ export const Type9xlLeading = "1";
132
+ export const Type9xlTracking = "-0.045em";
133
+ export const TypeDisplaySize = "1.25rem";
134
+ export const TypeDisplayLeading = "1.45";
135
+ export const TypeDisplayTracking = "-0.012em";
136
+ export const TypeBodySize = "0.875rem";
137
+ export const TypeBodyLeading = "1.5";
138
+ export const TypeBodyTracking = "0em";
139
+ export const TypeLabelSize = "0.75rem";
140
+ export const TypeLabelLeading = "1.4";
141
+ export const TypeLabelTracking = "0.005em";
142
+ export const TypeMetaSize = "0.6875rem";
143
+ export const TypeMetaLeading = "1.45";
144
+ export const TypeMetaTracking = "0.01em";
145
+ export const LayerRaised = "10"; // Within a component: an element lifted over its siblings.
146
+ export const LayerSticky = "20"; // Sticky headers and table heads.
147
+ export const LayerFloating = "30"; // Floating toolbars and canvas chrome anchored to content.
148
+ export const LayerNav = "40"; // App navigation and the shell's top bar.
149
+ export const LayerDrawer = "60"; // Mobile navigation drawers and their toggles.
150
+ export const LayerBanner = "90"; // Page-level banners: cookie consent, announcements.
151
+ export const LayerSkipLink = "100"; // The skip link — above page chrome whenever it is focused.
152
+ export const LayerPanel = "200"; // App-owned floating panels that are not overlays (locale switcher, search flyouts).
153
+ export const LayerBackdrop = "1040"; // Overlay tier. Mirrors overlayZIndex in @nebutra/ui; a test keeps them equal.
154
+ export const LayerModal = "1050"; // Dialogs and sheets.
155
+ export const LayerPopover = "1060"; // Popovers, menus, selects.
156
+ export const LayerTooltip = "1070"; // Tooltips.
157
+ export const LayerDevtools = "9999"; // Developer and debug surfaces only — never product UI.
87
158
  export const RingHairline = "inset 0 0 0 1px rgb(255 255 255 / 0.04)"; // Etched-glass hairline (light mode value, dark mode override in themes/dark.json)
88
159
  export const OverlayFaint = "rgb(0 0 0 / 0.06)"; // Light-mode faint overlay (6% black)
89
160
  export const FontFamilySans =
90
- 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif';
161
+ 'var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif';
91
162
  export const FontFamilyCn =
92
- 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", sans-serif'; // CJK-locale stack — same order as sans, minus the Latin-only system faces
163
+ 'var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", sans-serif'; // CJK-locale stack — same order as sans, minus the Latin-only system faces
93
164
  export const FontFamilyDisplay =
94
- 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif';
165
+ 'var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif';
95
166
  export const FontFamilyHeading =
96
- 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif';
167
+ 'var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif';
97
168
  export const FontFamilyMono =
98
169
  'var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace';
99
170
  export const TransitionDefault = {
@@ -128,17 +199,17 @@ export const MotionDurationReveal = "300ms";
128
199
  export const MotionDurationCinematic = "500ms";
129
200
  export const FocusRingDefault = "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.
130
201
  export const ScaleNeutral1 = "#ffffff"; // app background
131
- export const ScaleNeutral2 = "#f8fafc"; // subtle background
132
- export const ScaleNeutral3 = "#f1f5f9"; // component bg default
133
- export const ScaleNeutral4 = "#e2e8f0"; // component bg hover
134
- export const ScaleNeutral5 = "#cbd5e1"; // component bg active
135
- export const ScaleNeutral6 = "#b0bccb"; // 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.
136
- export const ScaleNeutral7 = "#96a3b5"; // default border — DERIVED: OKLab lerp step5 → step9 at t=0.50.
137
- export const ScaleNeutral8 = "#7d8ba0"; // hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75.
138
- export const ScaleNeutral9 = "#64748b"; // solid fill
139
- export const ScaleNeutral10 = "#475569"; // solid fill hover
140
- export const ScaleNeutral11 = "#334155"; // secondary text
141
- export const ScaleNeutral12 = "#0f172a"; // primary text
202
+ export const ScaleNeutral2 = "#f9fafb"; // subtle background
203
+ export const ScaleNeutral3 = "#f3f4f6"; // component bg default
204
+ export const ScaleNeutral4 = "#e4e5e8"; // component bg hover
205
+ export const ScaleNeutral5 = "#d2d4d8"; // component bg active
206
+ export const ScaleNeutral6 = "#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.
207
+ export const ScaleNeutral7 = "#a0a2a6"; // default border — DERIVED: OKLab lerp step5 → step9 at t=0.50.
208
+ export const ScaleNeutral8 = "#888a8e"; // hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75.
209
+ export const ScaleNeutral9 = "#717377"; // solid fill
210
+ export const ScaleNeutral10 = "#515357"; // solid fill hover
211
+ export const ScaleNeutral11 = "#3f4044"; // secondary text
212
+ export const ScaleNeutral12 = "#18191c"; // primary text
142
213
  export const ScaleBlue1 = "#fafcfe"; // app tint — L* 99.01, C 0.003, hue 264. Radix blue-1 sits at L* 99.31 C 0.003.
143
214
  export const ScaleBlue2 = "#f2f6fd"; // subtle background — L* 97.21, C 0.010.
144
215
  export const ScaleBlue3 = "#e7eefc"; // component background default — L* 94.79, C 0.020.
@@ -165,20 +236,20 @@ export const ScaleCyan10 = "#33e5bb"; // solid fill hover — L* 82.61, C 0.150.
165
236
  export const ScaleCyan11 = "#027a61"; // secondary text — L* 51.64, C 0.100. 5.17:1 on step 1.
166
237
  export const ScaleCyan12 = "#004133"; // primary text — L* 33.45, C 0.065. 11.35:1 on step 1.
167
238
  export const ScaleCyanContrast = "#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.
168
- export const ShadcnBackground = "0 0% 100%"; // HSL channel triple — consume via hsl(var(--background))
169
- export const ShadcnForeground = "222 47% 11%";
239
+ export const ShadcnBackground = "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).
240
+ export const ShadcnForeground = "225 7.7% 10.2%";
170
241
  export const ShadcnCard = "0 0% 100%";
171
- export const ShadcnCardForeground = "222 47% 11%";
242
+ export const ShadcnCardForeground = "225 7.7% 10.2%";
172
243
  export const ShadcnPopover = "0 0% 100%";
173
- export const ShadcnPopoverForeground = "222 47% 11%";
174
- export const ShadcnPrimary = "222.8 85% 55.7%"; // Product action blue. Derived, not borrowed: hue 264 is the VI hue, L* and C were chosen inside the band the industry actually ships (measured OKLCH C 0.167-0.235, L* 54-67 across Vercel, Stripe, Linear, GitHub, Tailwind, Radix, Figma, Notion). #2e65ee = L*55.5 C0.215, white label 4.99:1. The previous 228 85% 56% (#2f56ee, C0.232) also passed at 5.70:1 — this trims it to the middle of the band rather than its top edge. The VI lock --blue-9 (#0033FE, C0.290) is 23% above anything in that band and must never surface on a component.
244
+ export const ShadcnPopoverForeground = "225 7.7% 10.2%";
245
+ export const ShadcnPrimary = "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.
175
246
  export const ShadcnPrimaryForeground = "0 0% 100%";
176
- export const ShadcnSecondary = "210 40% 96%";
177
- export const ShadcnSecondaryForeground = "222 47% 11%";
178
- export const ShadcnMuted = "210 40% 96%";
179
- export const ShadcnMutedForeground = "215 25% 45%";
180
- export const ShadcnAccent = "228 100% 97%";
181
- export const ShadcnAccentForeground = "228 85% 56%"; // Softer brand blue — desaturated to match --primary, avoids 100% saturation glare on focus/accent
247
+ export const ShadcnSecondary = "220 14.3% 95.9%";
248
+ export const ShadcnSecondaryForeground = "225 7.7% 10.2%";
249
+ export const ShadcnMuted = "220 14.3% 95.9%";
250
+ export const ShadcnMutedForeground = "220 2.6% 45.5%";
251
+ export const ShadcnAccent = "220 14.3% 95.9%";
252
+ export const ShadcnAccentForeground = "225 7.7% 10.2%"; // Softer brand blue — desaturated to match --primary, avoids 100% saturation glare on focus/accent
182
253
  export const ShadcnDestructive = "0 84% 45%";
183
254
  export const ShadcnDestructiveForeground = "0 0% 100%";
184
255
  export const ShadcnDestructiveStrong = "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.
@@ -190,17 +261,17 @@ export const ShadcnWarningForeground = "222 47% 11%";
190
261
  export const ShadcnWarningStrong = "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.
191
262
  export const ShadcnInfo = "228 85% 56%";
192
263
  export const ShadcnInfoForeground = "0 0% 100%";
193
- export const ShadcnBorder = "240 5.9% 90%";
194
- export const ShadcnInput = "240 5.9% 90%";
195
- export const ShadcnRing = "222 47% 11%"; // Focus ring — neutral foreground (Vercel/Linear/Cursor 2026 pattern). Brand blue is reserved for selected/active states; focus indication is chrome and stays neutral. Consumed by global :focus-visible outline as hsl(var(--ring) / 0.5).
196
- export const ShadcnSidebar = "210 40% 98%";
197
- export const ShadcnSidebarForeground = "215 25% 27%";
198
- export const ShadcnSidebarPrimary = "228 85% 56%";
264
+ export const ShadcnBorder = "225 8% 90.2%";
265
+ export const ShadcnInput = "225 8% 90.2%";
266
+ export const ShadcnRing = "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.
267
+ export const ShadcnSidebar = "210 20% 98%";
268
+ export const ShadcnSidebarForeground = "225 7.7% 10.2%";
269
+ export const ShadcnSidebarPrimary = "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.
199
270
  export const ShadcnSidebarPrimaryForeground = "0 0% 100%";
200
- export const ShadcnSidebarAccent = "228 100% 97%";
201
- export const ShadcnSidebarAccentForeground = "228 85% 56%";
202
- export const ShadcnSidebarBorder = "214 32% 91%";
203
- export const ShadcnSidebarRing = "228 85% 56%";
271
+ export const ShadcnSidebarAccent = "220 14.3% 95.9%";
272
+ export const ShadcnSidebarAccentForeground = "225 7.7% 10.2%";
273
+ export const ShadcnSidebarBorder = "225 8% 90.2%";
274
+ export const ShadcnSidebarRing = "222.8 85% 55.7%";
204
275
  export const ShadcnChart1 = "228 100% 50%";
205
276
  export const ShadcnChart2 = "168 91% 49%";
206
277
  export const ShadcnChart3 = "263 70% 66%";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/design-tokens",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "W3C DTCG ($value/$type) tokens — SSOT, Style Dictionary 4 pipeline. Coexists with @nebutra/tokens; not a replacement.",
5
5
  "private": false,
6
6
  "nebutra": {
@@ -57,7 +57,8 @@ const FONT_REGISTRY = {
57
57
  geist: "--font-geist-sans",
58
58
  "geist sans": "--font-geist-sans",
59
59
  "geist mono": "--font-geist-mono",
60
- "vivo sans sc": "--font-noto-sans-sc",
60
+ misans: "--font-misans",
61
+ "dm sans display": "--font-dm-sans",
61
62
  inter: "--font-inter",
62
63
  "space grotesk": "--font-space-grotesk",
63
64
  "playfair display": "--font-playfair-display",
@@ -688,6 +689,12 @@ function buildTailwindThemeInline() {
688
689
  --color-success-foreground: hsl(var(--success-foreground));
689
690
  --color-warning: hsl(var(--warning));
690
691
  --color-warning-foreground: hsl(var(--warning-foreground));
692
+ /* Status colours as INK (text, icon fill/stroke). --destructive / --warning /
693
+ --success are fills: amber is 2.0:1 as text in light, red 2.5:1 in dark.
694
+ text-destructive-strong etc. read at ~5:1 in both themes. */
695
+ --color-destructive-strong: hsl(var(--destructive-strong));
696
+ --color-warning-strong: hsl(var(--warning-strong));
697
+ --color-success-strong: hsl(var(--success-strong));
691
698
  --color-info: hsl(var(--info));
692
699
  --color-info-foreground: hsl(var(--info-foreground));
693
700
  --color-border: hsl(var(--border));
@@ -862,6 +869,66 @@ function buildTailwindThemeInline() {
862
869
  --ease-in-out: var(--motion-ease-in-out);
863
870
  --ease-spring: var(--motion-ease-spring);
864
871
 
872
+ /* Type scale — see core.json:type. Indirect so a Brand Package can retune size,
873
+ leading and tracking; each text-* utility sets all three. */
874
+ --text-2xs: var(--type-2xs-size);
875
+ --text-2xs--line-height: var(--type-2xs-leading);
876
+ --text-2xs--letter-spacing: var(--type-2xs-tracking);
877
+ --text-xs: var(--type-xs-size);
878
+ --text-xs--line-height: var(--type-xs-leading);
879
+ --text-xs--letter-spacing: var(--type-xs-tracking);
880
+ --text-ui: var(--type-ui-size);
881
+ --text-ui--line-height: var(--type-ui-leading);
882
+ --text-ui--letter-spacing: var(--type-ui-tracking);
883
+ --text-sm: var(--type-sm-size);
884
+ --text-sm--line-height: var(--type-sm-leading);
885
+ --text-sm--letter-spacing: var(--type-sm-tracking);
886
+ --text-base: var(--type-base-size);
887
+ --text-base--line-height: var(--type-base-leading);
888
+ --text-base--letter-spacing: var(--type-base-tracking);
889
+ --text-lg: var(--type-lg-size);
890
+ --text-lg--line-height: var(--type-lg-leading);
891
+ --text-lg--letter-spacing: var(--type-lg-tracking);
892
+ --text-xl: var(--type-xl-size);
893
+ --text-xl--line-height: var(--type-xl-leading);
894
+ --text-xl--letter-spacing: var(--type-xl-tracking);
895
+ --text-2xl: var(--type-2xl-size);
896
+ --text-2xl--line-height: var(--type-2xl-leading);
897
+ --text-2xl--letter-spacing: var(--type-2xl-tracking);
898
+ --text-3xl: var(--type-3xl-size);
899
+ --text-3xl--line-height: var(--type-3xl-leading);
900
+ --text-3xl--letter-spacing: var(--type-3xl-tracking);
901
+ --text-4xl: var(--type-4xl-size);
902
+ --text-4xl--line-height: var(--type-4xl-leading);
903
+ --text-4xl--letter-spacing: var(--type-4xl-tracking);
904
+ --text-5xl: var(--type-5xl-size);
905
+ --text-5xl--line-height: var(--type-5xl-leading);
906
+ --text-5xl--letter-spacing: var(--type-5xl-tracking);
907
+ --text-6xl: var(--type-6xl-size);
908
+ --text-6xl--line-height: var(--type-6xl-leading);
909
+ --text-6xl--letter-spacing: var(--type-6xl-tracking);
910
+ --text-7xl: var(--type-7xl-size);
911
+ --text-7xl--line-height: var(--type-7xl-leading);
912
+ --text-7xl--letter-spacing: var(--type-7xl-tracking);
913
+ --text-8xl: var(--type-8xl-size);
914
+ --text-8xl--line-height: var(--type-8xl-leading);
915
+ --text-8xl--letter-spacing: var(--type-8xl-tracking);
916
+ --text-9xl: var(--type-9xl-size);
917
+ --text-9xl--line-height: var(--type-9xl-leading);
918
+ --text-9xl--letter-spacing: var(--type-9xl-tracking);
919
+ --text-display: var(--type-display-size);
920
+ --text-display--line-height: var(--type-display-leading);
921
+ --text-display--letter-spacing: var(--type-display-tracking);
922
+ --text-body: var(--type-body-size);
923
+ --text-body--line-height: var(--type-body-leading);
924
+ --text-body--letter-spacing: var(--type-body-tracking);
925
+ --text-label: var(--type-label-size);
926
+ --text-label--line-height: var(--type-label-leading);
927
+ --text-label--letter-spacing: var(--type-label-tracking);
928
+ --text-meta: var(--type-meta-size);
929
+ --text-meta--line-height: var(--type-meta-leading);
930
+ --text-meta--letter-spacing: var(--type-meta-tracking);
931
+
865
932
  /* Motion — Durations (four-rail; see core.json:duration). Names denote intent, not relative speed. */
866
933
  --duration-micro: var(--motion-duration-micro);
867
934
  --duration-flow: var(--motion-duration-flow);
@@ -893,14 +960,15 @@ function buildTailwindThemeInline() {
893
960
  --spacing-{step} alias. --spacing itself (0.25rem arithmetic base) stays
894
961
  untouched. See static/base.css. */
895
962
 
896
- /* Font family — Geist keeps Latin + numerals, self-hosted Noto Sans SC takes
897
- CJK, system CJK faces behind it. Order is the design decision; see the
898
- fontFamily $description in tokens/core.json. Mirror of those tokens — keep
899
- the two in sync (verify:parity compares them). */
900
- --font-sans: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
901
- --font-cn: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", "Microsoft YaHei", sans-serif;
902
- --font-display: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif;
903
- --font-heading: var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif;
963
+ /* Font family — Geist keeps Latin + numerals for body/UI, DM Sans sets Latin
964
+ display and headings, self-hosted MiSans takes CJK, system CJK faces behind
965
+ it. Order is the design decision; see the fontFamily $description in
966
+ tokens/core.json. Mirror of those tokens — keep the two in sync
967
+ (verify:parity compares them). */
968
+ --font-sans: var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
969
+ --font-cn: var(--font-geist-sans), "Geist", var(--font-misans, "MiSans"), "PingFang SC", "Microsoft YaHei", sans-serif;
970
+ --font-display: var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
971
+ --font-heading: var(--font-dm-sans), "DM Sans", var(--font-misans, "MiSans"), "PingFang SC", sans-serif;
904
972
  --font-mono: var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
905
973
  }
906
974
  `;