@bison-lab/tokens 3.3.0 → 3.7.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/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import { FontId } from "@bison-lab/fonts";
2
+
1
3
  //#region src/colors.d.ts
2
4
  /**
3
5
  * Color derivation engine.
@@ -460,9 +462,14 @@ interface ThemeConfig {
460
462
  shadow: ShadowPreset;
461
463
  motion: MotionPreset;
462
464
  density: DensityPreset;
463
- /** A font family name. Becomes a catalogue id in BIS-58. */
465
+ /**
466
+ * A catalogue id from `@bison-lab/fonts` (`"inter"`). A value that is not
467
+ * one is a legacy family name from an older `bison.config.json` and is
468
+ * quoted into the stack as-is; the CLI migrates it to its id on the next
469
+ * run.
470
+ */
464
471
  fontBody: string;
465
- /** A font family name, or `null` to use `fontBody` for headings. */
472
+ /** A catalogue id, a legacy family name, or `null` to use `fontBody` for headings. */
466
473
  fontHeading: string | null;
467
474
  fontWeights: {
468
475
  normal: number;
@@ -477,6 +484,13 @@ interface BuildThemeCssOptions {
477
484
  * `.dark`, `"data-theme"` under `[data-theme="dark"]`.
478
485
  */
479
486
  attribute?: "class" | "data-theme";
487
+ /**
488
+ * When set, the stylesheet opens with the `@font-face` rules for the
489
+ * catalogue families the config names, served from this base URL. The
490
+ * CLI passes `/fonts`; a site rendering the theme at request time may
491
+ * prefer `fontFaceCss` in its own `<head>` and leave this unset.
492
+ */
493
+ fontsBaseUrl?: string;
480
494
  }
481
495
  declare const DEFAULT_DARK_SELECTOR = "[data-theme=\"dark\"]";
482
496
  /** Whether a value is the six-digit hex colour a theme field must hold. */
@@ -484,6 +498,8 @@ declare function isThemeHex(value: unknown): value is string;
484
498
  type ThemeColorFields = Pick<ThemeConfig, "brandPrimary" | "brandSecondary" | "brandAccent" | "brandHighlight" | "brandSuccess" | "greyScale">;
485
499
  /** The `BrandColors` a config's five hexes and grey scale describe. Throws on a hex that is not `#rrggbb`. */
486
500
  declare function brandColorsFromConfig(config: ThemeColorFields): BrandColors;
501
+ /** The catalogue ids a config names, body first, without repeats. A legacy family name is not one. */
502
+ declare function themeFontIds(config: Pick<ThemeConfig, "fontBody" | "fontHeading">): FontId[];
487
503
  /**
488
504
  * The complete theme stylesheet for a config: the light semantic palette,
489
505
  * the brand and destructive scales, the four preset variable blocks, the
@@ -497,9 +513,10 @@ declare function brandColorsFromConfig(config: ThemeColorFields): BrandColors;
497
513
  * that stored a bad value fails loudly rather than shipping a stylesheet
498
514
  * with a `NaN` in it. A blank `darkSelector` means the default.
499
515
  *
500
- * Until BIS-58 lands the font catalogue, `fontBody` and `fontHeading` are
501
- * family names quoted straight into `--font-body` / `--font-heading`, as the
502
- * CLI has always written them.
516
+ * `fontBody` / `fontHeading` are catalogue ids and become the catalogue's
517
+ * stack; a legacy family name is quoted as the CLI always wrote it. The
518
+ * `@font-face` rules themselves are written only when `fontsBaseUrl` is
519
+ * given.
503
520
  */
504
521
  declare function buildThemeCss(config: ThemeConfig, options?: BuildThemeCssOptions): string;
505
522
  //#endregion
@@ -551,6 +568,17 @@ declare function relativeLuminance(color: ColorInput): number;
551
568
  declare function contrastRatio(a: ColorInput, b: ColorInput): number;
552
569
  /** The WCAG text ratings a ratio earns: 3:1 large text, 4.5:1 AA, 7:1 AAA. */
553
570
  declare function rateContrast(ratio: number): ContrastRating;
571
+ /** The level word both `ContrastStrip` and the Theme admin field show. */
572
+ declare function contrastLevelLabel(level: ContrastLevel): string;
573
+ interface ContrastVerdict {
574
+ meets: boolean;
575
+ text: string;
576
+ }
577
+ /**
578
+ * Whether `sample` clears `target`, and the sentence both renderers print
579
+ * (`"meets 4.5:1"` / `"below 7:1"`).
580
+ */
581
+ declare function contrastVerdict(sample: Pick<ContrastSample, "ratio">, target: number): ContrastVerdict;
554
582
  /**
555
583
  * Measures every text-carrying pairing the palette derives for these
556
584
  * colours, in light and dark. Advice, not enforcement: nothing here refuses
@@ -563,5 +591,114 @@ declare function rateContrast(ratio: number): ContrastRating;
563
591
  */
564
592
  declare function auditTheme(config: ContrastThemeInput, options?: AuditThemeOptions): ContrastCheck[];
565
593
  //#endregion
566
- export { type AnimationPresets, type AuditThemeOptions, type BrandColors, type BrandScales, type BuildThemeCssOptions, type ColorInput, type ColorScale, type ContrastCheck, type ContrastLevel, type ContrastRating, type ContrastRole, type ContrastSample, type ContrastThemeInput, DEFAULT_DARK_SELECTOR, DESTRUCTIVE_SCALE_HEX, type DensityPreset, type FontWeightMap, GREY_SCALES, type GreyScale, type HSL, type MotionPreset, type MotionVariant, type OKLCH, type RadiusPreset, SHADE_STEPS, type SemanticPalette, type ShadeStep, type ShadowPreset, type StaggerContainerVariant, type ThemeConfig, type ZIndexToken, auditTheme, brandColorsFromConfig, buildThemeCss, contrastRatio, defaultFontWeights, densityPresets, deriveDarkPalette, deriveLightPalette, generateBrandScales, generateColorScale, generateDestructiveScaleCSS, generatePaletteCSS, generateScaleCSS, getAnimationPresets, getDestructiveColorScale, hexToHSL, hexToOklch, hslToString, isThemeHex, motionPresets, oklchToHslString, radiusPresets, rateContrast, relativeLuminance, shadowPresets, spacingScale, typeScale, zIndexTokens };
594
+ //#region src/preset-hints.d.ts
595
+ /**
596
+ * The label and hint the CLI and the Theme Global show for each preset
597
+ * value. One record, so the two cannot drift.
598
+ */
599
+ interface PresetHint {
600
+ label: string;
601
+ hint: string;
602
+ }
603
+ declare const presetHints: {
604
+ readonly greyScale: {
605
+ readonly slate: {
606
+ readonly label: "Slate";
607
+ readonly hint: "Cool blue-grey — modern, tech-forward";
608
+ };
609
+ readonly gray: {
610
+ readonly label: "Gray";
611
+ readonly hint: "Balanced blue-grey — neutral default";
612
+ };
613
+ readonly zinc: {
614
+ readonly label: "Zinc";
615
+ readonly hint: "Cool desaturated — minimal, sharp";
616
+ };
617
+ readonly neutral: {
618
+ readonly label: "Neutral";
619
+ readonly hint: "Pure grey — zero hue bias";
620
+ };
621
+ readonly stone: {
622
+ readonly label: "Stone";
623
+ readonly hint: "Warm earthy grey — organic, approachable";
624
+ };
625
+ };
626
+ readonly radius: {
627
+ readonly sharp: {
628
+ readonly label: "Sharp";
629
+ readonly hint: "0px — square corners";
630
+ };
631
+ readonly subtle: {
632
+ readonly label: "Subtle";
633
+ readonly hint: "6px — barely rounded";
634
+ };
635
+ readonly rounded: {
636
+ readonly label: "Rounded";
637
+ readonly hint: "8px — balanced (default)";
638
+ };
639
+ readonly pill: {
640
+ readonly label: "Pill";
641
+ readonly hint: "12px — soft, bubbly";
642
+ };
643
+ };
644
+ readonly shadow: {
645
+ readonly flat: {
646
+ readonly label: "Flat";
647
+ readonly hint: "No shadows — clean, modern";
648
+ };
649
+ readonly subtle: {
650
+ readonly label: "Subtle";
651
+ readonly hint: "Soft low-opacity shadows (default)";
652
+ };
653
+ readonly elevated: {
654
+ readonly label: "Elevated";
655
+ readonly hint: "Prominent layered shadows";
656
+ };
657
+ };
658
+ readonly motion: {
659
+ readonly snappy: {
660
+ readonly label: "Snappy";
661
+ readonly hint: "Fast, spring-like (150ms)";
662
+ };
663
+ readonly smooth: {
664
+ readonly label: "Smooth";
665
+ readonly hint: "Balanced ease-out (200ms, default)";
666
+ };
667
+ readonly minimal: {
668
+ readonly label: "Minimal";
669
+ readonly hint: "Barely-there, linear (100ms)";
670
+ };
671
+ };
672
+ readonly density: {
673
+ readonly compact: {
674
+ readonly label: "Compact";
675
+ readonly hint: "Tight padding, smaller components";
676
+ };
677
+ readonly default: {
678
+ readonly label: "Default";
679
+ readonly hint: "Balanced spacing";
680
+ };
681
+ readonly spacious: {
682
+ readonly label: "Spacious";
683
+ readonly hint: "Generous padding, larger components";
684
+ };
685
+ };
686
+ readonly defaultTheme: {
687
+ readonly system: {
688
+ readonly label: "System";
689
+ readonly hint: "Follow OS preference (recommended)";
690
+ };
691
+ readonly light: {
692
+ readonly label: "Light";
693
+ readonly hint: "Always start in light mode";
694
+ };
695
+ readonly dark: {
696
+ readonly label: "Dark";
697
+ readonly hint: "Always start in dark mode";
698
+ };
699
+ };
700
+ };
701
+ type PresetHintFamily = keyof typeof presetHints;
702
+ //#endregion
703
+ export { type AnimationPresets, type AuditThemeOptions, type BrandColors, type BrandScales, type BuildThemeCssOptions, type ColorInput, type ColorScale, type ContrastCheck, type ContrastLevel, type ContrastRating, type ContrastRole, type ContrastSample, type ContrastThemeInput, type ContrastVerdict, DEFAULT_DARK_SELECTOR, DESTRUCTIVE_SCALE_HEX, type DensityPreset, type FontWeightMap, GREY_SCALES, type GreyScale, type HSL, type MotionPreset, type MotionVariant, type OKLCH, type PresetHint, type PresetHintFamily, type RadiusPreset, SHADE_STEPS, type SemanticPalette, type ShadeStep, type ShadowPreset, type StaggerContainerVariant, type ThemeConfig, type ZIndexToken, auditTheme, brandColorsFromConfig, buildThemeCss, contrastLevelLabel, contrastRatio, contrastVerdict, defaultFontWeights, densityPresets, deriveDarkPalette, deriveLightPalette, generateBrandScales, generateColorScale, generateDestructiveScaleCSS, generatePaletteCSS, generateScaleCSS, getAnimationPresets, getDestructiveColorScale, hexToHSL, hexToOklch, hslToString, isThemeHex, motionPresets, oklchToHslString, presetHints, radiusPresets, rateContrast, relativeLuminance, shadowPresets, spacingScale, themeFontIds, typeScale, zIndexTokens };
567
704
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/colors.ts","../src/oklch.ts","../src/radius.ts","../src/shadows.ts","../src/motion.ts","../src/density.ts","../src/typography.ts","../src/spacing.ts","../src/z-index.ts","../src/animation-presets.ts","../src/theme.ts","../src/contrast.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/colors.ts","../src/oklch.ts","../src/radius.ts","../src/shadows.ts","../src/motion.ts","../src/density.ts","../src/typography.ts","../src/spacing.ts","../src/z-index.ts","../src/animation-presets.ts","../src/theme.ts","../src/contrast.ts","../src/preset-hints.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { fontFaceCss, fontStack, isFontId } from "@bison-lab/fonts";
1
2
  //#region src/oklch.ts
2
3
  function srgbToLinear(c) {
3
4
  return c <= .04045 ? c / 12.92 : Math.pow((c + .055) / 1.055, 2.4);
@@ -949,6 +950,17 @@ function resolveDarkSelector(config, options) {
949
950
  if (options?.attribute === "data-theme") return DEFAULT_DARK_SELECTOR;
950
951
  return config.darkSelector?.trim() || "[data-theme=\"dark\"]";
951
952
  }
953
+ const LEGACY_FONT_FALLBACK = "ui-sans-serif, system-ui, sans-serif";
954
+ /** The `font-family` value for a config font: the catalogue stack for an id, the CLI's old quoting for a legacy name. */
955
+ function fontFamilyValue(value) {
956
+ return isFontId(value) ? fontStack(value) : `'${value}', ${LEGACY_FONT_FALLBACK}`;
957
+ }
958
+ /** The catalogue ids a config names, body first, without repeats. A legacy family name is not one. */
959
+ function themeFontIds(config) {
960
+ const ids = [];
961
+ for (const value of [config.fontBody, config.fontHeading ?? config.fontBody]) if (isFontId(value) && !ids.includes(value)) ids.push(value);
962
+ return ids;
963
+ }
952
964
  function paletteLines(palette, indent) {
953
965
  return Object.entries(palette).map(([key, value]) => `${indent}--${key}: ${value};`);
954
966
  }
@@ -968,9 +980,10 @@ function presetLines(vars) {
968
980
  * that stored a bad value fails loudly rather than shipping a stylesheet
969
981
  * with a `NaN` in it. A blank `darkSelector` means the default.
970
982
  *
971
- * Until BIS-58 lands the font catalogue, `fontBody` and `fontHeading` are
972
- * family names quoted straight into `--font-body` / `--font-heading`, as the
973
- * CLI has always written them.
983
+ * `fontBody` / `fontHeading` are catalogue ids and become the catalogue's
984
+ * stack; a legacy family name is quoted as the CLI always wrote it. The
985
+ * `@font-face` rules themselves are written only when `fontsBaseUrl` is
986
+ * given.
974
987
  */
975
988
  function buildThemeCss(config, options) {
976
989
  const brand = brandColorsFromConfig(config);
@@ -978,6 +991,7 @@ function buildThemeCss(config, options) {
978
991
  const dark = deriveDarkPalette(brand);
979
992
  const scales = generateBrandScales(brand);
980
993
  const darkSelector = resolveDarkSelector(config, options);
994
+ const fontFaces = options?.fontsBaseUrl ? fontFaceCss(themeFontIds(config), options.fontsBaseUrl) : "";
981
995
  const darkBlock = darkSelector === "@media" ? [
982
996
  `@media (prefers-color-scheme: dark) {`,
983
997
  ` :root {`,
@@ -995,6 +1009,7 @@ function buildThemeCss(config, options) {
995
1009
  `/* Grey: ${config.greyScale} */`,
996
1010
  `/* Presets: radius ${config.radius} · shadow ${config.shadow} · motion ${config.motion} · density ${config.density} */`,
997
1011
  ``,
1012
+ ...fontFaces ? [fontFaces, ``] : [],
998
1013
  `:root {`,
999
1014
  ...paletteLines(light, " "),
1000
1015
  ``,
@@ -1017,8 +1032,8 @@ function buildThemeCss(config, options) {
1017
1032
  ...presetLines(densityPresets[config.density]),
1018
1033
  ``,
1019
1034
  ` /* Typography */`,
1020
- ` --font-body: '${config.fontBody}', ui-sans-serif, system-ui, sans-serif;`,
1021
- ` --font-heading: '${config.fontHeading ?? config.fontBody}', ui-sans-serif, system-ui, sans-serif;`,
1035
+ ` --font-body: ${fontFamilyValue(config.fontBody)};`,
1036
+ ` --font-heading: ${fontFamilyValue(config.fontHeading ?? config.fontBody)};`,
1022
1037
  ` --font-weight-normal: ${config.fontWeights.normal};`,
1023
1038
  ` --font-weight-medium: ${config.fontWeights.medium};`,
1024
1039
  ` --font-weight-semibold: ${config.fontWeights.semibold};`,
@@ -1122,6 +1137,27 @@ function rateContrast(ratio) {
1122
1137
  level: aaa ? "aaa" : aa ? "aa" : aaLarge ? "aa-large" : "fail"
1123
1138
  };
1124
1139
  }
1140
+ const LEVEL_LABEL = {
1141
+ aaa: "AAA",
1142
+ aa: "AA",
1143
+ "aa-large": "AA large",
1144
+ fail: "Fails"
1145
+ };
1146
+ /** The level word both `ContrastStrip` and the Theme admin field show. */
1147
+ function contrastLevelLabel(level) {
1148
+ return LEVEL_LABEL[level];
1149
+ }
1150
+ /**
1151
+ * Whether `sample` clears `target`, and the sentence both renderers print
1152
+ * (`"meets 4.5:1"` / `"below 7:1"`).
1153
+ */
1154
+ function contrastVerdict(sample, target) {
1155
+ const meets = sample.ratio >= target;
1156
+ return {
1157
+ meets,
1158
+ text: `${meets ? "meets" : "below"} ${target}:1`
1159
+ };
1160
+ }
1125
1161
  /** Every pairing in the system that carries text, plus the focus ring. */
1126
1162
  const PAIRINGS = [
1127
1163
  {
@@ -1288,6 +1324,106 @@ function auditTheme(config, options = {}) {
1288
1324
  });
1289
1325
  }
1290
1326
  //#endregion
1291
- export { DEFAULT_DARK_SELECTOR, DESTRUCTIVE_SCALE_HEX, GREY_SCALES, SHADE_STEPS, auditTheme, brandColorsFromConfig, buildThemeCss, contrastRatio, defaultFontWeights, densityPresets, deriveDarkPalette, deriveLightPalette, generateBrandScales, generateColorScale, generateDestructiveScaleCSS, generatePaletteCSS, generateScaleCSS, getAnimationPresets, getDestructiveColorScale, hexToHSL, hexToOklch, hslToString, isThemeHex, motionPresets, oklchToHslString, radiusPresets, rateContrast, relativeLuminance, shadowPresets, spacingScale, typeScale, zIndexTokens };
1327
+ //#region src/preset-hints.ts
1328
+ const presetHints = {
1329
+ greyScale: {
1330
+ slate: {
1331
+ label: "Slate",
1332
+ hint: "Cool blue-grey — modern, tech-forward"
1333
+ },
1334
+ gray: {
1335
+ label: "Gray",
1336
+ hint: "Balanced blue-grey — neutral default"
1337
+ },
1338
+ zinc: {
1339
+ label: "Zinc",
1340
+ hint: "Cool desaturated — minimal, sharp"
1341
+ },
1342
+ neutral: {
1343
+ label: "Neutral",
1344
+ hint: "Pure grey — zero hue bias"
1345
+ },
1346
+ stone: {
1347
+ label: "Stone",
1348
+ hint: "Warm earthy grey — organic, approachable"
1349
+ }
1350
+ },
1351
+ radius: {
1352
+ sharp: {
1353
+ label: "Sharp",
1354
+ hint: "0px — square corners"
1355
+ },
1356
+ subtle: {
1357
+ label: "Subtle",
1358
+ hint: "6px — barely rounded"
1359
+ },
1360
+ rounded: {
1361
+ label: "Rounded",
1362
+ hint: "8px — balanced (default)"
1363
+ },
1364
+ pill: {
1365
+ label: "Pill",
1366
+ hint: "12px — soft, bubbly"
1367
+ }
1368
+ },
1369
+ shadow: {
1370
+ flat: {
1371
+ label: "Flat",
1372
+ hint: "No shadows — clean, modern"
1373
+ },
1374
+ subtle: {
1375
+ label: "Subtle",
1376
+ hint: "Soft low-opacity shadows (default)"
1377
+ },
1378
+ elevated: {
1379
+ label: "Elevated",
1380
+ hint: "Prominent layered shadows"
1381
+ }
1382
+ },
1383
+ motion: {
1384
+ snappy: {
1385
+ label: "Snappy",
1386
+ hint: "Fast, spring-like (150ms)"
1387
+ },
1388
+ smooth: {
1389
+ label: "Smooth",
1390
+ hint: "Balanced ease-out (200ms, default)"
1391
+ },
1392
+ minimal: {
1393
+ label: "Minimal",
1394
+ hint: "Barely-there, linear (100ms)"
1395
+ }
1396
+ },
1397
+ density: {
1398
+ compact: {
1399
+ label: "Compact",
1400
+ hint: "Tight padding, smaller components"
1401
+ },
1402
+ default: {
1403
+ label: "Default",
1404
+ hint: "Balanced spacing"
1405
+ },
1406
+ spacious: {
1407
+ label: "Spacious",
1408
+ hint: "Generous padding, larger components"
1409
+ }
1410
+ },
1411
+ defaultTheme: {
1412
+ system: {
1413
+ label: "System",
1414
+ hint: "Follow OS preference (recommended)"
1415
+ },
1416
+ light: {
1417
+ label: "Light",
1418
+ hint: "Always start in light mode"
1419
+ },
1420
+ dark: {
1421
+ label: "Dark",
1422
+ hint: "Always start in dark mode"
1423
+ }
1424
+ }
1425
+ };
1426
+ //#endregion
1427
+ export { DEFAULT_DARK_SELECTOR, DESTRUCTIVE_SCALE_HEX, GREY_SCALES, SHADE_STEPS, auditTheme, brandColorsFromConfig, buildThemeCss, contrastLevelLabel, contrastRatio, contrastVerdict, defaultFontWeights, densityPresets, deriveDarkPalette, deriveLightPalette, generateBrandScales, generateColorScale, generateDestructiveScaleCSS, generatePaletteCSS, generateScaleCSS, getAnimationPresets, getDestructiveColorScale, hexToHSL, hexToOklch, hslToString, isThemeHex, motionPresets, oklchToHslString, presetHints, radiusPresets, rateContrast, relativeLuminance, shadowPresets, spacingScale, themeFontIds, typeScale, zIndexTokens };
1292
1428
 
1293
1429
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/oklch.ts","../src/colors.ts","../src/radius.ts","../src/shadows.ts","../src/motion.ts","../src/density.ts","../src/typography.ts","../src/spacing.ts","../src/z-index.ts","../src/animation-presets.ts","../src/theme.ts","../src/contrast.ts"],"sourcesContent":["/**\n * OKLCH color space math — zero dependencies.\n * hex → sRGB → linear sRGB → OKLab (matrix) → OKLCH (polar)\n * Reverse: OKLCH → OKLab → linear sRGB → sRGB → HSL string\n * Gamut clamping via binary search on chroma (12 iterations).\n */\n\nexport interface OKLCH {\n L: number; // lightness 0–1\n C: number; // chroma ≥ 0\n H: number; // hue 0–360\n}\n\n// --- sRGB ↔ linear sRGB ---\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(c: number): number {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;\n}\n\n// --- Hex → sRGB ---\n\nfunction hexToSrgb(hex: string): [number, number, number] {\n hex = hex.replace(\"#\", \"\");\n return [\n parseInt(hex.substring(0, 2), 16) / 255,\n parseInt(hex.substring(2, 4), 16) / 255,\n parseInt(hex.substring(4, 6), 16) / 255,\n ];\n}\n\n// --- linear sRGB ↔ OKLab ---\n\nfunction linearSrgbToOklab(r: number, g: number, b: number): [number, number, number] {\n const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;\n const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;\n const s = 0.0883024619 * r + 0.2024326059 * g + 0.6892649272 * b;\n\n const lc = Math.cbrt(l);\n const mc = Math.cbrt(m);\n const sc = Math.cbrt(s);\n\n return [\n 0.2104542553 * lc + 0.7936177850 * mc - 0.0040720468 * sc,\n 1.9779984951 * lc - 2.4285922050 * mc + 0.4505937099 * sc,\n 0.0259040371 * lc + 0.7827717662 * mc - 0.8086757660 * sc,\n ];\n}\n\nfunction oklabToLinearSrgb(L: number, a: number, b: number): [number, number, number] {\n const lc = L + 0.3963377774 * a + 0.2158037573 * b;\n const mc = L - 0.1055613458 * a - 0.0638541728 * b;\n const sc = L - 0.0894841775 * a - 1.2914855480 * b;\n\n return [\n 4.0767416621 * lc * lc * lc - 3.3077115913 * mc * mc * mc + 0.2309699292 * sc * sc * sc,\n -1.2684380046 * lc * lc * lc + 2.6097574011 * mc * mc * mc - 0.3413193965 * sc * sc * sc,\n -0.0041960863 * lc * lc * lc - 0.7034186147 * mc * mc * mc + 1.7076147010 * sc * sc * sc,\n ];\n}\n\n// --- OKLab ↔ OKLCH ---\n\nfunction oklabToOklch(L: number, a: number, b: number): OKLCH {\n const C = Math.sqrt(a * a + b * b);\n let H = (Math.atan2(b, a) * 180) / Math.PI;\n if (H < 0) H += 360;\n return { L, C, H };\n}\n\nfunction oklchToOklab(L: number, C: number, H: number): [number, number, number] {\n const hRad = (H * Math.PI) / 180;\n return [L, C * Math.cos(hRad), C * Math.sin(hRad)];\n}\n\n// --- Gamut clamping ---\n\nfunction isInGamut(r: number, g: number, b: number): boolean {\n const eps = 1e-4;\n return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;\n}\n\nfunction gamutClampChroma(L: number, C: number, H: number): number {\n if (C <= 0) return 0;\n\n const [, a, b] = oklchToOklab(L, C, H);\n const [r, g, bl] = oklabToLinearSrgb(L, a, b);\n\n if (isInGamut(r, g, bl)) return C;\n\n let lo = 0;\n let hi = C;\n\n for (let i = 0; i < 12; i++) {\n const mid = (lo + hi) / 2;\n const [, am, bm] = oklchToOklab(L, mid, H);\n const [rm, gm, bm2] = oklabToLinearSrgb(L, am, bm);\n\n if (isInGamut(rm, gm, bm2)) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n return lo;\n}\n\n// --- sRGB → HSL ---\n\nfunction srgbToHsl(r: number, g: number, b: number): [number, number, number] {\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n let h = 0;\n let s = 0;\n\n if (max !== min) {\n const d = max - min;\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n switch (max) {\n case r:\n h = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n break;\n case g:\n h = ((b - r) / d + 2) / 6;\n break;\n case b:\n h = ((r - g) / d + 4) / 6;\n break;\n }\n }\n\n return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];\n}\n\n// --- Public API ---\n\nexport function hexToOklch(hex: string): OKLCH {\n const [r, g, b] = hexToSrgb(hex);\n const [lr, lg, lb] = [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];\n const [L, a, ob] = linearSrgbToOklab(lr, lg, lb);\n return oklabToOklch(L, a, ob);\n}\n\nexport function oklchToHslString(L: number, C: number, H: number): string {\n const clampedC = gamutClampChroma(L, C, H);\n const [, a, b] = oklchToOklab(L, clampedC, H);\n const [lr, lg, lb] = oklabToLinearSrgb(L, a, b);\n\n const r = Math.min(1, Math.max(0, linearToSrgb(lr)));\n const g = Math.min(1, Math.max(0, linearToSrgb(lg)));\n const bl = Math.min(1, Math.max(0, linearToSrgb(lb)));\n\n const [h, s, l] = srgbToHsl(r, g, bl);\n return `${h} ${s}% ${l}%`;\n}\n","/**\n * Color derivation engine.\n * Takes brand colors (hex) + a grey scale selection and derives\n * a full semantic palette for both light and dark modes,\n * plus 50–950 shade scales via OKLCH color space.\n */\n\nimport { hexToOklch, oklchToHslString } from \"./oklch\";\n\nexport interface HSL {\n h: number;\n s: number;\n l: number;\n}\n\nexport function hexToHSL(hex: string): HSL {\n hex = hex.replace(\"#\", \"\");\n const r = parseInt(hex.substring(0, 2), 16) / 255;\n const g = parseInt(hex.substring(2, 4), 16) / 255;\n const b = parseInt(hex.substring(4, 6), 16) / 255;\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n let h = 0;\n let s = 0;\n\n if (max !== min) {\n const d = max - min;\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n switch (max) {\n case r:\n h = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n break;\n case g:\n h = ((b - r) / d + 2) / 6;\n break;\n case b:\n h = ((r - g) / d + 4) / 6;\n break;\n }\n }\n\n return {\n h: Math.round(h * 360),\n s: Math.round(s * 100),\n l: Math.round(l * 100),\n };\n}\n\nexport function hslToString(hsl: HSL): string {\n return `${hsl.h} ${hsl.s}% ${hsl.l}%`;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n// --- Grey scale palettes (Tailwind v4 values as HSL strings) ---\n\nexport type GreyScale = \"slate\" | \"gray\" | \"zinc\" | \"neutral\" | \"stone\";\n\ntype GreyShade = \"50\" | \"100\" | \"200\" | \"300\" | \"400\" | \"500\" | \"600\" | \"700\" | \"800\" | \"900\" | \"950\";\n\ntype GreyPalette = Record<GreyShade, string>;\n\nexport const GREY_SCALES: Record<GreyScale, GreyPalette> = {\n slate: {\n \"50\": \"210 40% 98%\",\n \"100\": \"210 40% 96%\",\n \"200\": \"214 32% 91%\",\n \"300\": \"213 27% 84%\",\n \"400\": \"215 20% 65%\",\n \"500\": \"215 16% 47%\",\n \"600\": \"215 19% 35%\",\n \"700\": \"215 25% 27%\",\n \"800\": \"217 33% 17%\",\n \"900\": \"222 47% 11%\",\n \"950\": \"229 84% 5%\",\n },\n gray: {\n \"50\": \"210 20% 98%\",\n \"100\": \"220 14% 96%\",\n \"200\": \"220 13% 91%\",\n \"300\": \"216 12% 84%\",\n \"400\": \"218 11% 65%\",\n \"500\": \"220 9% 46%\",\n \"600\": \"215 14% 34%\",\n \"700\": \"217 19% 27%\",\n \"800\": \"215 28% 17%\",\n \"900\": \"221 39% 11%\",\n \"950\": \"224 71% 4%\",\n },\n zinc: {\n \"50\": \"0 0% 98%\",\n \"100\": \"240 5% 96%\",\n \"200\": \"240 6% 90%\",\n \"300\": \"240 5% 84%\",\n \"400\": \"240 5% 65%\",\n \"500\": \"240 4% 46%\",\n \"600\": \"240 5% 34%\",\n \"700\": \"240 5% 26%\",\n \"800\": \"240 4% 16%\",\n \"900\": \"240 6% 10%\",\n \"950\": \"240 10% 4%\",\n },\n neutral: {\n \"50\": \"0 0% 98%\",\n \"100\": \"0 0% 96%\",\n \"200\": \"0 0% 90%\",\n \"300\": \"0 0% 83%\",\n \"400\": \"0 0% 64%\",\n \"500\": \"0 0% 45%\",\n \"600\": \"0 0% 32%\",\n \"700\": \"0 0% 25%\",\n \"800\": \"0 0% 15%\",\n \"900\": \"0 0% 9%\",\n \"950\": \"0 0% 4%\",\n },\n stone: {\n \"50\": \"60 9% 98%\",\n \"100\": \"60 5% 96%\",\n \"200\": \"20 6% 90%\",\n \"300\": \"24 6% 83%\",\n \"400\": \"24 5% 64%\",\n \"500\": \"25 5% 45%\",\n \"600\": \"33 5% 32%\",\n \"700\": \"30 6% 25%\",\n \"800\": \"12 6% 15%\",\n \"900\": \"24 10% 10%\",\n \"950\": \"20 14% 4%\",\n },\n};\n\n// --- Brand color types ---\n\nexport interface BrandColors {\n primary: string; // hex\n secondary: string; // hex\n accent?: string; // hex\n highlight?: string; // hex\n success: string; // hex\n grey?: GreyScale; // defaults to \"slate\"\n}\n\n// --- Shade scale types ---\n\nexport const SHADE_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const;\nexport type ShadeStep = (typeof SHADE_STEPS)[number];\nexport type ColorScale = Record<ShadeStep, string>;\n\nexport interface BrandScales {\n primary: ColorScale;\n secondary: ColorScale;\n \"brand-accent\": ColorScale;\n highlight: ColorScale;\n success: ColorScale;\n}\n\n// --- Target OKLCH lightness per step ---\n\nconst TARGET_LIGHTNESS: Record<ShadeStep, number> = {\n 50: 0.97,\n 100: 0.93,\n 200: 0.87,\n 300: 0.79,\n 400: 0.70,\n 500: 0.59,\n 600: 0.49,\n 700: 0.39,\n 800: 0.30,\n 900: 0.21,\n 950: 0.14,\n};\n\n// --- Scale generation ---\n\n/**\n * Generate a full 50–950 color scale from a single hex value.\n * Uses OKLCH for perceptual uniformity. The input color is auto-anchored\n * to its closest shade step and reproduced pixel-perfect at that step.\n */\nexport function generateColorScale(hex: string): ColorScale {\n const { L: inputL, C: inputC, H } = hexToOklch(hex);\n\n // Sine-based chroma curve: peaks at mid-lightness, falls toward extremes\n const chromaCurve = (L: number): number => Math.sin(Math.PI * L);\n const inputCurveValue = chromaCurve(inputL);\n\n // Anchor ratio so that input chroma is preserved at input lightness\n const chromaRatio = inputCurveValue > 0.01 ? inputC / inputCurveValue : inputC;\n\n const scale = {} as ColorScale;\n\n for (const step of SHADE_STEPS) {\n const targetL = TARGET_LIGHTNESS[step];\n const targetC = inputCurveValue > 0.01\n ? chromaRatio * chromaCurve(targetL)\n : inputC * chromaCurve(targetL);\n\n scale[step] = oklchToHslString(targetL, targetC, H);\n }\n\n return scale;\n}\n\n/**\n * Generate shade scales for all brand colors.\n */\nexport function generateBrandScales(brand: BrandColors): BrandScales {\n const accentHex = brand.accent ?? brand.primary;\n const highlightHex = brand.highlight ?? brand.secondary;\n\n return {\n primary: generateColorScale(brand.primary),\n secondary: generateColorScale(brand.secondary),\n \"brand-accent\": generateColorScale(accentHex),\n highlight: generateColorScale(highlightHex),\n success: generateColorScale(brand.success),\n };\n}\n\n/**\n * Generate CSS custom property declarations for all brand scales.\n * Output is indented with 2 spaces, suitable for injection into :root {}.\n */\nexport function generateScaleCSS(scales: BrandScales): string {\n const lines: string[] = [];\n\n for (const [colorName, scale] of Object.entries(scales)) {\n for (const step of SHADE_STEPS) {\n lines.push(` --${colorName}-${step}: ${(scale as ColorScale)[step]};`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Canonical red for destructive UI (matches common “danger” hue). Used for the\n * destructive 50–950 scale so tinted surfaces (e.g. soft delete buttons) work\n * like success scales, independent of brand colors.\n */\nexport const DESTRUCTIVE_SCALE_HEX = \"#ef4444\";\n\nlet destructiveColorScaleCache: ColorScale | null = null;\n\nexport function getDestructiveColorScale(): ColorScale {\n if (!destructiveColorScaleCache) {\n destructiveColorScaleCache = generateColorScale(DESTRUCTIVE_SCALE_HEX);\n }\n return destructiveColorScaleCache;\n}\n\n/** CSS custom properties `--destructive-50` … `--destructive-950` for :root injection. */\nexport function generateDestructiveScaleCSS(): string {\n const scale = getDestructiveColorScale();\n return SHADE_STEPS.map((step) => ` --destructive-${step}: ${scale[step]};`).join(\"\\n\");\n}\n\n// --- Semantic palette ---\n\nexport interface SemanticPalette {\n background: string;\n foreground: string;\n card: string;\n \"card-foreground\": string;\n \"surface-tertiary\": string;\n popover: string;\n \"popover-foreground\": string;\n primary: string;\n \"primary-foreground\": string;\n secondary: string;\n \"secondary-foreground\": string;\n accent: string;\n \"accent-foreground\": string;\n \"brand-accent\": string;\n \"brand-accent-foreground\": string;\n highlight: string;\n \"highlight-foreground\": string;\n success: string;\n \"success-foreground\": string;\n muted: string;\n \"muted-foreground\": string;\n \"content-secondary\": string;\n destructive: string;\n \"destructive-foreground\": string;\n border: string;\n \"border-strong\": string;\n \"border-subtle\": string;\n input: string;\n ring: string;\n}\n\n// --- Foreground contrast ---\n\n/**\n * Near-white ink on a dark fill, near-black on a light one. A lightness\n * heuristic, not a measurement: `auditTheme` in `./contrast` is what says\n * whether the pairing actually reads.\n */\nfunction contrastForeground(color: HSL): string {\n return color.l > 55\n ? hslToString({ h: color.h, s: clamp(color.s - 20, 0, 100), l: 10 })\n : hslToString({ h: color.h, s: clamp(color.s - 25, 0, 100), l: 98 });\n}\n\n/**\n * A brand fill lightens in dark mode only below this lightness. The CLI\n * shipped 40 and this module shipped 50 until BIS-57; 40 is the value every\n * generated `bison-theme.css` in the field was built with, and Spinal\n * Simplicity's `globals.css` records why a fill at 43 must not lighten: its\n * derived ink flips to near-black, and the button's fixed `hover:bg-primary-700`\n * then reads at 1.74:1.\n */\nconst DARK_FILL_LIGHTEN_BELOW = 40;\n\n/**\n * The focus ring lightens below this lightness. A ring is a thin outline on\n * the page background and carries no ink, so it is free to lighten where a\n * fill is not: `#DB0082` at 43% is 4.1:1 on zinc-950, at 68% it is 7.1:1.\n */\nconst DARK_RING_LIGHTEN_BELOW = 50;\n\nfunction lightenForDark(color: HSL, below: number): HSL {\n if (color.l < below) {\n return { ...color, l: clamp(color.l + 25, 0, 100) };\n }\n return color;\n}\n\n// --- Palette derivation ---\n\n/**\n * The light semantic palette for a brand. This is the one derivation the\n * CLI (`bison-theme.css`), `BisonProvider` and a CMS theme all share.\n *\n * Decided in BIS-57, where the CLI and this module had drifted:\n * - `muted-foreground` is grey-600, not grey-500. grey-500 is 4.6:1 on the\n * page background, and the token carries card descriptions, hints, table\n * labels and footer lines; grey-600 is 7.4:1. Spinal Simplicity overrode\n * it by hand for its patient audience.\n * - `content-secondary` is grey-500, not grey-400. It is text: the\n * progress bar's percentage label and every chart axis tick use it, and\n * grey-400 is about 2.5:1 on the page background on every scale. grey-500\n * is 4.5:1 or better on both the page background and a card.\n */\nexport function deriveLightPalette(brand: BrandColors): SemanticPalette {\n const grey = GREY_SCALES[brand.grey ?? \"slate\"];\n const primary = hexToHSL(brand.primary);\n const secondary = hexToHSL(brand.secondary);\n const accent = brand.accent ? hexToHSL(brand.accent) : primary;\n const highlight = brand.highlight ? hexToHSL(brand.highlight) : secondary;\n const success = hexToHSL(brand.success);\n\n return {\n background: grey[\"50\"],\n foreground: grey[\"950\"],\n card: \"0 0% 100%\",\n \"card-foreground\": grey[\"950\"],\n \"surface-tertiary\": grey[\"100\"],\n popover: \"0 0% 100%\",\n \"popover-foreground\": grey[\"950\"],\n primary: hslToString(primary),\n \"primary-foreground\": contrastForeground(primary),\n secondary: hslToString(secondary),\n \"secondary-foreground\": contrastForeground(secondary),\n accent: grey[\"100\"],\n \"accent-foreground\": grey[\"950\"],\n \"brand-accent\": hslToString(accent),\n \"brand-accent-foreground\": contrastForeground(accent),\n highlight: hslToString(highlight),\n \"highlight-foreground\": contrastForeground(highlight),\n success: hslToString(success),\n \"success-foreground\": contrastForeground(success),\n muted: grey[\"100\"],\n \"muted-foreground\": grey[\"600\"],\n \"content-secondary\": grey[\"500\"],\n destructive: \"0 84% 60%\",\n \"destructive-foreground\": \"0 0% 98%\",\n border: grey[\"200\"],\n \"border-strong\": grey[\"300\"],\n \"border-subtle\": grey[\"100\"],\n input: grey[\"200\"],\n ring: hslToString(primary),\n };\n}\n\n/**\n * The dark semantic palette for a brand, derived from the same inputs as\n * the light one: no manual dark palette.\n *\n * Decided in BIS-57, where the CLI and this module had drifted:\n * - Brand fills lighten only below lightness 40 (`DARK_FILL_LIGHTEN_BELOW`);\n * the ring lightens below 50 (`DARK_RING_LIGHTEN_BELOW`). See both.\n * - `muted-foreground` is grey-300, not grey-400. grey-400 is 6.9:1 on\n * `card`, where most supporting copy sits; grey-300 clears 7:1 on both\n * surfaces.\n * - `content-secondary` is grey-400, not grey-500. grey-500 on `card` is\n * about 3.7:1, under AA; grey-400 is about 7:1.\n * - `destructive` is `0 62% 30%`, not `0 84% 65%`. `destructive-foreground`\n * (`0 0% 98%`) is 9.8:1 on the former and 3.2:1 on the latter, which\n * fails AA for the button label.\n */\nexport function deriveDarkPalette(brand: BrandColors): SemanticPalette {\n const grey = GREY_SCALES[brand.grey ?? \"slate\"];\n const primaryHsl = hexToHSL(brand.primary);\n const primary = lightenForDark(primaryHsl, DARK_FILL_LIGHTEN_BELOW);\n const secondary = lightenForDark(hexToHSL(brand.secondary), DARK_FILL_LIGHTEN_BELOW);\n const accent = lightenForDark(brand.accent ? hexToHSL(brand.accent) : primaryHsl, DARK_FILL_LIGHTEN_BELOW);\n const highlight = lightenForDark(\n brand.highlight ? hexToHSL(brand.highlight) : hexToHSL(brand.secondary),\n DARK_FILL_LIGHTEN_BELOW,\n );\n const success = lightenForDark(hexToHSL(brand.success), DARK_FILL_LIGHTEN_BELOW);\n const ring = lightenForDark(primaryHsl, DARK_RING_LIGHTEN_BELOW);\n\n return {\n background: grey[\"950\"],\n foreground: grey[\"50\"],\n card: grey[\"900\"],\n \"card-foreground\": grey[\"50\"],\n \"surface-tertiary\": grey[\"800\"],\n popover: grey[\"900\"],\n \"popover-foreground\": grey[\"50\"],\n primary: hslToString(primary),\n \"primary-foreground\": contrastForeground(primary),\n secondary: hslToString(secondary),\n \"secondary-foreground\": contrastForeground(secondary),\n accent: grey[\"800\"],\n \"accent-foreground\": grey[\"50\"],\n \"brand-accent\": hslToString(accent),\n \"brand-accent-foreground\": contrastForeground(accent),\n highlight: hslToString(highlight),\n \"highlight-foreground\": contrastForeground(highlight),\n success: hslToString(success),\n \"success-foreground\": contrastForeground(success),\n muted: grey[\"800\"],\n \"muted-foreground\": grey[\"300\"],\n \"content-secondary\": grey[\"400\"],\n destructive: \"0 62% 30%\",\n \"destructive-foreground\": \"0 0% 98%\",\n border: grey[\"800\"],\n \"border-strong\": grey[\"700\"],\n \"border-subtle\": grey[\"900\"],\n input: grey[\"800\"],\n ring: hslToString(ring),\n };\n}\n\nexport function generatePaletteCSS(brand: BrandColors, attribute: \"class\" | \"data-theme\" = \"class\"): string {\n const light = deriveLightPalette(brand);\n const dark = deriveDarkPalette(brand);\n const scales = generateBrandScales(brand);\n\n const toVars = (palette: SemanticPalette, indent: string) =>\n Object.entries(palette)\n .map(([key, value]) => `${indent}--${key}: ${value};`)\n .join(\"\\n\");\n\n const darkSelector = attribute === \"class\" ? \".dark\" : '[data-theme=\"dark\"]';\n\n return `:root {\\n${toVars(light, \" \")}\\n\\n${generateScaleCSS(scales)}\\n\\n${generateDestructiveScaleCSS()}\\n}\\n\\n${darkSelector} {\\n${toVars(dark, \" \")}\\n}\\n`;\n}\n","export const radiusPresets = {\n sharp: {\n \"--radius\": \"0rem\",\n \"--radius-sm\": \"0rem\",\n \"--radius-md\": \"0rem\",\n \"--radius-lg\": \"0rem\",\n \"--radius-xl\": \"0rem\",\n \"--radius-full\": \"0rem\",\n },\n subtle: {\n \"--radius\": \"0.375rem\",\n \"--radius-sm\": \"0.375rem\",\n \"--radius-md\": \"0.375rem\",\n \"--radius-lg\": \"0.5rem\",\n \"--radius-xl\": \"0.75rem\",\n \"--radius-full\": \"9999px\",\n },\n rounded: {\n \"--radius\": \"0.5rem\",\n \"--radius-sm\": \"0.5rem\",\n \"--radius-md\": \"0.5rem\",\n \"--radius-lg\": \"0.75rem\",\n \"--radius-xl\": \"1rem\",\n \"--radius-full\": \"9999px\",\n },\n pill: {\n \"--radius\": \"0.75rem\",\n \"--radius-sm\": \"0.75rem\",\n \"--radius-md\": \"0.75rem\",\n \"--radius-lg\": \"1rem\",\n \"--radius-xl\": \"1.5rem\",\n \"--radius-full\": \"9999px\",\n },\n} as const;\n\nexport type RadiusPreset = keyof typeof radiusPresets;\n","export const shadowPresets = {\n flat: {\n \"--shadow-sm\": \"none\",\n \"--shadow\": \"none\",\n \"--shadow-md\": \"none\",\n \"--shadow-lg\": \"none\",\n \"--shadow-xl\": \"none\",\n },\n subtle: {\n \"--shadow-sm\": \"0 1px 2px 0 rgb(0 0 0 / 0.03)\",\n \"--shadow\": \"0 1px 3px 0 rgb(0 0 0 / 0.05), 0 1px 2px -1px rgb(0 0 0 / 0.05)\",\n \"--shadow-md\": \"0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05)\",\n \"--shadow-lg\": \"0 10px 15px -3px rgb(0 0 0 / 0.05), 0 4px 6px -4px rgb(0 0 0 / 0.05)\",\n \"--shadow-xl\": \"0 20px 25px -5px rgb(0 0 0 / 0.05), 0 8px 10px -6px rgb(0 0 0 / 0.05)\",\n },\n elevated: {\n \"--shadow-sm\": \"0 1px 2px 0 rgb(0 0 0 / 0.06), 0 1px 3px 0 rgb(0 0 0 / 0.1)\",\n \"--shadow\": \"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)\",\n \"--shadow-md\": \"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)\",\n \"--shadow-lg\": \"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)\",\n \"--shadow-xl\": \"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)\",\n },\n} as const;\n\nexport type ShadowPreset = keyof typeof shadowPresets;\n","export const motionPresets = {\n snappy: {\n \"--duration-fast\": \"100ms\",\n \"--duration-normal\": \"150ms\",\n \"--duration-slow\": \"250ms\",\n \"--ease-default\": \"cubic-bezier(0.22, 1, 0.36, 1)\",\n \"--ease-in\": \"cubic-bezier(0.55, 0, 1, 0.45)\",\n \"--ease-out\": \"cubic-bezier(0, 0.55, 0.45, 1)\",\n \"--ease-in-out\": \"cubic-bezier(0.65, 0, 0.35, 1)\",\n },\n smooth: {\n \"--duration-fast\": \"150ms\",\n \"--duration-normal\": \"200ms\",\n \"--duration-slow\": \"350ms\",\n \"--ease-default\": \"cubic-bezier(0.25, 0.1, 0.25, 1)\",\n \"--ease-in\": \"cubic-bezier(0.42, 0, 1, 1)\",\n \"--ease-out\": \"cubic-bezier(0, 0, 0.58, 1)\",\n \"--ease-in-out\": \"cubic-bezier(0.42, 0, 0.58, 1)\",\n },\n minimal: {\n \"--duration-fast\": \"75ms\",\n \"--duration-normal\": \"100ms\",\n \"--duration-slow\": \"200ms\",\n \"--ease-default\": \"linear\",\n \"--ease-in\": \"linear\",\n \"--ease-out\": \"linear\",\n \"--ease-in-out\": \"linear\",\n },\n} as const;\n\nexport type MotionPreset = keyof typeof motionPresets;\n","export const densityPresets = {\n compact: {\n \"--density-padding-xs\": \"0.125rem\",\n \"--density-padding-sm\": \"0.25rem\",\n \"--density-padding-md\": \"0.375rem\",\n \"--density-padding-lg\": \"0.5rem\",\n \"--density-padding-xl\": \"0.75rem\",\n \"--density-gap-sm\": \"0.25rem\",\n \"--density-gap-md\": \"0.5rem\",\n \"--density-gap-lg\": \"0.75rem\",\n \"--density-height-sm\": \"1.75rem\",\n \"--density-height-md\": \"2rem\",\n \"--density-height-lg\": \"2.5rem\",\n },\n default: {\n \"--density-padding-xs\": \"0.25rem\",\n \"--density-padding-sm\": \"0.5rem\",\n \"--density-padding-md\": \"0.75rem\",\n \"--density-padding-lg\": \"1rem\",\n \"--density-padding-xl\": \"1.5rem\",\n \"--density-gap-sm\": \"0.5rem\",\n \"--density-gap-md\": \"0.75rem\",\n \"--density-gap-lg\": \"1rem\",\n \"--density-height-sm\": \"2rem\",\n \"--density-height-md\": \"2.5rem\",\n \"--density-height-lg\": \"3rem\",\n },\n spacious: {\n \"--density-padding-xs\": \"0.5rem\",\n \"--density-padding-sm\": \"0.75rem\",\n \"--density-padding-md\": \"1rem\",\n \"--density-padding-lg\": \"1.5rem\",\n \"--density-padding-xl\": \"2rem\",\n \"--density-gap-sm\": \"0.75rem\",\n \"--density-gap-md\": \"1rem\",\n \"--density-gap-lg\": \"1.5rem\",\n \"--density-height-sm\": \"2.5rem\",\n \"--density-height-md\": \"3rem\",\n \"--density-height-lg\": \"3.5rem\",\n },\n} as const;\n\nexport type DensityPreset = keyof typeof densityPresets;\n","/** Locked type scale — consumers cannot override sizes, only font-family and weights. */\nexport const typeScale = {\n \"text-xs\": { fontSize: \"0.75rem\", lineHeight: \"1rem\" },\n \"text-sm\": { fontSize: \"0.875rem\", lineHeight: \"1.25rem\" },\n \"text-base\": { fontSize: \"1rem\", lineHeight: \"1.5rem\" },\n \"text-lg\": { fontSize: \"1.125rem\", lineHeight: \"1.75rem\" },\n \"text-xl\": { fontSize: \"1.25rem\", lineHeight: \"1.75rem\" },\n \"text-2xl\": { fontSize: \"1.5rem\", lineHeight: \"2rem\" },\n \"text-3xl\": { fontSize: \"1.875rem\", lineHeight: \"2.25rem\" },\n \"text-4xl\": { fontSize: \"2.25rem\", lineHeight: \"2.5rem\" },\n\n // Fluid headings using clamp()\n \"heading-sm\": {\n fontSize: \"clamp(1.125rem, 1rem + 0.5vw, 1.25rem)\",\n lineHeight: \"1.4\",\n letterSpacing: \"-0.01em\",\n },\n \"heading-md\": {\n fontSize: \"clamp(1.25rem, 1rem + 1vw, 1.875rem)\",\n lineHeight: \"1.3\",\n letterSpacing: \"-0.015em\",\n },\n \"heading-lg\": {\n fontSize: \"clamp(1.5rem, 1rem + 2vw, 2.25rem)\",\n lineHeight: \"1.2\",\n letterSpacing: \"-0.02em\",\n },\n \"heading-xl\": {\n fontSize: \"clamp(1.875rem, 1rem + 3vw, 3rem)\",\n lineHeight: \"1.1\",\n letterSpacing: \"-0.025em\",\n },\n \"heading-2xl\": {\n fontSize: \"clamp(2.25rem, 1rem + 4vw, 3.75rem)\",\n lineHeight: \"1.1\",\n letterSpacing: \"-0.03em\",\n },\n} as const;\n\nexport interface FontWeightMap {\n normal: number;\n medium: number;\n semibold: number;\n bold: number;\n extrabold?: number;\n}\n\nexport const defaultFontWeights: FontWeightMap = {\n normal: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n extrabold: 800,\n};\n","/** Locked spacing scale — 4px (0.25rem) base grid. Not consumer-overridable. */\nexport const spacingScale = {\n \"0\": \"0\",\n \"0.5\": \"0.125rem\",\n \"1\": \"0.25rem\",\n \"1.5\": \"0.375rem\",\n \"2\": \"0.5rem\",\n \"2.5\": \"0.625rem\",\n \"3\": \"0.75rem\",\n \"3.5\": \"0.875rem\",\n \"4\": \"1rem\",\n \"5\": \"1.25rem\",\n \"6\": \"1.5rem\",\n \"7\": \"1.75rem\",\n \"8\": \"2rem\",\n \"9\": \"2.25rem\",\n \"10\": \"2.5rem\",\n \"11\": \"2.75rem\",\n \"12\": \"3rem\",\n \"14\": \"3.5rem\",\n \"16\": \"4rem\",\n \"20\": \"5rem\",\n \"24\": \"6rem\",\n \"28\": \"7rem\",\n \"32\": \"8rem\",\n \"36\": \"9rem\",\n \"40\": \"10rem\",\n \"44\": \"11rem\",\n \"48\": \"12rem\",\n \"52\": \"13rem\",\n \"56\": \"14rem\",\n \"60\": \"15rem\",\n \"64\": \"16rem\",\n \"72\": \"18rem\",\n \"80\": \"20rem\",\n \"96\": \"24rem\",\n} as const;\n","/** Z-index tokens — overridable by consumers via CSS custom properties. */\nexport const zIndexTokens = {\n \"--z-dropdown\": \"50\",\n \"--z-sticky\": \"100\",\n \"--z-fixed\": \"200\",\n \"--z-modal-backdrop\": \"300\",\n \"--z-modal\": \"400\",\n \"--z-popover\": \"500\",\n \"--z-tooltip\": \"600\",\n \"--z-toast\": \"700\",\n} as const;\n\nexport type ZIndexToken = keyof typeof zIndexTokens;\n","/**\n * Framer Motion animation presets.\n *\n * Each preset is a Framer Motion `Variants` object with `hidden` and `visible`\n * states, plus optional `exit`. Durations and easings are resolved per motion\n * preset (snappy / smooth / minimal) at call time via `getAnimationPresets()`.\n */\n\nimport { motionPresets, type MotionPreset } from \"./motion\";\n\n// ---------------------------------------------------------------------------\n// Duration / easing helpers\n// ---------------------------------------------------------------------------\n\nfunction parseDuration(ms: string): number {\n return parseInt(ms, 10) / 1000;\n}\n\nfunction parseEasing(raw: string): number[] | string {\n if (raw === \"linear\") return \"linear\" as string;\n const match = raw.match(\n /cubic-bezier\\(\\s*([\\d.]+)\\s*,\\s*([\\d.-]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.-]+)\\s*\\)/\n );\n if (!match) return [0.25, 0.1, 0.25, 1];\n return [Number(match[1]), Number(match[2]), Number(match[3]), Number(match[4])];\n}\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface MotionVariant {\n hidden: Record<string, unknown>;\n visible: Record<string, unknown>;\n exit?: Record<string, unknown>;\n}\n\nexport interface StaggerContainerVariant {\n hidden: Record<string, unknown>;\n visible: Record<string, unknown>;\n}\n\nexport interface AnimationPresets {\n fadeIn: MotionVariant;\n fadeUp: MotionVariant;\n fadeDown: MotionVariant;\n scaleIn: MotionVariant;\n slideInLeft: MotionVariant;\n slideInRight: MotionVariant;\n cardLift: MotionVariant;\n staggerContainer: StaggerContainerVariant;\n staggerItem: MotionVariant;\n pageTransition: MotionVariant;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Returns Framer Motion variant presets tuned to the given motion preset.\n *\n * @example\n * ```tsx\n * import { getAnimationPresets } from \"@bison-lab/tokens\";\n * import { motion } from \"framer-motion\";\n *\n * const presets = getAnimationPresets(\"smooth\");\n *\n * <motion.div variants={presets.fadeUp} initial=\"hidden\" animate=\"visible\">\n * Hello\n * </motion.div>\n * ```\n */\nexport function getAnimationPresets(preset: MotionPreset = \"smooth\"): AnimationPresets {\n const tokens = motionPresets[preset];\n const normal = parseDuration(tokens[\"--duration-normal\"]);\n const slow = parseDuration(tokens[\"--duration-slow\"]);\n const ease = parseEasing(tokens[\"--ease-out\"]);\n\n const transition = { duration: normal, ease };\n const slowTransition = { duration: slow, ease };\n\n return {\n fadeIn: {\n hidden: { opacity: 0 },\n visible: { opacity: 1, transition },\n },\n\n fadeUp: {\n hidden: { opacity: 0, y: 10 },\n visible: { opacity: 1, y: 0, transition },\n },\n\n fadeDown: {\n hidden: { opacity: 0, y: -10 },\n visible: { opacity: 1, y: 0, transition },\n },\n\n scaleIn: {\n hidden: { opacity: 0, scale: 0.97 },\n visible: { opacity: 1, scale: 1, transition },\n },\n\n slideInLeft: {\n hidden: { opacity: 0, x: -16 },\n visible: { opacity: 1, x: 0, transition },\n },\n\n slideInRight: {\n hidden: { opacity: 0, x: 16 },\n visible: { opacity: 1, x: 0, transition },\n },\n\n cardLift: {\n hidden: { opacity: 0, y: 6, scale: 0.99 },\n visible: {\n opacity: 1,\n y: 0,\n scale: 1,\n transition: slowTransition,\n },\n exit: { opacity: 0, y: 4, scale: 0.99, transition },\n },\n\n staggerContainer: {\n hidden: {},\n visible: {\n transition: {\n staggerChildren: 0.07,\n delayChildren: 0.1,\n },\n },\n },\n\n staggerItem: {\n hidden: { opacity: 0, y: 8 },\n visible: { opacity: 1, y: 0, transition },\n },\n\n pageTransition: {\n hidden: { opacity: 0, y: 8 },\n visible: { opacity: 1, y: 0, transition: slowTransition },\n exit: { opacity: 0, y: -6, transition },\n },\n };\n}\n","/**\n * The theme a site configures, and the stylesheet built from it.\n *\n * `ThemeConfig` is the `bison.config.json` shape `@bison-lab/create-theme`\n * writes. `buildThemeCss` turns one into the complete theme stylesheet: the\n * CLI writes it to `bison-theme.css`, and a CMS theme (BIS-60) renders it at\n * request time from the same function, so the two can never disagree.\n */\n\nimport {\n deriveLightPalette,\n deriveDarkPalette,\n generateBrandScales,\n generateScaleCSS,\n generateDestructiveScaleCSS,\n type BrandColors,\n type GreyScale,\n type SemanticPalette,\n} from \"./colors\";\nimport { radiusPresets, type RadiusPreset } from \"./radius\";\nimport { shadowPresets, type ShadowPreset } from \"./shadows\";\nimport { motionPresets, type MotionPreset } from \"./motion\";\nimport { densityPresets, type DensityPreset } from \"./density\";\n\nexport interface ThemeConfig {\n /** `#rrggbb` */\n brandPrimary: string;\n brandSecondary: string;\n brandAccent: string;\n brandHighlight: string;\n brandSuccess: string;\n greyScale: GreyScale;\n /**\n * The selector the dark block is written under, or `\"@media\"` for a\n * `prefers-color-scheme` query. Defaults to `[data-theme=\"dark\"]`, which\n * is what `ThemeProvider` sets and the Tailwind preset's `dark:` variant\n * reads.\n */\n darkSelector?: string;\n defaultTheme: \"light\" | \"dark\" | \"system\";\n radius: RadiusPreset;\n shadow: ShadowPreset;\n motion: MotionPreset;\n density: DensityPreset;\n /** A font family name. Becomes a catalogue id in BIS-58. */\n fontBody: string;\n /** A font family name, or `null` to use `fontBody` for headings. */\n fontHeading: string | null;\n fontWeights: {\n normal: number;\n medium: number;\n semibold: number;\n bold: number;\n };\n}\n\nexport interface BuildThemeCssOptions {\n /**\n * Overrides `config.darkSelector`: `\"class\"` writes the dark block under\n * `.dark`, `\"data-theme\"` under `[data-theme=\"dark\"]`.\n */\n attribute?: \"class\" | \"data-theme\";\n}\n\nexport const DEFAULT_DARK_SELECTOR = '[data-theme=\"dark\"]';\n\n/** Whether a value is the six-digit hex colour a theme field must hold. */\nexport function isThemeHex(value: unknown): value is string {\n return typeof value === \"string\" && /^#[0-9a-f]{6}$/i.test(value);\n}\n\ntype ThemeColorFields = Pick<\n ThemeConfig,\n \"brandPrimary\" | \"brandSecondary\" | \"brandAccent\" | \"brandHighlight\" | \"brandSuccess\" | \"greyScale\"\n>;\n\nconst BRAND_FIELDS = [\n \"brandPrimary\",\n \"brandSecondary\",\n \"brandAccent\",\n \"brandHighlight\",\n \"brandSuccess\",\n] as const satisfies readonly (keyof ThemeColorFields)[];\n\n/**\n * Refuses a config whose brand colours are not `#rrggbb`, naming the field.\n * `hexToHSL` reads fixed substrings, so a short or malformed hex would\n * otherwise become `NaN` in the palette without a word: `buildThemeCss`\n * would emit a variable the browser drops, and a five-digit hex would\n * measure a plausible, wrong contrast ratio.\n */\nfunction assertThemeColors(config: ThemeColorFields): void {\n for (const field of BRAND_FIELDS) {\n const value = config[field];\n if (!isThemeHex(value)) {\n throw new Error(`${field} must be a six-digit hex colour like #1e3a5f, got ${JSON.stringify(value)}`);\n }\n }\n}\n\n/** The `BrandColors` a config's five hexes and grey scale describe. Throws on a hex that is not `#rrggbb`. */\nexport function brandColorsFromConfig(config: ThemeColorFields): BrandColors {\n assertThemeColors(config);\n return {\n primary: config.brandPrimary,\n secondary: config.brandSecondary,\n accent: config.brandAccent,\n highlight: config.brandHighlight,\n success: config.brandSuccess,\n grey: config.greyScale,\n };\n}\n\nfunction resolveDarkSelector(config: ThemeConfig, options?: BuildThemeCssOptions): string {\n if (options?.attribute === \"class\") return \".dark\";\n if (options?.attribute === \"data-theme\") return DEFAULT_DARK_SELECTOR;\n return config.darkSelector?.trim() || DEFAULT_DARK_SELECTOR;\n}\n\nfunction paletteLines(palette: SemanticPalette, indent: string): string[] {\n return Object.entries(palette).map(([key, value]) => `${indent}--${key}: ${value};`);\n}\n\nfunction presetLines(vars: Record<string, string>): string[] {\n return Object.entries(vars).map(([key, value]) => ` ${key}: ${value};`);\n}\n\n/**\n * The complete theme stylesheet for a config: the light semantic palette,\n * the brand and destructive scales, the four preset variable blocks, the\n * typography variables, and the dark palette under the dark selector.\n *\n * The preset blocks mean a site needs only this file and `base.css`; the\n * preset files under `css/themes/` stay for consumers composing their own\n * theme, and importing both is harmless.\n *\n * Throws, naming the field, when a brand colour is not `#rrggbb`; a CMS\n * that stored a bad value fails loudly rather than shipping a stylesheet\n * with a `NaN` in it. A blank `darkSelector` means the default.\n *\n * Until BIS-58 lands the font catalogue, `fontBody` and `fontHeading` are\n * family names quoted straight into `--font-body` / `--font-heading`, as the\n * CLI has always written them.\n */\nexport function buildThemeCss(config: ThemeConfig, options?: BuildThemeCssOptions): string {\n const brand = brandColorsFromConfig(config);\n const light = deriveLightPalette(brand);\n const dark = deriveDarkPalette(brand);\n const scales = generateBrandScales(brand);\n const darkSelector = resolveDarkSelector(config, options);\n\n const darkBlock =\n darkSelector === \"@media\"\n ? [\n `@media (prefers-color-scheme: dark) {`,\n ` :root {`,\n ...paletteLines(dark, \" \"),\n ` }`,\n `}`,\n ]\n : [`${darkSelector} {`, ...paletteLines(dark, \" \"), `}`];\n\n const lines: string[] = [\n `/* Bison Lab Theme — generated by @bison-lab/tokens buildThemeCss. Do not hand-edit; regenerate from bison.config.json. */`,\n `/* Brand: ${config.brandPrimary} / ${config.brandSecondary} / ${config.brandAccent} / ${config.brandHighlight} / ${config.brandSuccess} */`,\n `/* Grey: ${config.greyScale} */`,\n `/* Presets: radius ${config.radius} · shadow ${config.shadow} · motion ${config.motion} · density ${config.density} */`,\n ``,\n `:root {`,\n ...paletteLines(light, \" \"),\n ``,\n ` /* Brand color scales (50–950) */`,\n generateScaleCSS(scales),\n ``,\n ` /* Destructive scale (50–950) — soft danger surfaces */`,\n generateDestructiveScaleCSS(),\n ``,\n ` /* Radius preset: ${config.radius} */`,\n ...presetLines(radiusPresets[config.radius]),\n ``,\n ` /* Shadow preset: ${config.shadow} */`,\n ...presetLines(shadowPresets[config.shadow]),\n ``,\n ` /* Motion preset: ${config.motion} */`,\n ...presetLines(motionPresets[config.motion]),\n ``,\n ` /* Density preset: ${config.density} */`,\n ...presetLines(densityPresets[config.density]),\n ``,\n ` /* Typography */`,\n ` --font-body: '${config.fontBody}', ui-sans-serif, system-ui, sans-serif;`,\n ` --font-heading: '${config.fontHeading ?? config.fontBody}', ui-sans-serif, system-ui, sans-serif;`,\n ` --font-weight-normal: ${config.fontWeights.normal};`,\n ` --font-weight-medium: ${config.fontWeights.medium};`,\n ` --font-weight-semibold: ${config.fontWeights.semibold};`,\n ` --font-weight-bold: ${config.fontWeights.bold};`,\n ` --font-weight-extrabold: 800;`,\n `}`,\n ``,\n ...darkBlock,\n ``,\n ];\n\n return lines.join(\"\\n\");\n}\n","/**\n * WCAG contrast, measured on the palette a theme derives.\n *\n * Pure functions with no DOM, so the Theme admin (BIS-60) can run them in\n * the browser as the editor types. `auditTheme` returns data only; the\n * `ContrastStrip` that draws it is BIS-59.\n */\n\nimport { deriveLightPalette, deriveDarkPalette, type SemanticPalette } from \"./colors\";\nimport { brandColorsFromConfig, type ThemeConfig } from \"./theme\";\n\n/** `#rrggbb`, or the token form the palettes use: `\"h s% l%\"`. */\nexport type ColorInput = string;\n\nexport type ContrastLevel = \"fail\" | \"aa-large\" | \"aa\" | \"aaa\";\n\n/**\n * What the pairing carries. `text` and `fill-with-text` are held to the\n * body text target; `large-text` to 4.5 under a 7:1 target, else 3;\n * `non-text` (a focus ring, a border) to WCAG 1.4.11's single 3:1 tier.\n */\nexport type ContrastRole = \"text\" | \"large-text\" | \"fill-with-text\" | \"non-text\";\n\nexport interface ContrastRating {\n aa: boolean;\n aaLarge: boolean;\n aaa: boolean;\n level: ContrastLevel;\n}\n\nexport interface ContrastSample {\n ratio: number;\n /** The WCAG text rating of `ratio`, whatever the role. */\n level: ContrastLevel;\n /** The measured colours, in the token form, so a renderer can draw them. */\n foreground: string;\n background: string;\n}\n\nexport interface ContrastCheck {\n /** Stable: `<foreground-token>-on-<background-token>`. */\n id: string;\n /** What the pairing affects, as advice: \"Secondary as eyebrow text on the page background\". */\n label: string;\n role: ContrastRole;\n light: ContrastSample;\n dark: ContrastSample;\n /** The threshold this check was judged against, after the role's rule. */\n target: number;\n /** Both modes at or above `target`. */\n meetsTarget: boolean;\n}\n\nexport type ContrastThemeInput = Pick<\n ThemeConfig,\n \"brandPrimary\" | \"brandSecondary\" | \"brandAccent\" | \"brandHighlight\" | \"brandSuccess\" | \"greyScale\"\n>;\n\nexport interface AuditThemeOptions {\n /** The body text target. 4.5 is AA; a site with an older audience passes 7. */\n target?: number;\n}\n\n// --- Colour parsing ---\n\ntype Rgb = [number, number, number];\n\nfunction hslToRgb(h: number, s: number, l: number): Rgb {\n const chroma = (1 - Math.abs(2 * l - 1)) * s;\n const hue = (((h % 360) + 360) % 360) / 60;\n const x = chroma * (1 - Math.abs((hue % 2) - 1));\n const m = l - chroma / 2;\n let rgb: Rgb;\n if (hue < 1) rgb = [chroma, x, 0];\n else if (hue < 2) rgb = [x, chroma, 0];\n else if (hue < 3) rgb = [0, chroma, x];\n else if (hue < 4) rgb = [0, x, chroma];\n else if (hue < 5) rgb = [x, 0, chroma];\n else rgb = [chroma, 0, x];\n return [rgb[0] + m, rgb[1] + m, rgb[2] + m];\n}\n\nfunction parseColor(input: ColorInput): Rgb {\n const hex = /^#([0-9a-f]{6})$/i.exec(input.trim());\n if (hex) {\n const value = parseInt(hex[1], 16);\n return [(value >> 16) & 255, (value >> 8) & 255, value & 255].map((channel) => channel / 255) as Rgb;\n }\n const triplet = /^(-?[\\d.]+)\\s+([\\d.]+)%\\s+([\\d.]+)%$/.exec(input.trim());\n if (triplet) {\n return hslToRgb(Number(triplet[1]), Number(triplet[2]) / 100, Number(triplet[3]) / 100);\n }\n throw new Error(`Not a colour this module reads: \"${input}\". Pass #rrggbb or \"h s% l%\".`);\n}\n\n// --- WCAG 2.x ---\n\nfunction linearChannel(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\n/** WCAG relative luminance, 0 for black to 1 for white. */\nexport function relativeLuminance(color: ColorInput): number {\n const [r, g, b] = parseColor(color);\n return 0.2126 * linearChannel(r) + 0.7152 * linearChannel(g) + 0.0722 * linearChannel(b);\n}\n\n/** WCAG contrast ratio between two colours, 1 to 21, to two decimal places. Order does not matter. */\nexport function contrastRatio(a: ColorInput, b: ColorInput): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const ratio = (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n return Math.round(ratio * 100) / 100;\n}\n\n/** The WCAG text ratings a ratio earns: 3:1 large text, 4.5:1 AA, 7:1 AAA. */\nexport function rateContrast(ratio: number): ContrastRating {\n const aaLarge = ratio >= 3;\n const aa = ratio >= 4.5;\n const aaa = ratio >= 7;\n return { aa, aaLarge, aaa, level: aaa ? \"aaa\" : aa ? \"aa\" : aaLarge ? \"aa-large\" : \"fail\" };\n}\n\n// --- The audit ---\n\ntype PaletteToken = keyof SemanticPalette;\n\ninterface Pairing {\n foreground: PaletteToken;\n background: PaletteToken;\n role: ContrastRole;\n label: string;\n}\n\n/** Every pairing in the system that carries text, plus the focus ring. */\nconst PAIRINGS: readonly Pairing[] = [\n { foreground: \"primary-foreground\", background: \"primary\", role: \"fill-with-text\", label: \"Primary button label on the primary fill\" },\n { foreground: \"secondary-foreground\", background: \"secondary\", role: \"fill-with-text\", label: \"Secondary button label on the secondary fill\" },\n { foreground: \"brand-accent-foreground\", background: \"brand-accent\", role: \"fill-with-text\", label: \"Copy on a brand-accent band\" },\n { foreground: \"highlight-foreground\", background: \"highlight\", role: \"fill-with-text\", label: \"Copy on a highlight fill\" },\n { foreground: \"success-foreground\", background: \"success\", role: \"fill-with-text\", label: \"Success badge label on its fill\" },\n { foreground: \"destructive-foreground\", background: \"destructive\", role: \"fill-with-text\", label: \"Destructive button label on its fill\" },\n { foreground: \"accent-foreground\", background: \"accent\", role: \"fill-with-text\", label: \"Hovered and selected item text on the accent surface\" },\n { foreground: \"muted-foreground\", background: \"muted\", role: \"fill-with-text\", label: \"Hint text on a muted surface\" },\n { foreground: \"card-foreground\", background: \"card\", role: \"fill-with-text\", label: \"Card body text on the card surface\" },\n { foreground: \"popover-foreground\", background: \"popover\", role: \"fill-with-text\", label: \"Menu and popover text on the popover surface\" },\n { foreground: \"foreground\", background: \"background\", role: \"text\", label: \"Body text on the page background\" },\n { foreground: \"foreground\", background: \"card\", role: \"text\", label: \"Body text on a card\" },\n { foreground: \"muted-foreground\", background: \"background\", role: \"text\", label: \"Supporting text on the page background\" },\n { foreground: \"muted-foreground\", background: \"card\", role: \"text\", label: \"Supporting text on a card\" },\n { foreground: \"content-secondary\", background: \"background\", role: \"text\", label: \"Chart axis labels and tertiary text on the page background\" },\n { foreground: \"content-secondary\", background: \"card\", role: \"text\", label: \"Chart axis labels and tertiary text on a card\" },\n { foreground: \"primary\", background: \"background\", role: \"text\", label: \"Primary as link text on the page background\" },\n { foreground: \"secondary\", background: \"background\", role: \"text\", label: \"Secondary as eyebrow text on the page background\" },\n { foreground: \"ring\", background: \"background\", role: \"non-text\", label: \"Focus ring on the page background\" },\n];\n\nfunction roleTarget(role: ContrastRole, target: number): number {\n switch (role) {\n case \"non-text\":\n return 3;\n case \"large-text\":\n return target >= 7 ? 4.5 : 3;\n default:\n return target;\n }\n}\n\nfunction sample(palette: SemanticPalette, pairing: Pairing): ContrastSample {\n const foreground = palette[pairing.foreground];\n const background = palette[pairing.background];\n const ratio = contrastRatio(foreground, background);\n return { ratio, level: rateContrast(ratio).level, foreground, background };\n}\n\n/**\n * Measures every text-carrying pairing the palette derives for these\n * colours, in light and dark. Advice, not enforcement: nothing here refuses\n * a theme on its ratios. The checks are data; BIS-59's `ContrastStrip` draws\n * them.\n *\n * Throws, naming the field, when a brand colour is not `#rrggbb`, the same\n * contract as `buildThemeCss`. An admin field validates its own value\n * before calling this, so a half-typed hex never reaches it.\n */\nexport function auditTheme(config: ContrastThemeInput, options: AuditThemeOptions = {}): ContrastCheck[] {\n const target = options.target ?? 4.5;\n const brand = brandColorsFromConfig(config);\n const light = deriveLightPalette(brand);\n const dark = deriveDarkPalette(brand);\n\n return PAIRINGS.map((pairing) => {\n const lightSample = sample(light, pairing);\n const darkSample = sample(dark, pairing);\n const threshold = roleTarget(pairing.role, target);\n return {\n id: `${pairing.foreground}-on-${pairing.background}`,\n label: pairing.label,\n role: pairing.role,\n light: lightSample,\n dark: darkSample,\n target: threshold,\n meetsTarget: lightSample.ratio >= threshold && darkSample.ratio >= threshold,\n };\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/oklch.ts","../src/colors.ts","../src/radius.ts","../src/shadows.ts","../src/motion.ts","../src/density.ts","../src/typography.ts","../src/spacing.ts","../src/z-index.ts","../src/animation-presets.ts","../src/theme.ts","../src/contrast.ts","../src/preset-hints.ts"],"sourcesContent":["/**\n * OKLCH color space math — zero dependencies.\n * hex → sRGB → linear sRGB → OKLab (matrix) → OKLCH (polar)\n * Reverse: OKLCH → OKLab → linear sRGB → sRGB → HSL string\n * Gamut clamping via binary search on chroma (12 iterations).\n */\n\nexport interface OKLCH {\n L: number; // lightness 0–1\n C: number; // chroma ≥ 0\n H: number; // hue 0–360\n}\n\n// --- sRGB ↔ linear sRGB ---\n\nfunction srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(c: number): number {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;\n}\n\n// --- Hex → sRGB ---\n\nfunction hexToSrgb(hex: string): [number, number, number] {\n hex = hex.replace(\"#\", \"\");\n return [\n parseInt(hex.substring(0, 2), 16) / 255,\n parseInt(hex.substring(2, 4), 16) / 255,\n parseInt(hex.substring(4, 6), 16) / 255,\n ];\n}\n\n// --- linear sRGB ↔ OKLab ---\n\nfunction linearSrgbToOklab(r: number, g: number, b: number): [number, number, number] {\n const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;\n const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;\n const s = 0.0883024619 * r + 0.2024326059 * g + 0.6892649272 * b;\n\n const lc = Math.cbrt(l);\n const mc = Math.cbrt(m);\n const sc = Math.cbrt(s);\n\n return [\n 0.2104542553 * lc + 0.7936177850 * mc - 0.0040720468 * sc,\n 1.9779984951 * lc - 2.4285922050 * mc + 0.4505937099 * sc,\n 0.0259040371 * lc + 0.7827717662 * mc - 0.8086757660 * sc,\n ];\n}\n\nfunction oklabToLinearSrgb(L: number, a: number, b: number): [number, number, number] {\n const lc = L + 0.3963377774 * a + 0.2158037573 * b;\n const mc = L - 0.1055613458 * a - 0.0638541728 * b;\n const sc = L - 0.0894841775 * a - 1.2914855480 * b;\n\n return [\n 4.0767416621 * lc * lc * lc - 3.3077115913 * mc * mc * mc + 0.2309699292 * sc * sc * sc,\n -1.2684380046 * lc * lc * lc + 2.6097574011 * mc * mc * mc - 0.3413193965 * sc * sc * sc,\n -0.0041960863 * lc * lc * lc - 0.7034186147 * mc * mc * mc + 1.7076147010 * sc * sc * sc,\n ];\n}\n\n// --- OKLab ↔ OKLCH ---\n\nfunction oklabToOklch(L: number, a: number, b: number): OKLCH {\n const C = Math.sqrt(a * a + b * b);\n let H = (Math.atan2(b, a) * 180) / Math.PI;\n if (H < 0) H += 360;\n return { L, C, H };\n}\n\nfunction oklchToOklab(L: number, C: number, H: number): [number, number, number] {\n const hRad = (H * Math.PI) / 180;\n return [L, C * Math.cos(hRad), C * Math.sin(hRad)];\n}\n\n// --- Gamut clamping ---\n\nfunction isInGamut(r: number, g: number, b: number): boolean {\n const eps = 1e-4;\n return r >= -eps && r <= 1 + eps && g >= -eps && g <= 1 + eps && b >= -eps && b <= 1 + eps;\n}\n\nfunction gamutClampChroma(L: number, C: number, H: number): number {\n if (C <= 0) return 0;\n\n const [, a, b] = oklchToOklab(L, C, H);\n const [r, g, bl] = oklabToLinearSrgb(L, a, b);\n\n if (isInGamut(r, g, bl)) return C;\n\n let lo = 0;\n let hi = C;\n\n for (let i = 0; i < 12; i++) {\n const mid = (lo + hi) / 2;\n const [, am, bm] = oklchToOklab(L, mid, H);\n const [rm, gm, bm2] = oklabToLinearSrgb(L, am, bm);\n\n if (isInGamut(rm, gm, bm2)) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n\n return lo;\n}\n\n// --- sRGB → HSL ---\n\nfunction srgbToHsl(r: number, g: number, b: number): [number, number, number] {\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n let h = 0;\n let s = 0;\n\n if (max !== min) {\n const d = max - min;\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n switch (max) {\n case r:\n h = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n break;\n case g:\n h = ((b - r) / d + 2) / 6;\n break;\n case b:\n h = ((r - g) / d + 4) / 6;\n break;\n }\n }\n\n return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];\n}\n\n// --- Public API ---\n\nexport function hexToOklch(hex: string): OKLCH {\n const [r, g, b] = hexToSrgb(hex);\n const [lr, lg, lb] = [srgbToLinear(r), srgbToLinear(g), srgbToLinear(b)];\n const [L, a, ob] = linearSrgbToOklab(lr, lg, lb);\n return oklabToOklch(L, a, ob);\n}\n\nexport function oklchToHslString(L: number, C: number, H: number): string {\n const clampedC = gamutClampChroma(L, C, H);\n const [, a, b] = oklchToOklab(L, clampedC, H);\n const [lr, lg, lb] = oklabToLinearSrgb(L, a, b);\n\n const r = Math.min(1, Math.max(0, linearToSrgb(lr)));\n const g = Math.min(1, Math.max(0, linearToSrgb(lg)));\n const bl = Math.min(1, Math.max(0, linearToSrgb(lb)));\n\n const [h, s, l] = srgbToHsl(r, g, bl);\n return `${h} ${s}% ${l}%`;\n}\n","/**\n * Color derivation engine.\n * Takes brand colors (hex) + a grey scale selection and derives\n * a full semantic palette for both light and dark modes,\n * plus 50–950 shade scales via OKLCH color space.\n */\n\nimport { hexToOklch, oklchToHslString } from \"./oklch\";\n\nexport interface HSL {\n h: number;\n s: number;\n l: number;\n}\n\nexport function hexToHSL(hex: string): HSL {\n hex = hex.replace(\"#\", \"\");\n const r = parseInt(hex.substring(0, 2), 16) / 255;\n const g = parseInt(hex.substring(2, 4), 16) / 255;\n const b = parseInt(hex.substring(4, 6), 16) / 255;\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n let h = 0;\n let s = 0;\n\n if (max !== min) {\n const d = max - min;\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n switch (max) {\n case r:\n h = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n break;\n case g:\n h = ((b - r) / d + 2) / 6;\n break;\n case b:\n h = ((r - g) / d + 4) / 6;\n break;\n }\n }\n\n return {\n h: Math.round(h * 360),\n s: Math.round(s * 100),\n l: Math.round(l * 100),\n };\n}\n\nexport function hslToString(hsl: HSL): string {\n return `${hsl.h} ${hsl.s}% ${hsl.l}%`;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n// --- Grey scale palettes (Tailwind v4 values as HSL strings) ---\n\nexport type GreyScale = \"slate\" | \"gray\" | \"zinc\" | \"neutral\" | \"stone\";\n\ntype GreyShade = \"50\" | \"100\" | \"200\" | \"300\" | \"400\" | \"500\" | \"600\" | \"700\" | \"800\" | \"900\" | \"950\";\n\ntype GreyPalette = Record<GreyShade, string>;\n\nexport const GREY_SCALES: Record<GreyScale, GreyPalette> = {\n slate: {\n \"50\": \"210 40% 98%\",\n \"100\": \"210 40% 96%\",\n \"200\": \"214 32% 91%\",\n \"300\": \"213 27% 84%\",\n \"400\": \"215 20% 65%\",\n \"500\": \"215 16% 47%\",\n \"600\": \"215 19% 35%\",\n \"700\": \"215 25% 27%\",\n \"800\": \"217 33% 17%\",\n \"900\": \"222 47% 11%\",\n \"950\": \"229 84% 5%\",\n },\n gray: {\n \"50\": \"210 20% 98%\",\n \"100\": \"220 14% 96%\",\n \"200\": \"220 13% 91%\",\n \"300\": \"216 12% 84%\",\n \"400\": \"218 11% 65%\",\n \"500\": \"220 9% 46%\",\n \"600\": \"215 14% 34%\",\n \"700\": \"217 19% 27%\",\n \"800\": \"215 28% 17%\",\n \"900\": \"221 39% 11%\",\n \"950\": \"224 71% 4%\",\n },\n zinc: {\n \"50\": \"0 0% 98%\",\n \"100\": \"240 5% 96%\",\n \"200\": \"240 6% 90%\",\n \"300\": \"240 5% 84%\",\n \"400\": \"240 5% 65%\",\n \"500\": \"240 4% 46%\",\n \"600\": \"240 5% 34%\",\n \"700\": \"240 5% 26%\",\n \"800\": \"240 4% 16%\",\n \"900\": \"240 6% 10%\",\n \"950\": \"240 10% 4%\",\n },\n neutral: {\n \"50\": \"0 0% 98%\",\n \"100\": \"0 0% 96%\",\n \"200\": \"0 0% 90%\",\n \"300\": \"0 0% 83%\",\n \"400\": \"0 0% 64%\",\n \"500\": \"0 0% 45%\",\n \"600\": \"0 0% 32%\",\n \"700\": \"0 0% 25%\",\n \"800\": \"0 0% 15%\",\n \"900\": \"0 0% 9%\",\n \"950\": \"0 0% 4%\",\n },\n stone: {\n \"50\": \"60 9% 98%\",\n \"100\": \"60 5% 96%\",\n \"200\": \"20 6% 90%\",\n \"300\": \"24 6% 83%\",\n \"400\": \"24 5% 64%\",\n \"500\": \"25 5% 45%\",\n \"600\": \"33 5% 32%\",\n \"700\": \"30 6% 25%\",\n \"800\": \"12 6% 15%\",\n \"900\": \"24 10% 10%\",\n \"950\": \"20 14% 4%\",\n },\n};\n\n// --- Brand color types ---\n\nexport interface BrandColors {\n primary: string; // hex\n secondary: string; // hex\n accent?: string; // hex\n highlight?: string; // hex\n success: string; // hex\n grey?: GreyScale; // defaults to \"slate\"\n}\n\n// --- Shade scale types ---\n\nexport const SHADE_STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const;\nexport type ShadeStep = (typeof SHADE_STEPS)[number];\nexport type ColorScale = Record<ShadeStep, string>;\n\nexport interface BrandScales {\n primary: ColorScale;\n secondary: ColorScale;\n \"brand-accent\": ColorScale;\n highlight: ColorScale;\n success: ColorScale;\n}\n\n// --- Target OKLCH lightness per step ---\n\nconst TARGET_LIGHTNESS: Record<ShadeStep, number> = {\n 50: 0.97,\n 100: 0.93,\n 200: 0.87,\n 300: 0.79,\n 400: 0.70,\n 500: 0.59,\n 600: 0.49,\n 700: 0.39,\n 800: 0.30,\n 900: 0.21,\n 950: 0.14,\n};\n\n// --- Scale generation ---\n\n/**\n * Generate a full 50–950 color scale from a single hex value.\n * Uses OKLCH for perceptual uniformity. The input color is auto-anchored\n * to its closest shade step and reproduced pixel-perfect at that step.\n */\nexport function generateColorScale(hex: string): ColorScale {\n const { L: inputL, C: inputC, H } = hexToOklch(hex);\n\n // Sine-based chroma curve: peaks at mid-lightness, falls toward extremes\n const chromaCurve = (L: number): number => Math.sin(Math.PI * L);\n const inputCurveValue = chromaCurve(inputL);\n\n // Anchor ratio so that input chroma is preserved at input lightness\n const chromaRatio = inputCurveValue > 0.01 ? inputC / inputCurveValue : inputC;\n\n const scale = {} as ColorScale;\n\n for (const step of SHADE_STEPS) {\n const targetL = TARGET_LIGHTNESS[step];\n const targetC = inputCurveValue > 0.01\n ? chromaRatio * chromaCurve(targetL)\n : inputC * chromaCurve(targetL);\n\n scale[step] = oklchToHslString(targetL, targetC, H);\n }\n\n return scale;\n}\n\n/**\n * Generate shade scales for all brand colors.\n */\nexport function generateBrandScales(brand: BrandColors): BrandScales {\n const accentHex = brand.accent ?? brand.primary;\n const highlightHex = brand.highlight ?? brand.secondary;\n\n return {\n primary: generateColorScale(brand.primary),\n secondary: generateColorScale(brand.secondary),\n \"brand-accent\": generateColorScale(accentHex),\n highlight: generateColorScale(highlightHex),\n success: generateColorScale(brand.success),\n };\n}\n\n/**\n * Generate CSS custom property declarations for all brand scales.\n * Output is indented with 2 spaces, suitable for injection into :root {}.\n */\nexport function generateScaleCSS(scales: BrandScales): string {\n const lines: string[] = [];\n\n for (const [colorName, scale] of Object.entries(scales)) {\n for (const step of SHADE_STEPS) {\n lines.push(` --${colorName}-${step}: ${(scale as ColorScale)[step]};`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Canonical red for destructive UI (matches common “danger” hue). Used for the\n * destructive 50–950 scale so tinted surfaces (e.g. soft delete buttons) work\n * like success scales, independent of brand colors.\n */\nexport const DESTRUCTIVE_SCALE_HEX = \"#ef4444\";\n\nlet destructiveColorScaleCache: ColorScale | null = null;\n\nexport function getDestructiveColorScale(): ColorScale {\n if (!destructiveColorScaleCache) {\n destructiveColorScaleCache = generateColorScale(DESTRUCTIVE_SCALE_HEX);\n }\n return destructiveColorScaleCache;\n}\n\n/** CSS custom properties `--destructive-50` … `--destructive-950` for :root injection. */\nexport function generateDestructiveScaleCSS(): string {\n const scale = getDestructiveColorScale();\n return SHADE_STEPS.map((step) => ` --destructive-${step}: ${scale[step]};`).join(\"\\n\");\n}\n\n// --- Semantic palette ---\n\nexport interface SemanticPalette {\n background: string;\n foreground: string;\n card: string;\n \"card-foreground\": string;\n \"surface-tertiary\": string;\n popover: string;\n \"popover-foreground\": string;\n primary: string;\n \"primary-foreground\": string;\n secondary: string;\n \"secondary-foreground\": string;\n accent: string;\n \"accent-foreground\": string;\n \"brand-accent\": string;\n \"brand-accent-foreground\": string;\n highlight: string;\n \"highlight-foreground\": string;\n success: string;\n \"success-foreground\": string;\n muted: string;\n \"muted-foreground\": string;\n \"content-secondary\": string;\n destructive: string;\n \"destructive-foreground\": string;\n border: string;\n \"border-strong\": string;\n \"border-subtle\": string;\n input: string;\n ring: string;\n}\n\n// --- Foreground contrast ---\n\n/**\n * Near-white ink on a dark fill, near-black on a light one. A lightness\n * heuristic, not a measurement: `auditTheme` in `./contrast` is what says\n * whether the pairing actually reads.\n */\nfunction contrastForeground(color: HSL): string {\n return color.l > 55\n ? hslToString({ h: color.h, s: clamp(color.s - 20, 0, 100), l: 10 })\n : hslToString({ h: color.h, s: clamp(color.s - 25, 0, 100), l: 98 });\n}\n\n/**\n * A brand fill lightens in dark mode only below this lightness. The CLI\n * shipped 40 and this module shipped 50 until BIS-57; 40 is the value every\n * generated `bison-theme.css` in the field was built with, and Spinal\n * Simplicity's `globals.css` records why a fill at 43 must not lighten: its\n * derived ink flips to near-black, and the button's fixed `hover:bg-primary-700`\n * then reads at 1.74:1.\n */\nconst DARK_FILL_LIGHTEN_BELOW = 40;\n\n/**\n * The focus ring lightens below this lightness. A ring is a thin outline on\n * the page background and carries no ink, so it is free to lighten where a\n * fill is not: `#DB0082` at 43% is 4.1:1 on zinc-950, at 68% it is 7.1:1.\n */\nconst DARK_RING_LIGHTEN_BELOW = 50;\n\nfunction lightenForDark(color: HSL, below: number): HSL {\n if (color.l < below) {\n return { ...color, l: clamp(color.l + 25, 0, 100) };\n }\n return color;\n}\n\n// --- Palette derivation ---\n\n/**\n * The light semantic palette for a brand. This is the one derivation the\n * CLI (`bison-theme.css`), `BisonProvider` and a CMS theme all share.\n *\n * Decided in BIS-57, where the CLI and this module had drifted:\n * - `muted-foreground` is grey-600, not grey-500. grey-500 is 4.6:1 on the\n * page background, and the token carries card descriptions, hints, table\n * labels and footer lines; grey-600 is 7.4:1. Spinal Simplicity overrode\n * it by hand for its patient audience.\n * - `content-secondary` is grey-500, not grey-400. It is text: the\n * progress bar's percentage label and every chart axis tick use it, and\n * grey-400 is about 2.5:1 on the page background on every scale. grey-500\n * is 4.5:1 or better on both the page background and a card.\n */\nexport function deriveLightPalette(brand: BrandColors): SemanticPalette {\n const grey = GREY_SCALES[brand.grey ?? \"slate\"];\n const primary = hexToHSL(brand.primary);\n const secondary = hexToHSL(brand.secondary);\n const accent = brand.accent ? hexToHSL(brand.accent) : primary;\n const highlight = brand.highlight ? hexToHSL(brand.highlight) : secondary;\n const success = hexToHSL(brand.success);\n\n return {\n background: grey[\"50\"],\n foreground: grey[\"950\"],\n card: \"0 0% 100%\",\n \"card-foreground\": grey[\"950\"],\n \"surface-tertiary\": grey[\"100\"],\n popover: \"0 0% 100%\",\n \"popover-foreground\": grey[\"950\"],\n primary: hslToString(primary),\n \"primary-foreground\": contrastForeground(primary),\n secondary: hslToString(secondary),\n \"secondary-foreground\": contrastForeground(secondary),\n accent: grey[\"100\"],\n \"accent-foreground\": grey[\"950\"],\n \"brand-accent\": hslToString(accent),\n \"brand-accent-foreground\": contrastForeground(accent),\n highlight: hslToString(highlight),\n \"highlight-foreground\": contrastForeground(highlight),\n success: hslToString(success),\n \"success-foreground\": contrastForeground(success),\n muted: grey[\"100\"],\n \"muted-foreground\": grey[\"600\"],\n \"content-secondary\": grey[\"500\"],\n destructive: \"0 84% 60%\",\n \"destructive-foreground\": \"0 0% 98%\",\n border: grey[\"200\"],\n \"border-strong\": grey[\"300\"],\n \"border-subtle\": grey[\"100\"],\n input: grey[\"200\"],\n ring: hslToString(primary),\n };\n}\n\n/**\n * The dark semantic palette for a brand, derived from the same inputs as\n * the light one: no manual dark palette.\n *\n * Decided in BIS-57, where the CLI and this module had drifted:\n * - Brand fills lighten only below lightness 40 (`DARK_FILL_LIGHTEN_BELOW`);\n * the ring lightens below 50 (`DARK_RING_LIGHTEN_BELOW`). See both.\n * - `muted-foreground` is grey-300, not grey-400. grey-400 is 6.9:1 on\n * `card`, where most supporting copy sits; grey-300 clears 7:1 on both\n * surfaces.\n * - `content-secondary` is grey-400, not grey-500. grey-500 on `card` is\n * about 3.7:1, under AA; grey-400 is about 7:1.\n * - `destructive` is `0 62% 30%`, not `0 84% 65%`. `destructive-foreground`\n * (`0 0% 98%`) is 9.8:1 on the former and 3.2:1 on the latter, which\n * fails AA for the button label.\n */\nexport function deriveDarkPalette(brand: BrandColors): SemanticPalette {\n const grey = GREY_SCALES[brand.grey ?? \"slate\"];\n const primaryHsl = hexToHSL(brand.primary);\n const primary = lightenForDark(primaryHsl, DARK_FILL_LIGHTEN_BELOW);\n const secondary = lightenForDark(hexToHSL(brand.secondary), DARK_FILL_LIGHTEN_BELOW);\n const accent = lightenForDark(brand.accent ? hexToHSL(brand.accent) : primaryHsl, DARK_FILL_LIGHTEN_BELOW);\n const highlight = lightenForDark(\n brand.highlight ? hexToHSL(brand.highlight) : hexToHSL(brand.secondary),\n DARK_FILL_LIGHTEN_BELOW,\n );\n const success = lightenForDark(hexToHSL(brand.success), DARK_FILL_LIGHTEN_BELOW);\n const ring = lightenForDark(primaryHsl, DARK_RING_LIGHTEN_BELOW);\n\n return {\n background: grey[\"950\"],\n foreground: grey[\"50\"],\n card: grey[\"900\"],\n \"card-foreground\": grey[\"50\"],\n \"surface-tertiary\": grey[\"800\"],\n popover: grey[\"900\"],\n \"popover-foreground\": grey[\"50\"],\n primary: hslToString(primary),\n \"primary-foreground\": contrastForeground(primary),\n secondary: hslToString(secondary),\n \"secondary-foreground\": contrastForeground(secondary),\n accent: grey[\"800\"],\n \"accent-foreground\": grey[\"50\"],\n \"brand-accent\": hslToString(accent),\n \"brand-accent-foreground\": contrastForeground(accent),\n highlight: hslToString(highlight),\n \"highlight-foreground\": contrastForeground(highlight),\n success: hslToString(success),\n \"success-foreground\": contrastForeground(success),\n muted: grey[\"800\"],\n \"muted-foreground\": grey[\"300\"],\n \"content-secondary\": grey[\"400\"],\n destructive: \"0 62% 30%\",\n \"destructive-foreground\": \"0 0% 98%\",\n border: grey[\"800\"],\n \"border-strong\": grey[\"700\"],\n \"border-subtle\": grey[\"900\"],\n input: grey[\"800\"],\n ring: hslToString(ring),\n };\n}\n\nexport function generatePaletteCSS(brand: BrandColors, attribute: \"class\" | \"data-theme\" = \"class\"): string {\n const light = deriveLightPalette(brand);\n const dark = deriveDarkPalette(brand);\n const scales = generateBrandScales(brand);\n\n const toVars = (palette: SemanticPalette, indent: string) =>\n Object.entries(palette)\n .map(([key, value]) => `${indent}--${key}: ${value};`)\n .join(\"\\n\");\n\n const darkSelector = attribute === \"class\" ? \".dark\" : '[data-theme=\"dark\"]';\n\n return `:root {\\n${toVars(light, \" \")}\\n\\n${generateScaleCSS(scales)}\\n\\n${generateDestructiveScaleCSS()}\\n}\\n\\n${darkSelector} {\\n${toVars(dark, \" \")}\\n}\\n`;\n}\n","export const radiusPresets = {\n sharp: {\n \"--radius\": \"0rem\",\n \"--radius-sm\": \"0rem\",\n \"--radius-md\": \"0rem\",\n \"--radius-lg\": \"0rem\",\n \"--radius-xl\": \"0rem\",\n \"--radius-full\": \"0rem\",\n },\n subtle: {\n \"--radius\": \"0.375rem\",\n \"--radius-sm\": \"0.375rem\",\n \"--radius-md\": \"0.375rem\",\n \"--radius-lg\": \"0.5rem\",\n \"--radius-xl\": \"0.75rem\",\n \"--radius-full\": \"9999px\",\n },\n rounded: {\n \"--radius\": \"0.5rem\",\n \"--radius-sm\": \"0.5rem\",\n \"--radius-md\": \"0.5rem\",\n \"--radius-lg\": \"0.75rem\",\n \"--radius-xl\": \"1rem\",\n \"--radius-full\": \"9999px\",\n },\n pill: {\n \"--radius\": \"0.75rem\",\n \"--radius-sm\": \"0.75rem\",\n \"--radius-md\": \"0.75rem\",\n \"--radius-lg\": \"1rem\",\n \"--radius-xl\": \"1.5rem\",\n \"--radius-full\": \"9999px\",\n },\n} as const;\n\nexport type RadiusPreset = keyof typeof radiusPresets;\n","export const shadowPresets = {\n flat: {\n \"--shadow-sm\": \"none\",\n \"--shadow\": \"none\",\n \"--shadow-md\": \"none\",\n \"--shadow-lg\": \"none\",\n \"--shadow-xl\": \"none\",\n },\n subtle: {\n \"--shadow-sm\": \"0 1px 2px 0 rgb(0 0 0 / 0.03)\",\n \"--shadow\": \"0 1px 3px 0 rgb(0 0 0 / 0.05), 0 1px 2px -1px rgb(0 0 0 / 0.05)\",\n \"--shadow-md\": \"0 4px 6px -1px rgb(0 0 0 / 0.05), 0 2px 4px -2px rgb(0 0 0 / 0.05)\",\n \"--shadow-lg\": \"0 10px 15px -3px rgb(0 0 0 / 0.05), 0 4px 6px -4px rgb(0 0 0 / 0.05)\",\n \"--shadow-xl\": \"0 20px 25px -5px rgb(0 0 0 / 0.05), 0 8px 10px -6px rgb(0 0 0 / 0.05)\",\n },\n elevated: {\n \"--shadow-sm\": \"0 1px 2px 0 rgb(0 0 0 / 0.06), 0 1px 3px 0 rgb(0 0 0 / 0.1)\",\n \"--shadow\": \"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)\",\n \"--shadow-md\": \"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)\",\n \"--shadow-lg\": \"0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)\",\n \"--shadow-xl\": \"0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)\",\n },\n} as const;\n\nexport type ShadowPreset = keyof typeof shadowPresets;\n","export const motionPresets = {\n snappy: {\n \"--duration-fast\": \"100ms\",\n \"--duration-normal\": \"150ms\",\n \"--duration-slow\": \"250ms\",\n \"--ease-default\": \"cubic-bezier(0.22, 1, 0.36, 1)\",\n \"--ease-in\": \"cubic-bezier(0.55, 0, 1, 0.45)\",\n \"--ease-out\": \"cubic-bezier(0, 0.55, 0.45, 1)\",\n \"--ease-in-out\": \"cubic-bezier(0.65, 0, 0.35, 1)\",\n },\n smooth: {\n \"--duration-fast\": \"150ms\",\n \"--duration-normal\": \"200ms\",\n \"--duration-slow\": \"350ms\",\n \"--ease-default\": \"cubic-bezier(0.25, 0.1, 0.25, 1)\",\n \"--ease-in\": \"cubic-bezier(0.42, 0, 1, 1)\",\n \"--ease-out\": \"cubic-bezier(0, 0, 0.58, 1)\",\n \"--ease-in-out\": \"cubic-bezier(0.42, 0, 0.58, 1)\",\n },\n minimal: {\n \"--duration-fast\": \"75ms\",\n \"--duration-normal\": \"100ms\",\n \"--duration-slow\": \"200ms\",\n \"--ease-default\": \"linear\",\n \"--ease-in\": \"linear\",\n \"--ease-out\": \"linear\",\n \"--ease-in-out\": \"linear\",\n },\n} as const;\n\nexport type MotionPreset = keyof typeof motionPresets;\n","export const densityPresets = {\n compact: {\n \"--density-padding-xs\": \"0.125rem\",\n \"--density-padding-sm\": \"0.25rem\",\n \"--density-padding-md\": \"0.375rem\",\n \"--density-padding-lg\": \"0.5rem\",\n \"--density-padding-xl\": \"0.75rem\",\n \"--density-gap-sm\": \"0.25rem\",\n \"--density-gap-md\": \"0.5rem\",\n \"--density-gap-lg\": \"0.75rem\",\n \"--density-height-sm\": \"1.75rem\",\n \"--density-height-md\": \"2rem\",\n \"--density-height-lg\": \"2.5rem\",\n },\n default: {\n \"--density-padding-xs\": \"0.25rem\",\n \"--density-padding-sm\": \"0.5rem\",\n \"--density-padding-md\": \"0.75rem\",\n \"--density-padding-lg\": \"1rem\",\n \"--density-padding-xl\": \"1.5rem\",\n \"--density-gap-sm\": \"0.5rem\",\n \"--density-gap-md\": \"0.75rem\",\n \"--density-gap-lg\": \"1rem\",\n \"--density-height-sm\": \"2rem\",\n \"--density-height-md\": \"2.5rem\",\n \"--density-height-lg\": \"3rem\",\n },\n spacious: {\n \"--density-padding-xs\": \"0.5rem\",\n \"--density-padding-sm\": \"0.75rem\",\n \"--density-padding-md\": \"1rem\",\n \"--density-padding-lg\": \"1.5rem\",\n \"--density-padding-xl\": \"2rem\",\n \"--density-gap-sm\": \"0.75rem\",\n \"--density-gap-md\": \"1rem\",\n \"--density-gap-lg\": \"1.5rem\",\n \"--density-height-sm\": \"2.5rem\",\n \"--density-height-md\": \"3rem\",\n \"--density-height-lg\": \"3.5rem\",\n },\n} as const;\n\nexport type DensityPreset = keyof typeof densityPresets;\n","/** Locked type scale — consumers cannot override sizes, only font-family and weights. */\nexport const typeScale = {\n \"text-xs\": { fontSize: \"0.75rem\", lineHeight: \"1rem\" },\n \"text-sm\": { fontSize: \"0.875rem\", lineHeight: \"1.25rem\" },\n \"text-base\": { fontSize: \"1rem\", lineHeight: \"1.5rem\" },\n \"text-lg\": { fontSize: \"1.125rem\", lineHeight: \"1.75rem\" },\n \"text-xl\": { fontSize: \"1.25rem\", lineHeight: \"1.75rem\" },\n \"text-2xl\": { fontSize: \"1.5rem\", lineHeight: \"2rem\" },\n \"text-3xl\": { fontSize: \"1.875rem\", lineHeight: \"2.25rem\" },\n \"text-4xl\": { fontSize: \"2.25rem\", lineHeight: \"2.5rem\" },\n\n // Fluid headings using clamp()\n \"heading-sm\": {\n fontSize: \"clamp(1.125rem, 1rem + 0.5vw, 1.25rem)\",\n lineHeight: \"1.4\",\n letterSpacing: \"-0.01em\",\n },\n \"heading-md\": {\n fontSize: \"clamp(1.25rem, 1rem + 1vw, 1.875rem)\",\n lineHeight: \"1.3\",\n letterSpacing: \"-0.015em\",\n },\n \"heading-lg\": {\n fontSize: \"clamp(1.5rem, 1rem + 2vw, 2.25rem)\",\n lineHeight: \"1.2\",\n letterSpacing: \"-0.02em\",\n },\n \"heading-xl\": {\n fontSize: \"clamp(1.875rem, 1rem + 3vw, 3rem)\",\n lineHeight: \"1.1\",\n letterSpacing: \"-0.025em\",\n },\n \"heading-2xl\": {\n fontSize: \"clamp(2.25rem, 1rem + 4vw, 3.75rem)\",\n lineHeight: \"1.1\",\n letterSpacing: \"-0.03em\",\n },\n} as const;\n\nexport interface FontWeightMap {\n normal: number;\n medium: number;\n semibold: number;\n bold: number;\n extrabold?: number;\n}\n\nexport const defaultFontWeights: FontWeightMap = {\n normal: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n extrabold: 800,\n};\n","/** Locked spacing scale — 4px (0.25rem) base grid. Not consumer-overridable. */\nexport const spacingScale = {\n \"0\": \"0\",\n \"0.5\": \"0.125rem\",\n \"1\": \"0.25rem\",\n \"1.5\": \"0.375rem\",\n \"2\": \"0.5rem\",\n \"2.5\": \"0.625rem\",\n \"3\": \"0.75rem\",\n \"3.5\": \"0.875rem\",\n \"4\": \"1rem\",\n \"5\": \"1.25rem\",\n \"6\": \"1.5rem\",\n \"7\": \"1.75rem\",\n \"8\": \"2rem\",\n \"9\": \"2.25rem\",\n \"10\": \"2.5rem\",\n \"11\": \"2.75rem\",\n \"12\": \"3rem\",\n \"14\": \"3.5rem\",\n \"16\": \"4rem\",\n \"20\": \"5rem\",\n \"24\": \"6rem\",\n \"28\": \"7rem\",\n \"32\": \"8rem\",\n \"36\": \"9rem\",\n \"40\": \"10rem\",\n \"44\": \"11rem\",\n \"48\": \"12rem\",\n \"52\": \"13rem\",\n \"56\": \"14rem\",\n \"60\": \"15rem\",\n \"64\": \"16rem\",\n \"72\": \"18rem\",\n \"80\": \"20rem\",\n \"96\": \"24rem\",\n} as const;\n","/** Z-index tokens — overridable by consumers via CSS custom properties. */\nexport const zIndexTokens = {\n \"--z-dropdown\": \"50\",\n \"--z-sticky\": \"100\",\n \"--z-fixed\": \"200\",\n \"--z-modal-backdrop\": \"300\",\n \"--z-modal\": \"400\",\n \"--z-popover\": \"500\",\n \"--z-tooltip\": \"600\",\n \"--z-toast\": \"700\",\n} as const;\n\nexport type ZIndexToken = keyof typeof zIndexTokens;\n","/**\n * Framer Motion animation presets.\n *\n * Each preset is a Framer Motion `Variants` object with `hidden` and `visible`\n * states, plus optional `exit`. Durations and easings are resolved per motion\n * preset (snappy / smooth / minimal) at call time via `getAnimationPresets()`.\n */\n\nimport { motionPresets, type MotionPreset } from \"./motion\";\n\n// ---------------------------------------------------------------------------\n// Duration / easing helpers\n// ---------------------------------------------------------------------------\n\nfunction parseDuration(ms: string): number {\n return parseInt(ms, 10) / 1000;\n}\n\nfunction parseEasing(raw: string): number[] | string {\n if (raw === \"linear\") return \"linear\" as string;\n const match = raw.match(\n /cubic-bezier\\(\\s*([\\d.]+)\\s*,\\s*([\\d.-]+)\\s*,\\s*([\\d.]+)\\s*,\\s*([\\d.-]+)\\s*\\)/\n );\n if (!match) return [0.25, 0.1, 0.25, 1];\n return [Number(match[1]), Number(match[2]), Number(match[3]), Number(match[4])];\n}\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface MotionVariant {\n hidden: Record<string, unknown>;\n visible: Record<string, unknown>;\n exit?: Record<string, unknown>;\n}\n\nexport interface StaggerContainerVariant {\n hidden: Record<string, unknown>;\n visible: Record<string, unknown>;\n}\n\nexport interface AnimationPresets {\n fadeIn: MotionVariant;\n fadeUp: MotionVariant;\n fadeDown: MotionVariant;\n scaleIn: MotionVariant;\n slideInLeft: MotionVariant;\n slideInRight: MotionVariant;\n cardLift: MotionVariant;\n staggerContainer: StaggerContainerVariant;\n staggerItem: MotionVariant;\n pageTransition: MotionVariant;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Returns Framer Motion variant presets tuned to the given motion preset.\n *\n * @example\n * ```tsx\n * import { getAnimationPresets } from \"@bison-lab/tokens\";\n * import { motion } from \"framer-motion\";\n *\n * const presets = getAnimationPresets(\"smooth\");\n *\n * <motion.div variants={presets.fadeUp} initial=\"hidden\" animate=\"visible\">\n * Hello\n * </motion.div>\n * ```\n */\nexport function getAnimationPresets(preset: MotionPreset = \"smooth\"): AnimationPresets {\n const tokens = motionPresets[preset];\n const normal = parseDuration(tokens[\"--duration-normal\"]);\n const slow = parseDuration(tokens[\"--duration-slow\"]);\n const ease = parseEasing(tokens[\"--ease-out\"]);\n\n const transition = { duration: normal, ease };\n const slowTransition = { duration: slow, ease };\n\n return {\n fadeIn: {\n hidden: { opacity: 0 },\n visible: { opacity: 1, transition },\n },\n\n fadeUp: {\n hidden: { opacity: 0, y: 10 },\n visible: { opacity: 1, y: 0, transition },\n },\n\n fadeDown: {\n hidden: { opacity: 0, y: -10 },\n visible: { opacity: 1, y: 0, transition },\n },\n\n scaleIn: {\n hidden: { opacity: 0, scale: 0.97 },\n visible: { opacity: 1, scale: 1, transition },\n },\n\n slideInLeft: {\n hidden: { opacity: 0, x: -16 },\n visible: { opacity: 1, x: 0, transition },\n },\n\n slideInRight: {\n hidden: { opacity: 0, x: 16 },\n visible: { opacity: 1, x: 0, transition },\n },\n\n cardLift: {\n hidden: { opacity: 0, y: 6, scale: 0.99 },\n visible: {\n opacity: 1,\n y: 0,\n scale: 1,\n transition: slowTransition,\n },\n exit: { opacity: 0, y: 4, scale: 0.99, transition },\n },\n\n staggerContainer: {\n hidden: {},\n visible: {\n transition: {\n staggerChildren: 0.07,\n delayChildren: 0.1,\n },\n },\n },\n\n staggerItem: {\n hidden: { opacity: 0, y: 8 },\n visible: { opacity: 1, y: 0, transition },\n },\n\n pageTransition: {\n hidden: { opacity: 0, y: 8 },\n visible: { opacity: 1, y: 0, transition: slowTransition },\n exit: { opacity: 0, y: -6, transition },\n },\n };\n}\n","/**\n * The theme a site configures, and the stylesheet built from it.\n *\n * `ThemeConfig` is the `bison.config.json` shape `@bison-lab/create-theme`\n * writes. `buildThemeCss` turns one into the complete theme stylesheet: the\n * CLI writes it to `bison-theme.css`, and a CMS theme (BIS-60) renders it at\n * request time from the same function, so the two can never disagree.\n */\n\nimport {\n deriveLightPalette,\n deriveDarkPalette,\n generateBrandScales,\n generateScaleCSS,\n generateDestructiveScaleCSS,\n type BrandColors,\n type GreyScale,\n type SemanticPalette,\n} from \"./colors\";\nimport { radiusPresets, type RadiusPreset } from \"./radius\";\nimport { shadowPresets, type ShadowPreset } from \"./shadows\";\nimport { motionPresets, type MotionPreset } from \"./motion\";\nimport { densityPresets, type DensityPreset } from \"./density\";\nimport { fontFaceCss, fontStack, isFontId, type FontId } from \"@bison-lab/fonts\";\n\nexport interface ThemeConfig {\n /** `#rrggbb` */\n brandPrimary: string;\n brandSecondary: string;\n brandAccent: string;\n brandHighlight: string;\n brandSuccess: string;\n greyScale: GreyScale;\n /**\n * The selector the dark block is written under, or `\"@media\"` for a\n * `prefers-color-scheme` query. Defaults to `[data-theme=\"dark\"]`, which\n * is what `ThemeProvider` sets and the Tailwind preset's `dark:` variant\n * reads.\n */\n darkSelector?: string;\n defaultTheme: \"light\" | \"dark\" | \"system\";\n radius: RadiusPreset;\n shadow: ShadowPreset;\n motion: MotionPreset;\n density: DensityPreset;\n /**\n * A catalogue id from `@bison-lab/fonts` (`\"inter\"`). A value that is not\n * one is a legacy family name from an older `bison.config.json` and is\n * quoted into the stack as-is; the CLI migrates it to its id on the next\n * run.\n */\n fontBody: string;\n /** A catalogue id, a legacy family name, or `null` to use `fontBody` for headings. */\n fontHeading: string | null;\n fontWeights: {\n normal: number;\n medium: number;\n semibold: number;\n bold: number;\n };\n}\n\nexport interface BuildThemeCssOptions {\n /**\n * Overrides `config.darkSelector`: `\"class\"` writes the dark block under\n * `.dark`, `\"data-theme\"` under `[data-theme=\"dark\"]`.\n */\n attribute?: \"class\" | \"data-theme\";\n /**\n * When set, the stylesheet opens with the `@font-face` rules for the\n * catalogue families the config names, served from this base URL. The\n * CLI passes `/fonts`; a site rendering the theme at request time may\n * prefer `fontFaceCss` in its own `<head>` and leave this unset.\n */\n fontsBaseUrl?: string;\n}\n\nexport const DEFAULT_DARK_SELECTOR = '[data-theme=\"dark\"]';\n\n/** Whether a value is the six-digit hex colour a theme field must hold. */\nexport function isThemeHex(value: unknown): value is string {\n return typeof value === \"string\" && /^#[0-9a-f]{6}$/i.test(value);\n}\n\ntype ThemeColorFields = Pick<\n ThemeConfig,\n \"brandPrimary\" | \"brandSecondary\" | \"brandAccent\" | \"brandHighlight\" | \"brandSuccess\" | \"greyScale\"\n>;\n\nconst BRAND_FIELDS = [\n \"brandPrimary\",\n \"brandSecondary\",\n \"brandAccent\",\n \"brandHighlight\",\n \"brandSuccess\",\n] as const satisfies readonly (keyof ThemeColorFields)[];\n\n/**\n * Refuses a config whose brand colours are not `#rrggbb`, naming the field.\n * `hexToHSL` reads fixed substrings, so a short or malformed hex would\n * otherwise become `NaN` in the palette without a word: `buildThemeCss`\n * would emit a variable the browser drops, and a five-digit hex would\n * measure a plausible, wrong contrast ratio.\n */\nfunction assertThemeColors(config: ThemeColorFields): void {\n for (const field of BRAND_FIELDS) {\n const value = config[field];\n if (!isThemeHex(value)) {\n throw new Error(`${field} must be a six-digit hex colour like #1e3a5f, got ${JSON.stringify(value)}`);\n }\n }\n}\n\n/** The `BrandColors` a config's five hexes and grey scale describe. Throws on a hex that is not `#rrggbb`. */\nexport function brandColorsFromConfig(config: ThemeColorFields): BrandColors {\n assertThemeColors(config);\n return {\n primary: config.brandPrimary,\n secondary: config.brandSecondary,\n accent: config.brandAccent,\n highlight: config.brandHighlight,\n success: config.brandSuccess,\n grey: config.greyScale,\n };\n}\n\nfunction resolveDarkSelector(config: ThemeConfig, options?: BuildThemeCssOptions): string {\n if (options?.attribute === \"class\") return \".dark\";\n if (options?.attribute === \"data-theme\") return DEFAULT_DARK_SELECTOR;\n return config.darkSelector?.trim() || DEFAULT_DARK_SELECTOR;\n}\n\nconst LEGACY_FONT_FALLBACK = \"ui-sans-serif, system-ui, sans-serif\";\n\n/** The `font-family` value for a config font: the catalogue stack for an id, the CLI's old quoting for a legacy name. */\nfunction fontFamilyValue(value: string): string {\n return isFontId(value) ? fontStack(value) : `'${value}', ${LEGACY_FONT_FALLBACK}`;\n}\n\n/** The catalogue ids a config names, body first, without repeats. A legacy family name is not one. */\nexport function themeFontIds(config: Pick<ThemeConfig, \"fontBody\" | \"fontHeading\">): FontId[] {\n const ids: FontId[] = [];\n for (const value of [config.fontBody, config.fontHeading ?? config.fontBody]) {\n if (isFontId(value) && !ids.includes(value)) ids.push(value);\n }\n return ids;\n}\n\nfunction paletteLines(palette: SemanticPalette, indent: string): string[] {\n return Object.entries(palette).map(([key, value]) => `${indent}--${key}: ${value};`);\n}\n\nfunction presetLines(vars: Record<string, string>): string[] {\n return Object.entries(vars).map(([key, value]) => ` ${key}: ${value};`);\n}\n\n/**\n * The complete theme stylesheet for a config: the light semantic palette,\n * the brand and destructive scales, the four preset variable blocks, the\n * typography variables, and the dark palette under the dark selector.\n *\n * The preset blocks mean a site needs only this file and `base.css`; the\n * preset files under `css/themes/` stay for consumers composing their own\n * theme, and importing both is harmless.\n *\n * Throws, naming the field, when a brand colour is not `#rrggbb`; a CMS\n * that stored a bad value fails loudly rather than shipping a stylesheet\n * with a `NaN` in it. A blank `darkSelector` means the default.\n *\n * `fontBody` / `fontHeading` are catalogue ids and become the catalogue's\n * stack; a legacy family name is quoted as the CLI always wrote it. The\n * `@font-face` rules themselves are written only when `fontsBaseUrl` is\n * given.\n */\nexport function buildThemeCss(config: ThemeConfig, options?: BuildThemeCssOptions): string {\n const brand = brandColorsFromConfig(config);\n const light = deriveLightPalette(brand);\n const dark = deriveDarkPalette(brand);\n const scales = generateBrandScales(brand);\n const darkSelector = resolveDarkSelector(config, options);\n const fontFaces = options?.fontsBaseUrl ? fontFaceCss(themeFontIds(config), options.fontsBaseUrl) : \"\";\n\n const darkBlock =\n darkSelector === \"@media\"\n ? [\n `@media (prefers-color-scheme: dark) {`,\n ` :root {`,\n ...paletteLines(dark, \" \"),\n ` }`,\n `}`,\n ]\n : [`${darkSelector} {`, ...paletteLines(dark, \" \"), `}`];\n\n const lines: string[] = [\n `/* Bison Lab Theme — generated by @bison-lab/tokens buildThemeCss. Do not hand-edit; regenerate from bison.config.json. */`,\n `/* Brand: ${config.brandPrimary} / ${config.brandSecondary} / ${config.brandAccent} / ${config.brandHighlight} / ${config.brandSuccess} */`,\n `/* Grey: ${config.greyScale} */`,\n `/* Presets: radius ${config.radius} · shadow ${config.shadow} · motion ${config.motion} · density ${config.density} */`,\n ``,\n ...(fontFaces ? [fontFaces, ``] : []),\n `:root {`,\n ...paletteLines(light, \" \"),\n ``,\n ` /* Brand color scales (50–950) */`,\n generateScaleCSS(scales),\n ``,\n ` /* Destructive scale (50–950) — soft danger surfaces */`,\n generateDestructiveScaleCSS(),\n ``,\n ` /* Radius preset: ${config.radius} */`,\n ...presetLines(radiusPresets[config.radius]),\n ``,\n ` /* Shadow preset: ${config.shadow} */`,\n ...presetLines(shadowPresets[config.shadow]),\n ``,\n ` /* Motion preset: ${config.motion} */`,\n ...presetLines(motionPresets[config.motion]),\n ``,\n ` /* Density preset: ${config.density} */`,\n ...presetLines(densityPresets[config.density]),\n ``,\n ` /* Typography */`,\n ` --font-body: ${fontFamilyValue(config.fontBody)};`,\n ` --font-heading: ${fontFamilyValue(config.fontHeading ?? config.fontBody)};`,\n ` --font-weight-normal: ${config.fontWeights.normal};`,\n ` --font-weight-medium: ${config.fontWeights.medium};`,\n ` --font-weight-semibold: ${config.fontWeights.semibold};`,\n ` --font-weight-bold: ${config.fontWeights.bold};`,\n ` --font-weight-extrabold: 800;`,\n `}`,\n ``,\n ...darkBlock,\n ``,\n ];\n\n return lines.join(\"\\n\");\n}\n","/**\n * WCAG contrast, measured on the palette a theme derives.\n *\n * Pure functions with no DOM, so the Theme admin (BIS-60) can run them in\n * the browser as the editor types. `auditTheme` returns data only; the\n * `ContrastStrip` that draws it is BIS-59.\n */\n\nimport { deriveLightPalette, deriveDarkPalette, type SemanticPalette } from \"./colors\";\nimport { brandColorsFromConfig, type ThemeConfig } from \"./theme\";\n\n/** `#rrggbb`, or the token form the palettes use: `\"h s% l%\"`. */\nexport type ColorInput = string;\n\nexport type ContrastLevel = \"fail\" | \"aa-large\" | \"aa\" | \"aaa\";\n\n/**\n * What the pairing carries. `text` and `fill-with-text` are held to the\n * body text target; `large-text` to 4.5 under a 7:1 target, else 3;\n * `non-text` (a focus ring, a border) to WCAG 1.4.11's single 3:1 tier.\n */\nexport type ContrastRole = \"text\" | \"large-text\" | \"fill-with-text\" | \"non-text\";\n\nexport interface ContrastRating {\n aa: boolean;\n aaLarge: boolean;\n aaa: boolean;\n level: ContrastLevel;\n}\n\nexport interface ContrastSample {\n ratio: number;\n /** The WCAG text rating of `ratio`, whatever the role. */\n level: ContrastLevel;\n /** The measured colours, in the token form, so a renderer can draw them. */\n foreground: string;\n background: string;\n}\n\nexport interface ContrastCheck {\n /** Stable: `<foreground-token>-on-<background-token>`. */\n id: string;\n /** What the pairing affects, as advice: \"Secondary as eyebrow text on the page background\". */\n label: string;\n role: ContrastRole;\n light: ContrastSample;\n dark: ContrastSample;\n /** The threshold this check was judged against, after the role's rule. */\n target: number;\n /** Both modes at or above `target`. */\n meetsTarget: boolean;\n}\n\nexport type ContrastThemeInput = Pick<\n ThemeConfig,\n \"brandPrimary\" | \"brandSecondary\" | \"brandAccent\" | \"brandHighlight\" | \"brandSuccess\" | \"greyScale\"\n>;\n\nexport interface AuditThemeOptions {\n /** The body text target. 4.5 is AA; a site with an older audience passes 7. */\n target?: number;\n}\n\n// --- Colour parsing ---\n\ntype Rgb = [number, number, number];\n\nfunction hslToRgb(h: number, s: number, l: number): Rgb {\n const chroma = (1 - Math.abs(2 * l - 1)) * s;\n const hue = (((h % 360) + 360) % 360) / 60;\n const x = chroma * (1 - Math.abs((hue % 2) - 1));\n const m = l - chroma / 2;\n let rgb: Rgb;\n if (hue < 1) rgb = [chroma, x, 0];\n else if (hue < 2) rgb = [x, chroma, 0];\n else if (hue < 3) rgb = [0, chroma, x];\n else if (hue < 4) rgb = [0, x, chroma];\n else if (hue < 5) rgb = [x, 0, chroma];\n else rgb = [chroma, 0, x];\n return [rgb[0] + m, rgb[1] + m, rgb[2] + m];\n}\n\nfunction parseColor(input: ColorInput): Rgb {\n const hex = /^#([0-9a-f]{6})$/i.exec(input.trim());\n if (hex) {\n const value = parseInt(hex[1], 16);\n return [(value >> 16) & 255, (value >> 8) & 255, value & 255].map((channel) => channel / 255) as Rgb;\n }\n const triplet = /^(-?[\\d.]+)\\s+([\\d.]+)%\\s+([\\d.]+)%$/.exec(input.trim());\n if (triplet) {\n return hslToRgb(Number(triplet[1]), Number(triplet[2]) / 100, Number(triplet[3]) / 100);\n }\n throw new Error(`Not a colour this module reads: \"${input}\". Pass #rrggbb or \"h s% l%\".`);\n}\n\n// --- WCAG 2.x ---\n\nfunction linearChannel(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\n/** WCAG relative luminance, 0 for black to 1 for white. */\nexport function relativeLuminance(color: ColorInput): number {\n const [r, g, b] = parseColor(color);\n return 0.2126 * linearChannel(r) + 0.7152 * linearChannel(g) + 0.0722 * linearChannel(b);\n}\n\n/** WCAG contrast ratio between two colours, 1 to 21, to two decimal places. Order does not matter. */\nexport function contrastRatio(a: ColorInput, b: ColorInput): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const ratio = (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n return Math.round(ratio * 100) / 100;\n}\n\n/** The WCAG text ratings a ratio earns: 3:1 large text, 4.5:1 AA, 7:1 AAA. */\nexport function rateContrast(ratio: number): ContrastRating {\n const aaLarge = ratio >= 3;\n const aa = ratio >= 4.5;\n const aaa = ratio >= 7;\n return { aa, aaLarge, aaa, level: aaa ? \"aaa\" : aa ? \"aa\" : aaLarge ? \"aa-large\" : \"fail\" };\n}\n\nconst LEVEL_LABEL: Record<ContrastLevel, string> = {\n aaa: \"AAA\",\n aa: \"AA\",\n \"aa-large\": \"AA large\",\n fail: \"Fails\",\n};\n\n/** The level word both `ContrastStrip` and the Theme admin field show. */\nexport function contrastLevelLabel(level: ContrastLevel): string {\n return LEVEL_LABEL[level];\n}\n\nexport interface ContrastVerdict {\n meets: boolean;\n text: string;\n}\n\n/**\n * Whether `sample` clears `target`, and the sentence both renderers print\n * (`\"meets 4.5:1\"` / `\"below 7:1\"`).\n */\nexport function contrastVerdict(\n sample: Pick<ContrastSample, \"ratio\">,\n target: number,\n): ContrastVerdict {\n const meets = sample.ratio >= target;\n return { meets, text: `${meets ? \"meets\" : \"below\"} ${target}:1` };\n}\n\n// --- The audit ---\n\ntype PaletteToken = keyof SemanticPalette;\n\ninterface Pairing {\n foreground: PaletteToken;\n background: PaletteToken;\n role: ContrastRole;\n label: string;\n}\n\n/** Every pairing in the system that carries text, plus the focus ring. */\nconst PAIRINGS: readonly Pairing[] = [\n { foreground: \"primary-foreground\", background: \"primary\", role: \"fill-with-text\", label: \"Primary button label on the primary fill\" },\n { foreground: \"secondary-foreground\", background: \"secondary\", role: \"fill-with-text\", label: \"Secondary button label on the secondary fill\" },\n { foreground: \"brand-accent-foreground\", background: \"brand-accent\", role: \"fill-with-text\", label: \"Copy on a brand-accent band\" },\n { foreground: \"highlight-foreground\", background: \"highlight\", role: \"fill-with-text\", label: \"Copy on a highlight fill\" },\n { foreground: \"success-foreground\", background: \"success\", role: \"fill-with-text\", label: \"Success badge label on its fill\" },\n { foreground: \"destructive-foreground\", background: \"destructive\", role: \"fill-with-text\", label: \"Destructive button label on its fill\" },\n { foreground: \"accent-foreground\", background: \"accent\", role: \"fill-with-text\", label: \"Hovered and selected item text on the accent surface\" },\n { foreground: \"muted-foreground\", background: \"muted\", role: \"fill-with-text\", label: \"Hint text on a muted surface\" },\n { foreground: \"card-foreground\", background: \"card\", role: \"fill-with-text\", label: \"Card body text on the card surface\" },\n { foreground: \"popover-foreground\", background: \"popover\", role: \"fill-with-text\", label: \"Menu and popover text on the popover surface\" },\n { foreground: \"foreground\", background: \"background\", role: \"text\", label: \"Body text on the page background\" },\n { foreground: \"foreground\", background: \"card\", role: \"text\", label: \"Body text on a card\" },\n { foreground: \"muted-foreground\", background: \"background\", role: \"text\", label: \"Supporting text on the page background\" },\n { foreground: \"muted-foreground\", background: \"card\", role: \"text\", label: \"Supporting text on a card\" },\n { foreground: \"content-secondary\", background: \"background\", role: \"text\", label: \"Chart axis labels and tertiary text on the page background\" },\n { foreground: \"content-secondary\", background: \"card\", role: \"text\", label: \"Chart axis labels and tertiary text on a card\" },\n { foreground: \"primary\", background: \"background\", role: \"text\", label: \"Primary as link text on the page background\" },\n { foreground: \"secondary\", background: \"background\", role: \"text\", label: \"Secondary as eyebrow text on the page background\" },\n { foreground: \"ring\", background: \"background\", role: \"non-text\", label: \"Focus ring on the page background\" },\n];\n\nfunction roleTarget(role: ContrastRole, target: number): number {\n switch (role) {\n case \"non-text\":\n return 3;\n case \"large-text\":\n return target >= 7 ? 4.5 : 3;\n default:\n return target;\n }\n}\n\nfunction sample(palette: SemanticPalette, pairing: Pairing): ContrastSample {\n const foreground = palette[pairing.foreground];\n const background = palette[pairing.background];\n const ratio = contrastRatio(foreground, background);\n return { ratio, level: rateContrast(ratio).level, foreground, background };\n}\n\n/**\n * Measures every text-carrying pairing the palette derives for these\n * colours, in light and dark. Advice, not enforcement: nothing here refuses\n * a theme on its ratios. The checks are data; BIS-59's `ContrastStrip` draws\n * them.\n *\n * Throws, naming the field, when a brand colour is not `#rrggbb`, the same\n * contract as `buildThemeCss`. An admin field validates its own value\n * before calling this, so a half-typed hex never reaches it.\n */\nexport function auditTheme(config: ContrastThemeInput, options: AuditThemeOptions = {}): ContrastCheck[] {\n const target = options.target ?? 4.5;\n const brand = brandColorsFromConfig(config);\n const light = deriveLightPalette(brand);\n const dark = deriveDarkPalette(brand);\n\n return PAIRINGS.map((pairing) => {\n const lightSample = sample(light, pairing);\n const darkSample = sample(dark, pairing);\n const threshold = roleTarget(pairing.role, target);\n return {\n id: `${pairing.foreground}-on-${pairing.background}`,\n label: pairing.label,\n role: pairing.role,\n light: lightSample,\n dark: darkSample,\n target: threshold,\n meetsTarget: lightSample.ratio >= threshold && darkSample.ratio >= threshold,\n };\n });\n}\n","/**\n * The label and hint the CLI and the Theme Global show for each preset\n * value. One record, so the two cannot drift.\n */\n\nexport interface PresetHint {\n label: string;\n hint: string;\n}\n\nexport const presetHints = {\n greyScale: {\n slate: { label: \"Slate\", hint: \"Cool blue-grey — modern, tech-forward\" },\n gray: { label: \"Gray\", hint: \"Balanced blue-grey — neutral default\" },\n zinc: { label: \"Zinc\", hint: \"Cool desaturated — minimal, sharp\" },\n neutral: { label: \"Neutral\", hint: \"Pure grey — zero hue bias\" },\n stone: { label: \"Stone\", hint: \"Warm earthy grey — organic, approachable\" },\n },\n radius: {\n sharp: { label: \"Sharp\", hint: \"0px — square corners\" },\n subtle: { label: \"Subtle\", hint: \"6px — barely rounded\" },\n rounded: { label: \"Rounded\", hint: \"8px — balanced (default)\" },\n pill: { label: \"Pill\", hint: \"12px — soft, bubbly\" },\n },\n shadow: {\n flat: { label: \"Flat\", hint: \"No shadows — clean, modern\" },\n subtle: { label: \"Subtle\", hint: \"Soft low-opacity shadows (default)\" },\n elevated: { label: \"Elevated\", hint: \"Prominent layered shadows\" },\n },\n motion: {\n snappy: { label: \"Snappy\", hint: \"Fast, spring-like (150ms)\" },\n smooth: { label: \"Smooth\", hint: \"Balanced ease-out (200ms, default)\" },\n minimal: { label: \"Minimal\", hint: \"Barely-there, linear (100ms)\" },\n },\n density: {\n compact: { label: \"Compact\", hint: \"Tight padding, smaller components\" },\n default: { label: \"Default\", hint: \"Balanced spacing\" },\n spacious: { label: \"Spacious\", hint: \"Generous padding, larger components\" },\n },\n defaultTheme: {\n system: { label: \"System\", hint: \"Follow OS preference (recommended)\" },\n light: { label: \"Light\", hint: \"Always start in light mode\" },\n dark: { label: \"Dark\", hint: \"Always start in dark mode\" },\n },\n} as const satisfies Record<string, Record<string, PresetHint>>;\n\nexport type PresetHintFamily = keyof typeof presetHints;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bison-lab/tokens",
3
- "version": "3.3.0",
3
+ "version": "3.7.0",
4
4
  "description": "Design tokens, CSS custom properties, and preset files for Bison Lab component library",
5
5
  "homepage": "https://components.bisonlab.ai",
6
6
  "repository": {
@@ -38,6 +38,9 @@
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
+ "dependencies": {
42
+ "@bison-lab/fonts": "3.6.0"
43
+ },
41
44
  "scripts": {
42
45
  "build": "tsdown",
43
46
  "dev": "tsdown --watch",