@nebutra/design-tokens 0.1.2 → 2.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/dark.js CHANGED
@@ -59,23 +59,24 @@ export const SizeBreakpointXs = "26.25rem"; // 420px — narrow-phone grid flip
59
59
  export const SizeBreakpointTight = "67.5rem"; // 1080px — dense dashboard header horizontal
60
60
  export const SizeBreakpointShell = "73.75rem"; // 1180px — three-pane shell threshold
61
61
  export const SizeBreakpoint3xl = "112.5rem"; // 1800px — 4K / Studio Display friendliness
62
- export const SizeRadiusNone = "0";
63
- export const SizeRadiusSm = "0.25rem";
64
- export const SizeRadiusMd = "0.375rem";
65
- export const SizeRadiusLg = "0.5rem";
66
- export const SizeRadiusXl = "0.75rem";
67
- export const SizeRadius2xl = "1rem";
68
- export const SizeRadius3xl = "1.5rem";
69
- export const SizeRadiusFull = "9999px";
70
- export const SizeRadiusButton = "0.5rem"; // 2026 button radius — Vercel/Linear/Cursor consensus
71
- export const SizeRadiusCard = "0.75rem"; // 2026 card radius
72
- export const SizeRadiusPanel = "1rem"; // 2026 bento/hero panel radius
62
+ export const SizeRadiusNone = "0"; // Job: flush. Edge-to-edge surfaces — a full-bleed table row, a segment welded to its neighbour, an inset that must not round twice.
63
+ export const SizeRadiusXs = "0.125rem"; // Job: swatch. The smallest corner that still reads as intentional at 8-12px: chart legend keys, mark highlights, progress and sparkline bar caps. Added because 18 call sites had hand-written 1px, 2px and 3px corners with no step to reach for. Matches the bottom of the Tailwind scale, so rounded-xs and var(--radius-xs) agree.
64
+ export const SizeRadiusSm = "0.25rem"; // Job: inner. The corner of something nested inside a control — a thumbnail inside a chip, a swatch inside a tile, a code span inside a paragraph. One step inside md.
65
+ export const SizeRadiusMd = "0.375rem"; // Job: control. The default for anything you can click or type into at text height: input, select trigger, small button, menu item, badge with square corners. The most-used step on the ladder.
66
+ export const SizeRadiusLg = "0.5rem"; // Job: container. Things that hold controls rather than being one: popover, dropdown surface, tooltip, toolbar, inline code block. Aliased as radius-button.
67
+ export const SizeRadiusXl = "0.75rem"; // Job: card. A discrete surface in a list or grid that carries its own padding and elevation. Aliased as radius-card.
68
+ export const SizeRadius2xl = "1rem"; // Job: panel. A region that holds cards — bento cell, dialog, sheet, dashboard section. Aliased as radius-panel.
69
+ export const SizeRadius3xl = "1.5rem"; // Job: frame. The outermost shell of a marketing or docs composition — mockup window, hero frame, screenshot chrome. The top of the ladder.
70
+ export const SizeRadiusFull = "9999px"; // Job: pill. Fully round by intent, not by size: avatar, dot, toggle track, tag. Kept as one step, not a pair, so there is exactly one way to say round.
71
+ export const SizeRadiusButton = "0.5rem"; // Intent alias for the container step. Reach for this in product code instead of lg so the shape can be re-tuned in one place.
72
+ export const SizeRadiusCard = "0.75rem"; // Intent alias for the card step. Reach for this in product code instead of xl.
73
+ export const SizeRadiusPanel = "1rem"; // Intent alias for the panel step. Reach for this in product code instead of 2xl.
73
74
  export const DurationMicro = "100ms"; // Micro-feedback: hover, focus, toggle, button press
74
75
  export const DurationFlow = "200ms"; // State flow: modal open, dropdown reveal, tab switch, default page transition
75
76
  export const DurationReveal = "300ms"; // Content unveil: slide-in, expand, accordion, drawer
76
77
  export const DurationCinematic = "500ms"; // Hero-grade entrance: landing reveal, large delight moments
77
78
  export const EasingIn = "cubic-bezier(0.4, 0, 1, 1)";
78
- export const EasingOut = "cubic-bezier(0, 0, 0.2, 1)";
79
+ export const EasingOut = "cubic-bezier(0.16, 1, 0.3, 1)";
79
80
  export const EasingInOut = "cubic-bezier(0.4, 0, 0.2, 1)";
80
81
  export const EasingSpring = "cubic-bezier(0.175, 0.885, 0.32, 1.275)";
81
82
  export const TrackingDisplay = "-0.04em"; // Hero H1
@@ -86,13 +87,13 @@ export const LeadingHeading = "1.15";
86
87
  export const RingHairline = "inset 0 0 0 1px rgb(255 255 255 / 0.06)"; // Hairline override — more visible on dark background
87
88
  export const OverlayFaint = "rgb(255 255 255 / 0.06)"; // Dark-mode faint overlay (6% white) — inverted from light
88
89
  export const FontFamilySans =
89
- 'var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif';
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';
90
91
  export const FontFamilyCn =
91
- '"Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif';
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
92
93
  export const FontFamilyDisplay =
93
- 'var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif';
94
+ 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif';
94
95
  export const FontFamilyHeading =
95
- 'var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif';
96
+ 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif';
96
97
  export const FontFamilyMono =
97
98
  'var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace';
98
99
  export const TransitionDefault = {
@@ -100,10 +101,8 @@ export const TransitionDefault = {
100
101
  timingFunction: "ease-out",
101
102
  delay: "0ms",
102
103
  }; // Default state-flow transition — 200ms ease-out (matches 200ms). Used by --transition shorthand.
103
- export const TransitionDuration = "200ms"; // Default transition duration — 200ms flow rail.
104
- export const TransitionEasing = "ease-out"; // ease-out keyword form (CSS-friendly). Equivalent to cubic-bezier(0,0,0.2,1).
105
- export const BrandPrimary = "#5c7cfa"; // Dark-mode brand primary — visible against dark surfaces (uses --blue-9 = nebutra-blue-400).
106
- export const BrandAccent = "#1af7c8"; // Dark-mode brand accent — visible against dark surfaces (uses --cyan-9 = nebutra-cyan-400).
104
+ export const BrandPrimary = "#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.
105
+ export const BrandAccent = "#1af7c8"; // Dark-mode brand accent — nebutra-cyan-400 (L* 87.1). The functional bright fill is --cyan-9 (#0bf1c3, L* 85.41), the same hex in both modes; dark ink for it is --cyan-contrast at 10.35:1.
107
106
  export const BrandTertiary = "#8b5cf6";
108
107
  export const BrandGradientStart = "hsl(var(--primary))"; // Legacy alias of --primary (shadcn product blue). Prefer bg-primary / Button default.
109
108
  export const BrandGradientEnd = "hsl(var(--primary))"; // Legacy alias of --primary. Prefer hover:bg-primary/90.
@@ -133,45 +132,47 @@ export const ScaleNeutral2 = "#0f172a";
133
132
  export const ScaleNeutral3 = "#1e293b";
134
133
  export const ScaleNeutral4 = "#334155";
135
134
  export const ScaleNeutral5 = "#475569";
136
- export const ScaleNeutral6 = "#171717";
137
- export const ScaleNeutral7 = "#262626";
138
- export const ScaleNeutral8 = "#404040";
135
+ export const ScaleNeutral6 = "#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.
136
+ export const ScaleNeutral7 = "#7a7a7a"; // default border — DERIVED: OKLab lerp step5 → step9 at t=0.50, chroma 0.
137
+ export const ScaleNeutral8 = "#8d8d8d"; // hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75, chroma 0.
139
138
  export const ScaleNeutral9 = "#94a3b8";
140
139
  export const ScaleNeutral10 = "#cbd5e1";
141
140
  export const ScaleNeutral11 = "#e2e8f0";
142
141
  export const ScaleNeutral12 = "#f8fafc";
143
- export const ScaleBlue1 = "#000830";
144
- export const ScaleBlue2 = "#000f59";
145
- export const ScaleBlue3 = "#001882";
146
- export const ScaleBlue4 = "#0021ab";
147
- export const ScaleBlue5 = "#002ad4";
148
- export const ScaleBlue6 = "#001882";
149
- export const ScaleBlue7 = "#0021ab";
150
- export const ScaleBlue8 = "#002ad4";
151
- export const ScaleBlue9 = "#5c7cfa";
152
- export const ScaleBlue10 = "#91a7ff";
153
- export const ScaleBlue11 = "#bac8ff";
154
- export const ScaleBlue12 = "#dbe4ff";
155
- export const ScaleCyan1 = "#012923";
156
- export const ScaleCyan2 = "#035143";
157
- export const ScaleCyan3 = "#057963";
158
- export const ScaleCyan4 = "#07a183";
159
- export const ScaleCyan5 = "#09c9a3";
160
- export const ScaleCyan6 = "#057963";
161
- export const ScaleCyan7 = "#07a183";
162
- export const ScaleCyan8 = "#09c9a3";
163
- export const ScaleCyan9 = "#1af7c8";
164
- export const ScaleCyan10 = "#4dfcd4";
165
- export const ScaleCyan11 = "#80ffe0";
166
- export const ScaleCyan12 = "#b3ffec";
142
+ export const ScaleBlue1 = "#030815"; // app tint — L* 13.59, C 0.031, hue 264.
143
+ export const ScaleBlue2 = "#060f25"; // subtle background — L* 17.43, C 0.048.
144
+ export const ScaleBlue3 = "#09183b"; // component background default — L* 22.04, C 0.071.
145
+ export const ScaleBlue4 = "#0d2254"; // component background hover — L* 26.99, C 0.095.
146
+ export const ScaleBlue5 = "#132e6f"; // component background active — L* 32.46, C 0.118. Anchor for the derived border tier.
147
+ export const ScaleBlue6 = "#1c3c8a"; // subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 23.5 L*) at t=0.25. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 38.25, C 0.136.
148
+ export const ScaleBlue7 = "#254ba7"; // default border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 23.5 L*) at t=0.50. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 44.21, C 0.155.
149
+ export const ScaleBlue8 = "#2f5ac4"; // hovered border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 23.5 L*) at t=0.75. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 50.01, C 0.173.
150
+ export const ScaleBlue9 = "#396ae2"; // solid fill — L* 55.92, C 0.191, hue 264.3. White label 4.85:1. Same value as the dark shadcn --primary, so light and dark both take their deep fill from one source. Chroma ceiling for dark is 0.190; this is 0.191.
151
+ export const ScaleBlue10 = "#3f70e3"; // solid fill hover — L* 57.44, C 0.183. White label 4.53:1. The hover has to move UP in a dark ramp, which costs contrast, so the step is deliberately small (1.52 L*) to keep white above 4.5:1.
152
+ export const ScaleBlue11 = "#89aaee"; // secondary text — L* 74.01, C 0.106. 8.62:1 on step 1.
153
+ export const ScaleBlue12 = "#d2e0fd"; // primary text — L* 90.52, C 0.043. 15.08:1 on step 1.
154
+ export const ScaleBlueContrast = "#ffffff"; // Label ink for the solid fill tier. Absolute value, identical in both modes. 4.85:1 on step 9, 4.53:1 on step 10.
155
+ export const ScaleCyan1 = "#04100c"; // app tint — L* 15.90, C 0.021, hue 172.
156
+ export const ScaleCyan2 = "#091e18"; // subtle background — L* 21.54, C 0.030.
157
+ export const ScaleCyan3 = "#0f2e25"; // component background default — L* 27.46, C 0.041.
158
+ export const ScaleCyan4 = "#1c4035"; // component background hover — L* 34.10, C 0.046.
159
+ export const ScaleCyan5 = "#2b5347"; // component background active — L* 40.88, C 0.050. Anchor for the derived border tier.
160
+ export const ScaleCyan6 = "#307864"; // subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (span 44.5 L*) at t=0.25. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 52.12, C 0.079.
161
+ export const ScaleCyan7 = "#309e82"; // default border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (span 44.5 L*) at t=0.50. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 63.04, C 0.107.
162
+ export const ScaleCyan8 = "#28c7a2"; // hovered border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (span 44.5 L*) at t=0.75. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 74.30, C 0.136.
163
+ export const ScaleCyan9 = "#0bf1c3"; // solid fill — same bright value as light mode, the way Radix ships one hex for mint-9 in both modes: L* 85.41, C 0.164. Ink 10.35:1 against --cyan-contrast.
164
+ export const ScaleCyan10 = "#5af8ce"; // solid fill hover — L* 88.53, C 0.145, 3.12 L* above step 9 (Radix mint dark moves up 4.60 L*). Ink 11.34:1.
165
+ export const ScaleCyan11 = "#90fbdb"; // secondary text — L* 91.46, C 0.110. 15.68:1 on step 1.
166
+ export const ScaleCyan12 = "#bdfde8"; // primary text — L* 94.54, C 0.070. 16.95:1 on step 1.
167
+ export const ScaleCyanContrast = "#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.
167
168
  export const ShadcnBackground = "222 14% 9%";
168
169
  export const ShadcnForeground = "210 18% 96%";
169
170
  export const ShadcnCard = "222 12% 13%";
170
171
  export const ShadcnCardForeground = "210 18% 96%";
171
172
  export const ShadcnPopover = "222 12% 13%";
172
173
  export const ShadcnPopoverForeground = "210 18% 96%";
173
- export const ShadcnPrimary = "228 90% 72%";
174
- export const ShadcnPrimaryForeground = "222 14% 9%";
174
+ export const ShadcnPrimary = "222.6 74.4% 55.5%"; // Dark-mode action blue, paired with the near-black --primary-foreground, not white. #396ae2 keeps the VI hue and sits at L*55.9 C0.191.
175
+ export const ShadcnPrimaryForeground = "0 0% 100%"; // 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.
175
176
  export const ShadcnSecondary = "222 10% 18%";
176
177
  export const ShadcnSecondaryForeground = "210 18% 96%";
177
178
  export const ShadcnMuted = "222 10% 17%";
@@ -180,10 +181,13 @@ export const ShadcnAccent = "228 38% 22%";
180
181
  export const ShadcnAccentForeground = "228 95% 80%";
181
182
  export const ShadcnDestructive = "0 63% 38%";
182
183
  export const ShadcnDestructiveForeground = "210 18% 96%";
184
+ export const ShadcnDestructiveStrong = "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.
183
185
  export const ShadcnSuccess = "142 60% 42%";
184
186
  export const ShadcnSuccessForeground = "222 14% 9%";
187
+ export const ShadcnSuccessStrong = "142 60% 42%"; // Green as foreground on a dark surface. Needs no adjustment — 142 60% 42% is already 4.83:1 as ink on its own 10% tint — so it deliberately equals --success, matching how --warning-strong behaves in this theme.
185
188
  export const ShadcnWarning = "38 80% 52%";
186
189
  export const ShadcnWarningForeground = "222 14% 9%";
190
+ export const ShadcnWarningStrong = "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.
187
191
  export const ShadcnInfo = "228 95% 72%";
188
192
  export const ShadcnInfoForeground = "222 14% 9%";
189
193
  export const ShadcnBorder = "220 8% 22%";
@@ -248,3 +252,25 @@ export const ElevationBrand =
248
252
  "0 0 0 1px rgb(92 124 250 / 0.25), 0 4px 20px -2px rgb(92 124 250 / 0.3)";
249
253
  export const ElevationBrandLg =
250
254
  "0 0 0 1px rgb(92 124 250 / 0.3), 0 8px 40px -4px rgb(92 124 250 / 0.4)";
255
+ export const ElevationAmbientSm =
256
+ "0 1px 2px -1px rgb(0 0 0 / 0.4), 0 6px 16px -6px rgb(0 0 0 / 0.5)"; // Ambient elevation, small. Contact layer plus a wide soft pool — the marketing-surface counterpart to the tight xs..2xl product ramp.
257
+ export const ElevationAmbientMd =
258
+ "0 2px 4px -2px rgb(0 0 0 / 0.4), 0 12px 32px -10px rgb(0 0 0 / 0.55)"; // Ambient elevation, medium. Default resting depth for a floating marketing card.
259
+ export const ElevationAmbientLg =
260
+ "0 4px 8px -4px rgb(0 0 0 / 0.4), 0 24px 56px -18px rgb(0 0 0 / 0.6)"; // Ambient elevation, large. One step up from md; use for hover of a md surface, not as a resting default.
261
+ export const ElevationGlassSm =
262
+ "inset 0 1px 0 rgb(255 255 255 / 0.08), 0 1px 2px -1px rgb(0 0 0 / 0.4), 0 6px 16px -6px rgb(0 0 0 / 0.5)"; // Ambient-sm plus a lit top edge, for translucent panels over a blurred backdrop.
263
+ export const ElevationGlassMd =
264
+ "inset 0 1px 0 rgb(255 255 255 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.4), 0 12px 32px -10px rgb(0 0 0 / 0.55)"; // Ambient-md plus a lit top edge.
265
+ export const ElevationGlassLg =
266
+ "inset 0 1px 0 rgb(255 255 255 / 0.08), 0 4px 8px -4px rgb(0 0 0 / 0.4), 0 24px 56px -18px rgb(0 0 0 / 0.6)"; // Ambient-lg plus a lit top edge.
267
+ export const ElevationSheen = "inset 0 1px 0 rgb(0 0 0 / 0.12)"; // Top-edge sheen for solid inverted fills (bg-foreground). Lifts with light on a dark fill, with shade on a light one.
268
+ export const ElevationAmbientGlow = "0 0 80px rgb(255 255 255 / 0.06)"; // Centred offsetless halo — the only non-directional step on the ramp. Inverted from the light value: a black halo has nothing to darken against a dark surround, so the halo becomes light. Keep the alpha low; this is separation, not a bloom.
269
+ export const ElevationGlowAccent =
270
+ "0 8px 32px rgb(0 0 0 / 0.4), 0 0 60px color-mix(in srgb, var(--brand-accent) 8%, transparent)"; // Identical to the light value by design. The pool is theme-invariant black because glow-accent surfaces are fixed dark glass in both themes; only the accent halo shifts, via --brand-accent.
271
+ export const ElevationGlowAccentLg =
272
+ "0 12px 48px rgb(0 0 0 / 0.5), 0 0 80px color-mix(in srgb, var(--brand-accent) 12%, transparent)"; // Hover escalation of glow-accent. Identical to the light value by design.
273
+ export const ElevationGlowAccentSm =
274
+ "0 0 20px color-mix(in srgb, var(--brand-accent) 10%, transparent)"; // Accent halo alone, no pool. Identical to the light value by design.
275
+ export const ElevationGlowPrimary =
276
+ "0 20px 25px -5px hsl(var(--primary) / 0.05), 0 8px 10px -6px hsl(var(--primary) / 0.05)"; // xl geometry tinted with the action fill at 5 percent. Same reference as light; --primary carries the dark channels.
@@ -70,19 +70,29 @@ export const SizeBreakpointTight: string;
70
70
  export const SizeBreakpointShell: string;
71
71
  /** 1800px — 4K / Studio Display friendliness */
72
72
  export const SizeBreakpoint3xl: string;
73
+ /** Job: flush. Edge-to-edge surfaces — a full-bleed table row, a segment welded to its neighbour, an inset that must not round twice. */
73
74
  export const SizeRadiusNone: string;
75
+ /** Job: swatch. The smallest corner that still reads as intentional at 8-12px: chart legend keys, mark highlights, progress and sparkline bar caps. Added because 18 call sites had hand-written 1px, 2px and 3px corners with no step to reach for. Matches the bottom of the Tailwind scale, so rounded-xs and var(--radius-xs) agree. */
76
+ export const SizeRadiusXs: string;
77
+ /** Job: inner. The corner of something nested inside a control — a thumbnail inside a chip, a swatch inside a tile, a code span inside a paragraph. One step inside md. */
74
78
  export const SizeRadiusSm: string;
79
+ /** Job: control. The default for anything you can click or type into at text height: input, select trigger, small button, menu item, badge with square corners. The most-used step on the ladder. */
75
80
  export const SizeRadiusMd: string;
81
+ /** Job: container. Things that hold controls rather than being one: popover, dropdown surface, tooltip, toolbar, inline code block. Aliased as radius-button. */
76
82
  export const SizeRadiusLg: string;
83
+ /** Job: card. A discrete surface in a list or grid that carries its own padding and elevation. Aliased as radius-card. */
77
84
  export const SizeRadiusXl: string;
85
+ /** Job: panel. A region that holds cards — bento cell, dialog, sheet, dashboard section. Aliased as radius-panel. */
78
86
  export const SizeRadius2xl: string;
87
+ /** Job: frame. The outermost shell of a marketing or docs composition — mockup window, hero frame, screenshot chrome. The top of the ladder. */
79
88
  export const SizeRadius3xl: string;
89
+ /** Job: pill. Fully round by intent, not by size: avatar, dot, toggle track, tag. Kept as one step, not a pair, so there is exactly one way to say round. */
80
90
  export const SizeRadiusFull: string;
81
- /** 2026 button radius — Vercel/Linear/Cursor consensus */
91
+ /** Intent alias for the container step. Reach for this in product code instead of lg so the shape can be re-tuned in one place. */
82
92
  export const SizeRadiusButton: string;
83
- /** 2026 card radius */
93
+ /** Intent alias for the card step. Reach for this in product code instead of xl. */
84
94
  export const SizeRadiusCard: string;
85
- /** 2026 bento/hero panel radius */
95
+ /** Intent alias for the panel step. Reach for this in product code instead of 2xl. */
86
96
  export const SizeRadiusPanel: string;
87
97
  /** Micro-feedback: hover, focus, toggle, button press */
88
98
  export const DurationMicro: string;
@@ -109,6 +119,7 @@ export const RingHairline: string;
109
119
  /** Light-mode faint overlay (6% black) */
110
120
  export const OverlayFaint: string;
111
121
  export const FontFamilySans: string;
122
+ /** CJK-locale stack — same order as sans, minus the Latin-only system faces */
112
123
  export const FontFamilyCn: string;
113
124
  export const FontFamilyDisplay: string;
114
125
  export const FontFamilyHeading: string;
@@ -119,10 +130,6 @@ export const TransitionDefault: {
119
130
  timingFunction: string;
120
131
  delay: string;
121
132
  };
122
- /** Default transition duration — 200ms flow rail. */
123
- export const TransitionDuration: string;
124
- /** ease-out keyword form (CSS-friendly). Equivalent to cubic-bezier(0,0,0.2,1). */
125
- export const TransitionEasing: string;
126
133
  /** Brand primary — 云毓蓝 */
127
134
  export const BrandPrimary: string;
128
135
  /** Brand accent — 云毓青 */
@@ -170,11 +177,11 @@ export const ScaleNeutral3: string;
170
177
  export const ScaleNeutral4: string;
171
178
  /** component bg active */
172
179
  export const ScaleNeutral5: string;
173
- /** subtle border */
180
+ /** 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. */
174
181
  export const ScaleNeutral6: string;
175
- /** default border */
182
+ /** default border — DERIVED: OKLab lerp step5 → step9 at t=0.50. */
176
183
  export const ScaleNeutral7: string;
177
- /** hovered border */
184
+ /** hovered border — DERIVED: OKLab lerp step5 → step9 at t=0.75. */
178
185
  export const ScaleNeutral8: string;
179
186
  /** solid fill */
180
187
  export const ScaleNeutral9: string;
@@ -184,31 +191,58 @@ export const ScaleNeutral10: string;
184
191
  export const ScaleNeutral11: string;
185
192
  /** primary text */
186
193
  export const ScaleNeutral12: string;
194
+ /** app tint — L* 99.01, C 0.003, hue 264. Radix blue-1 sits at L* 99.31 C 0.003. */
187
195
  export const ScaleBlue1: string;
196
+ /** subtle background — L* 97.21, C 0.010. */
188
197
  export const ScaleBlue2: string;
198
+ /** component background default — L* 94.79, C 0.020. */
189
199
  export const ScaleBlue3: string;
200
+ /** component background hover — L* 91.69, C 0.034. */
190
201
  export const ScaleBlue4: string;
202
+ /** component background active — L* 87.77, C 0.052. Anchor for the derived border tier. */
191
203
  export const ScaleBlue5: string;
204
+ /** subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 32.3 L*) at t=0.25. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 79.61, C 0.093. */
192
205
  export const ScaleBlue6: string;
206
+ /** default border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 32.3 L*) at t=0.50. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 71.63, C 0.134. */
193
207
  export const ScaleBlue7: string;
208
+ /** hovered border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 32.3 L*) at t=0.75. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 63.47, C 0.175. */
194
209
  export const ScaleBlue8: string;
195
- /** solid fill — main brand */
210
+ /** solid fill — L* 55.50, C 0.215, hue 264.0. White label 4.99:1, inside the 4.7-5.4 band comparable products ship. Same value as shadcn --primary so the deep solid fill has one source, not two. The VI hex #0033FE (C 0.290, L* 48.4) stays the identity token --brand-primary and is deliberately NOT pinned into this ramp slot: pinning it left steps 6-9 only 3.5 L* apart with chroma climbing to 0.290. */
196
211
  export const ScaleBlue9: string;
212
+ /** solid fill hover — L* 49.52, C 0.205. White label 6.45:1. */
197
213
  export const ScaleBlue10: string;
214
+ /** secondary text — L* 42.04, C 0.180. 8.60:1 on step 1. */
198
215
  export const ScaleBlue11: string;
216
+ /** primary text — L* 23.91, C 0.115. 16.38:1 on step 1. */
199
217
  export const ScaleBlue12: string;
218
+ /** Label ink for the solid fill tier. Absolute value, identical in both modes: a deep fill always takes white, never the scale's own step 12, which is light in dark mode. 4.99:1 on step 9, 6.45:1 on step 10. */
219
+ export const ScaleBlueContrast: string;
220
+ /** app tint — L* 99.05, C 0.005, hue 172. */
200
221
  export const ScaleCyan1: string;
222
+ /** subtle background — L* 97.81, C 0.014. */
201
223
  export const ScaleCyan2: string;
224
+ /** component background default — L* 95.68, C 0.035. */
202
225
  export const ScaleCyan3: string;
226
+ /** component background hover — L* 93.29, C 0.058. */
203
227
  export const ScaleCyan4: string;
228
+ /** component background active — L* 90.27, C 0.086. Anchor for the derived border tier. */
204
229
  export const ScaleCyan5: string;
230
+ /** subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step10 (bright scale: |L9-L5| = 4.9 L* < 6.0, so the tier continues toward step 10) at t=0.25. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 88.38, C 0.102. */
205
231
  export const ScaleCyan6: string;
232
+ /** default border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step10 (bright scale: |L9-L5| = 4.9 L* < 6.0, so the tier continues toward step 10) at t=0.50. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 86.50, C 0.119. */
206
233
  export const ScaleCyan7: string;
234
+ /** hovered border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step10 (bright scale: |L9-L5| = 4.9 L* < 6.0, so the tier continues toward step 10) at t=0.75. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 84.42, C 0.134. */
207
235
  export const ScaleCyan8: string;
236
+ /** solid fill — bright scale, so the fill is LIGHT and takes dark ink: L* 85.41, C 0.164, hue 172.1. Ink 10.35:1 against --cyan-contrast. Step 9 legitimately sits above step 8 on a bright scale, exactly as Radix mint does (8 at L* 72.2, 9 at L* 87.0). */
208
237
  export const ScaleCyan9: string;
238
+ /** solid fill hover — L* 82.61, C 0.150. 2.80 L* below step 9, matching Radix mint's own 2.95 L* step9-to-step10 drop, so hovering the bright fill deepens it slightly instead of collapsing past its own border tier. The previous value was 10.87 L* below step 9 and landed DARKER than step 8. Ink 9.40:1. */
209
239
  export const ScaleCyan10: string;
240
+ /** secondary text — L* 51.64, C 0.100. 5.17:1 on step 1. */
210
241
  export const ScaleCyan11: string;
242
+ /** primary text — L* 33.45, C 0.065. 11.35:1 on step 1. */
211
243
  export const ScaleCyan12: string;
244
+ /** 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. */
245
+ export const ScaleCyanContrast: string;
212
246
  /** HSL channel triple — consume via hsl(var(--background)) */
213
247
  export const ShadcnBackground: string;
214
248
  export const ShadcnForeground: string;
@@ -216,7 +250,7 @@ export const ShadcnCard: string;
216
250
  export const ShadcnCardForeground: string;
217
251
  export const ShadcnPopover: string;
218
252
  export const ShadcnPopoverForeground: string;
219
- /** Softer brand blue #254bfa */
253
+ /** 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. */
220
254
  export const ShadcnPrimary: string;
221
255
  export const ShadcnPrimaryForeground: string;
222
256
  export const ShadcnSecondary: string;
@@ -228,10 +262,16 @@ export const ShadcnAccent: string;
228
262
  export const ShadcnAccentForeground: string;
229
263
  export const ShadcnDestructive: string;
230
264
  export const ShadcnDestructiveForeground: string;
265
+ /** 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. */
266
+ export const ShadcnDestructiveStrong: string;
231
267
  export const ShadcnSuccess: string;
232
268
  export const ShadcnSuccessForeground: string;
269
+ /** Green as FOREGROUND. --success (142 71% 29%) is 5.15:1 on white but only 4.49:1 as ink on its own 10% tint — one hundredth under AA, which is where badge green-subtle sat. Darkened two points: 5.71:1 on white, 4.99:1 on the tint, alongside --warning-strong (5.40) and --destructive-strong (5.42) so the three status foregrounds read with the same weight. Completes the trio; use --success for fills with --success-foreground on top. */
270
+ export const ShadcnSuccessStrong: string;
233
271
  export const ShadcnWarning: string;
234
272
  export const ShadcnWarningForeground: string;
273
+ /** 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. */
274
+ export const ShadcnWarningStrong: string;
235
275
  export const ShadcnInfo: string;
236
276
  export const ShadcnInfoForeground: string;
237
277
  export const ShadcnBorder: string;
@@ -291,3 +331,27 @@ export const ElevationXl: string;
291
331
  export const Elevation2xl: string;
292
332
  export const ElevationBrand: string;
293
333
  export const ElevationBrandLg: string;
334
+ /** Ambient elevation, small. Contact layer plus a wide soft pool — the marketing-surface counterpart to the tight xs..2xl product ramp. */
335
+ export const ElevationAmbientSm: string;
336
+ /** Ambient elevation, medium. Default resting depth for a floating marketing card. */
337
+ export const ElevationAmbientMd: string;
338
+ /** Ambient elevation, large. One step up from md; use for hover of a md surface, not as a resting default. */
339
+ export const ElevationAmbientLg: string;
340
+ /** Ambient-sm plus a lit top edge, for translucent panels over a blurred backdrop. */
341
+ export const ElevationGlassSm: string;
342
+ /** Ambient-md plus a lit top edge. */
343
+ export const ElevationGlassMd: string;
344
+ /** Ambient-lg plus a lit top edge. */
345
+ export const ElevationGlassLg: string;
346
+ /** Top-edge sheen for solid inverted fills (bg-foreground). Lifts with light on a dark fill, with shade on a light one. */
347
+ export const ElevationSheen: string;
348
+ /** Centred offsetless halo — the only non-directional step on the ramp. For a translucent panel that needs separation from its backdrop without reading as lifted off a surface. In dark mode this inverts to a low-alpha light halo, because a black halo cannot read against a dark surround. */
349
+ export const ElevationAmbientGlow: string;
350
+ /** Directional pool plus a wide brand-accent halo, for fixed dark-glass surfaces. The pool is theme-invariant black on purpose: the surfaces that carry an accent halo present the same dark glass in both themes, so a theme-flipping pool would lighten in light mode exactly where the panel needs the most separation. Accent sourced from --brand-accent, so it follows the light and dark accent values. */
351
+ export const ElevationGlowAccent: string;
352
+ /** Hover escalation of glow-accent. Deeper pool, wider and stronger accent halo. */
353
+ export const ElevationGlowAccentLg: string;
354
+ /** Accent halo alone, no pool. For a small accent-tinted chip or icon plate inside a glow-accent surface. */
355
+ export const ElevationGlowAccentSm: string;
356
+ /** xl geometry tinted with the action fill at 5 percent. For the emphasised card in a set (a highlighted plan) where the lift should read as coloured rather than neutral. */
357
+ export const ElevationGlowPrimary: string;
package/build/ts/light.js CHANGED
@@ -59,23 +59,24 @@ export const SizeBreakpointXs = "26.25rem"; // 420px — narrow-phone grid flip
59
59
  export const SizeBreakpointTight = "67.5rem"; // 1080px — dense dashboard header horizontal
60
60
  export const SizeBreakpointShell = "73.75rem"; // 1180px — three-pane shell threshold
61
61
  export const SizeBreakpoint3xl = "112.5rem"; // 1800px — 4K / Studio Display friendliness
62
- export const SizeRadiusNone = "0";
63
- export const SizeRadiusSm = "0.25rem";
64
- export const SizeRadiusMd = "0.375rem";
65
- export const SizeRadiusLg = "0.5rem";
66
- export const SizeRadiusXl = "0.75rem";
67
- export const SizeRadius2xl = "1rem";
68
- export const SizeRadius3xl = "1.5rem";
69
- export const SizeRadiusFull = "9999px";
70
- export const SizeRadiusButton = "0.5rem"; // 2026 button radius — Vercel/Linear/Cursor consensus
71
- export const SizeRadiusCard = "0.75rem"; // 2026 card radius
72
- export const SizeRadiusPanel = "1rem"; // 2026 bento/hero panel radius
62
+ export const SizeRadiusNone = "0"; // Job: flush. Edge-to-edge surfaces — a full-bleed table row, a segment welded to its neighbour, an inset that must not round twice.
63
+ export const SizeRadiusXs = "0.125rem"; // Job: swatch. The smallest corner that still reads as intentional at 8-12px: chart legend keys, mark highlights, progress and sparkline bar caps. Added because 18 call sites had hand-written 1px, 2px and 3px corners with no step to reach for. Matches the bottom of the Tailwind scale, so rounded-xs and var(--radius-xs) agree.
64
+ export const SizeRadiusSm = "0.25rem"; // Job: inner. The corner of something nested inside a control — a thumbnail inside a chip, a swatch inside a tile, a code span inside a paragraph. One step inside md.
65
+ export const SizeRadiusMd = "0.375rem"; // Job: control. The default for anything you can click or type into at text height: input, select trigger, small button, menu item, badge with square corners. The most-used step on the ladder.
66
+ export const SizeRadiusLg = "0.5rem"; // Job: container. Things that hold controls rather than being one: popover, dropdown surface, tooltip, toolbar, inline code block. Aliased as radius-button.
67
+ export const SizeRadiusXl = "0.75rem"; // Job: card. A discrete surface in a list or grid that carries its own padding and elevation. Aliased as radius-card.
68
+ export const SizeRadius2xl = "1rem"; // Job: panel. A region that holds cards — bento cell, dialog, sheet, dashboard section. Aliased as radius-panel.
69
+ export const SizeRadius3xl = "1.5rem"; // Job: frame. The outermost shell of a marketing or docs composition — mockup window, hero frame, screenshot chrome. The top of the ladder.
70
+ export const SizeRadiusFull = "9999px"; // Job: pill. Fully round by intent, not by size: avatar, dot, toggle track, tag. Kept as one step, not a pair, so there is exactly one way to say round.
71
+ export const SizeRadiusButton = "0.5rem"; // Intent alias for the container step. Reach for this in product code instead of lg so the shape can be re-tuned in one place.
72
+ export const SizeRadiusCard = "0.75rem"; // Intent alias for the card step. Reach for this in product code instead of xl.
73
+ export const SizeRadiusPanel = "1rem"; // Intent alias for the panel step. Reach for this in product code instead of 2xl.
73
74
  export const DurationMicro = "100ms"; // Micro-feedback: hover, focus, toggle, button press
74
75
  export const DurationFlow = "200ms"; // State flow: modal open, dropdown reveal, tab switch, default page transition
75
76
  export const DurationReveal = "300ms"; // Content unveil: slide-in, expand, accordion, drawer
76
77
  export const DurationCinematic = "500ms"; // Hero-grade entrance: landing reveal, large delight moments
77
78
  export const EasingIn = "cubic-bezier(0.4, 0, 1, 1)";
78
- export const EasingOut = "cubic-bezier(0, 0, 0.2, 1)";
79
+ export const EasingOut = "cubic-bezier(0.16, 1, 0.3, 1)";
79
80
  export const EasingInOut = "cubic-bezier(0.4, 0, 0.2, 1)";
80
81
  export const EasingSpring = "cubic-bezier(0.175, 0.885, 0.32, 1.275)";
81
82
  export const TrackingDisplay = "-0.04em"; // Hero H1
@@ -86,13 +87,13 @@ export const LeadingHeading = "1.15";
86
87
  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)
87
88
  export const OverlayFaint = "rgb(0 0 0 / 0.06)"; // Light-mode faint overlay (6% black)
88
89
  export const FontFamilySans =
89
- 'var(--font-geist-sans), "Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif';
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';
90
91
  export const FontFamilyCn =
91
- '"Noto Sans SC", "PingFang SC", "Microsoft YaHei", "vivo Sans", sans-serif';
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
92
93
  export const FontFamilyDisplay =
93
- 'var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif';
94
+ 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif';
94
95
  export const FontFamilyHeading =
95
- 'var(--font-geist-sans), "Geist", "Noto Sans SC", sans-serif';
96
+ 'var(--font-geist-sans), "Geist", var(--font-noto-sans-sc, "Noto Sans SC"), sans-serif';
96
97
  export const FontFamilyMono =
97
98
  'var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace';
98
99
  export const TransitionDefault = {
@@ -100,8 +101,6 @@ export const TransitionDefault = {
100
101
  timingFunction: "ease-out",
101
102
  delay: "0ms",
102
103
  }; // Default state-flow transition — 200ms ease-out (matches 200ms). Used by --transition shorthand.
103
- export const TransitionDuration = "200ms"; // Default transition duration — 200ms flow rail.
104
- export const TransitionEasing = "ease-out"; // ease-out keyword form (CSS-friendly). Equivalent to cubic-bezier(0,0,0.2,1).
105
104
  export const BrandPrimary = "#0033fe"; // Brand primary — 云毓蓝
106
105
  export const BrandAccent = "#0bf1c3"; // Brand accent — 云毓青
107
106
  export const BrandTertiary = "#8b5cf6";
@@ -133,44 +132,46 @@ export const ScaleNeutral2 = "#f8fafc"; // subtle background
133
132
  export const ScaleNeutral3 = "#f1f5f9"; // component bg default
134
133
  export const ScaleNeutral4 = "#e2e8f0"; // component bg hover
135
134
  export const ScaleNeutral5 = "#cbd5e1"; // component bg active
136
- export const ScaleNeutral6 = "#e2e8f0"; // subtle border
137
- export const ScaleNeutral7 = "#cbd5e1"; // default border
138
- export const ScaleNeutral8 = "#94a3b8"; // hovered border
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.
139
138
  export const ScaleNeutral9 = "#64748b"; // solid fill
140
139
  export const ScaleNeutral10 = "#475569"; // solid fill hover
141
140
  export const ScaleNeutral11 = "#334155"; // secondary text
142
141
  export const ScaleNeutral12 = "#0f172a"; // primary text
143
- export const ScaleBlue1 = "#f0f4ff";
144
- export const ScaleBlue2 = "#dbe4ff";
145
- export const ScaleBlue3 = "#bac8ff";
146
- export const ScaleBlue4 = "#91a7ff";
147
- export const ScaleBlue5 = "#5c7cfa";
148
- export const ScaleBlue6 = "#bac8ff";
149
- export const ScaleBlue7 = "#91a7ff";
150
- export const ScaleBlue8 = "#5c7cfa";
151
- export const ScaleBlue9 = "#0033fe"; // solid fill — main brand
152
- export const ScaleBlue10 = "#002ad4";
153
- export const ScaleBlue11 = "#0021ab";
154
- export const ScaleBlue12 = "#000f59";
155
- export const ScaleCyan1 = "#e6fff8";
156
- export const ScaleCyan2 = "#b3ffec";
157
- export const ScaleCyan3 = "#80ffe0";
158
- export const ScaleCyan4 = "#4dfcd4";
159
- export const ScaleCyan5 = "#1af7c8";
160
- export const ScaleCyan6 = "#80ffe0";
161
- export const ScaleCyan7 = "#4dfcd4";
162
- export const ScaleCyan8 = "#1af7c8";
163
- export const ScaleCyan9 = "#0bf1c3";
164
- export const ScaleCyan10 = "#09c9a3";
165
- export const ScaleCyan11 = "#07a183";
166
- export const ScaleCyan12 = "#035143";
142
+ 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
+ export const ScaleBlue2 = "#f2f6fd"; // subtle background — L* 97.21, C 0.010.
144
+ export const ScaleBlue3 = "#e7eefc"; // component background default — L* 94.79, C 0.020.
145
+ export const ScaleBlue4 = "#d8e4fb"; // component background hover — L* 91.69, C 0.034.
146
+ export const ScaleBlue5 = "#c6d7fa"; // component background active — L* 87.77, C 0.052. Anchor for the derived border tier.
147
+ export const ScaleBlue6 = "#9fbcf9"; // subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 32.3 L*) at t=0.25. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 79.61, C 0.093.
148
+ export const ScaleBlue7 = "#79a1f7"; // default border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 32.3 L*) at t=0.50. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 71.63, C 0.134.
149
+ export const ScaleBlue8 = "#5384f3"; // hovered border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step9 (deep scale, span 32.3 L*) at t=0.75. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 63.47, C 0.175.
150
+ export const ScaleBlue9 = "#2e65ee"; // solid fill — L* 55.50, C 0.215, hue 264.0. White label 4.99:1, inside the 4.7-5.4 band comparable products ship. Same value as shadcn --primary so the deep solid fill has one source, not two. The VI hex #0033FE (C 0.290, L* 48.4) stays the identity token --brand-primary and is deliberately NOT pinned into this ramp slot: pinning it left steps 6-9 only 3.5 L* apart with chroma climbing to 0.290.
151
+ export const ScaleBlue10 = "#2253d4"; // solid fill hover — L* 49.52, C 0.205. White label 6.45:1.
152
+ export const ScaleBlue11 = "#1740ad"; // secondary text — L* 42.04, C 0.180. 8.60:1 on step 1.
153
+ export const ScaleBlue12 = "#031654"; // primary text — L* 23.91, C 0.115. 16.38:1 on step 1.
154
+ export const ScaleBlueContrast = "#ffffff"; // Label ink for the solid fill tier. Absolute value, identical in both modes: a deep fill always takes white, never the scale's own step 12, which is light in dark mode. 4.99:1 on step 9, 6.45:1 on step 10.
155
+ export const ScaleCyan1 = "#f9fdfb"; // app tint — L* 99.05, C 0.005, hue 172.
156
+ export const ScaleCyan2 = "#effbf7"; // subtle background — L* 97.81, C 0.014.
157
+ export const ScaleCyan3 = "#daf9ee"; // component background default — L* 95.68, C 0.035.
158
+ export const ScaleCyan4 = "#c3f6e4"; // component background hover — L* 93.29, C 0.058.
159
+ export const ScaleCyan5 = "#a3f2d8"; // component background active — L* 90.27, C 0.086. Anchor for the derived border tier.
160
+ export const ScaleCyan6 = "#8eefd1"; // subtle border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step10 (bright scale: |L9-L5| = 4.9 L* < 6.0, so the tier continues toward step 10) at t=0.25. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 88.38, C 0.102.
161
+ export const ScaleCyan7 = "#77ecc9"; // default border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step10 (bright scale: |L9-L5| = 4.9 L* < 6.0, so the tier continues toward step 10) at t=0.50. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 86.50, C 0.119.
162
+ export const ScaleCyan8 = "#5be8c2"; // hovered border — DERIVED at build time (scripts/derive-border-tier.mjs): OKLab lerp of step 5 toward step10 (bright scale: |L9-L5| = 4.9 L* < 6.0, so the tier continues toward step 10) at t=0.75. The value here is a placeholder anchor only; the preprocessor overwrites it. Lands at L* 84.42, C 0.134.
163
+ export const ScaleCyan9 = "#0bf1c3"; // solid fill — bright scale, so the fill is LIGHT and takes dark ink: L* 85.41, C 0.164, hue 172.1. Ink 10.35:1 against --cyan-contrast. Step 9 legitimately sits above step 8 on a bright scale, exactly as Radix mint does (8 at L* 72.2, 9 at L* 87.0).
164
+ export const ScaleCyan10 = "#33e5bb"; // solid fill hover — L* 82.61, C 0.150. 2.80 L* below step 9, matching Radix mint's own 2.95 L* step9-to-step10 drop, so hovering the bright fill deepens it slightly instead of collapsing past its own border tier. The previous value was 10.87 L* below step 9 and landed DARKER than step 8. Ink 9.40:1.
165
+ export const ScaleCyan11 = "#027a61"; // secondary text — L* 51.64, C 0.100. 5.17:1 on step 1.
166
+ export const ScaleCyan12 = "#004133"; // primary text — L* 33.45, C 0.065. 11.35:1 on step 1.
167
+ 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.
167
168
  export const ShadcnBackground = "0 0% 100%"; // HSL channel triple — consume via hsl(var(--background))
168
169
  export const ShadcnForeground = "222 47% 11%";
169
170
  export const ShadcnCard = "0 0% 100%";
170
171
  export const ShadcnCardForeground = "222 47% 11%";
171
172
  export const ShadcnPopover = "0 0% 100%";
172
173
  export const ShadcnPopoverForeground = "222 47% 11%";
173
- export const ShadcnPrimary = "228 85% 56%"; // Softer brand blue #254bfa
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.
174
175
  export const ShadcnPrimaryForeground = "0 0% 100%";
175
176
  export const ShadcnSecondary = "210 40% 96%";
176
177
  export const ShadcnSecondaryForeground = "222 47% 11%";
@@ -180,10 +181,13 @@ export const ShadcnAccent = "228 100% 97%";
180
181
  export const ShadcnAccentForeground = "228 85% 56%"; // Softer brand blue — desaturated to match --primary, avoids 100% saturation glare on focus/accent
181
182
  export const ShadcnDestructive = "0 84% 45%";
182
183
  export const ShadcnDestructiveForeground = "0 0% 100%";
184
+ 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.
183
185
  export const ShadcnSuccess = "142 71% 29%";
184
186
  export const ShadcnSuccessForeground = "0 0% 100%";
187
+ export const ShadcnSuccessStrong = "142 71% 27%"; // Green as FOREGROUND. --success (142 71% 29%) is 5.15:1 on white but only 4.49:1 as ink on its own 10% tint — one hundredth under AA, which is where badge green-subtle sat. Darkened two points: 5.71:1 on white, 4.99:1 on the tint, alongside --warning-strong (5.40) and --destructive-strong (5.42) so the three status foregrounds read with the same weight. Completes the trio; use --success for fills with --success-foreground on top.
185
188
  export const ShadcnWarning = "38 92% 50%";
186
189
  export const ShadcnWarningForeground = "222 47% 11%";
190
+ 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.
187
191
  export const ShadcnInfo = "228 85% 56%";
188
192
  export const ShadcnInfoForeground = "0 0% 100%";
189
193
  export const ShadcnBorder = "240 5.9% 90%";
@@ -248,3 +252,25 @@ export const ElevationBrand =
248
252
  "0 0 0 1px rgb(0 51 254 / 0.15), 0 4px 20px -2px rgb(0 51 254 / 0.2)";
249
253
  export const ElevationBrandLg =
250
254
  "0 0 0 1px rgb(0 51 254 / 0.2), 0 8px 40px -4px rgb(0 51 254 / 0.3)";
255
+ export const ElevationAmbientSm =
256
+ "0 1px 2px -1px hsl(222 47% 11% / 0.06), 0 6px 16px -6px hsl(222 47% 11% / 0.12)"; // Ambient elevation, small. Contact layer plus a wide soft pool — the marketing-surface counterpart to the tight xs..2xl product ramp.
257
+ export const ElevationAmbientMd =
258
+ "0 2px 4px -2px hsl(222 47% 11% / 0.06), 0 12px 32px -10px hsl(222 47% 11% / 0.14)"; // Ambient elevation, medium. Default resting depth for a floating marketing card.
259
+ export const ElevationAmbientLg =
260
+ "0 4px 8px -4px hsl(222 47% 11% / 0.06), 0 24px 56px -18px hsl(222 47% 11% / 0.16)"; // Ambient elevation, large. One step up from md; use for hover of a md surface, not as a resting default.
261
+ export const ElevationGlassSm =
262
+ "inset 0 1px 0 rgb(255 255 255 / 0.5), 0 1px 2px -1px hsl(222 47% 11% / 0.06), 0 6px 16px -6px hsl(222 47% 11% / 0.12)"; // Ambient-sm plus a lit top edge, for translucent panels over a blurred backdrop.
263
+ export const ElevationGlassMd =
264
+ "inset 0 1px 0 rgb(255 255 255 / 0.5), 0 2px 4px -2px hsl(222 47% 11% / 0.06), 0 12px 32px -10px hsl(222 47% 11% / 0.14)"; // Ambient-md plus a lit top edge.
265
+ export const ElevationGlassLg =
266
+ "inset 0 1px 0 rgb(255 255 255 / 0.5), 0 4px 8px -4px hsl(222 47% 11% / 0.06), 0 24px 56px -18px hsl(222 47% 11% / 0.16)"; // Ambient-lg plus a lit top edge.
267
+ export const ElevationSheen = "inset 0 1px 0 rgb(255 255 255 / 0.2)"; // Top-edge sheen for solid inverted fills (bg-foreground). Lifts with light on a dark fill, with shade on a light one.
268
+ export const ElevationAmbientGlow = "0 0 80px hsl(222 47% 11% / 0.1)"; // Centred offsetless halo — the only non-directional step on the ramp. For a translucent panel that needs separation from its backdrop without reading as lifted off a surface. In dark mode this inverts to a low-alpha light halo, because a black halo cannot read against a dark surround.
269
+ export const ElevationGlowAccent =
270
+ "0 8px 32px rgb(0 0 0 / 0.4), 0 0 60px color-mix(in srgb, var(--brand-accent) 8%, transparent)"; // Directional pool plus a wide brand-accent halo, for fixed dark-glass surfaces. The pool is theme-invariant black on purpose: the surfaces that carry an accent halo present the same dark glass in both themes, so a theme-flipping pool would lighten in light mode exactly where the panel needs the most separation. Accent sourced from --brand-accent, so it follows the light and dark accent values.
271
+ export const ElevationGlowAccentLg =
272
+ "0 12px 48px rgb(0 0 0 / 0.5), 0 0 80px color-mix(in srgb, var(--brand-accent) 12%, transparent)"; // Hover escalation of glow-accent. Deeper pool, wider and stronger accent halo.
273
+ export const ElevationGlowAccentSm =
274
+ "0 0 20px color-mix(in srgb, var(--brand-accent) 10%, transparent)"; // Accent halo alone, no pool. For a small accent-tinted chip or icon plate inside a glow-accent surface.
275
+ export const ElevationGlowPrimary =
276
+ "0 20px 25px -5px hsl(var(--primary) / 0.05), 0 8px 10px -6px hsl(var(--primary) / 0.05)"; // xl geometry tinted with the action fill at 5 percent. For the emphasised card in a set (a highlighted plan) where the lift should read as coloured rather than neutral.