@vectoriox/iox-ui 5.13.1 → 5.15.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.
@@ -1071,7 +1071,7 @@ const SPEC_SPRINGS = {
1071
1071
  }
1072
1072
  };
1073
1073
  const SPEC_FONTS = {
1074
- "Inter": "Inter:wght@400;500;600;700",
1074
+ "Inter": "Inter:wght@300;400;500;600;700",
1075
1075
  "Inter Tight": "Inter+Tight:wght@500;600;700",
1076
1076
  "Manrope": "Manrope:wght@400;500;600;700",
1077
1077
  "IBM Plex Sans": "IBM+Plex+Sans:wght@400;500;600;700",
@@ -1086,12 +1086,21 @@ const SPEC_FONTS = {
1086
1086
  "Figtree": "Figtree:wght@400;500;600;700;800",
1087
1087
  "Google Sans Flex": "Google+Sans+Flex:wght@400;500;600;700",
1088
1088
  "Bricolage Grotesque": "Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700",
1089
- "Nunito": "Nunito:wght@400;600;700;800",
1089
+ "Nunito": "Nunito:wght@300;400;600;700;800",
1090
1090
  "DM Sans": "DM+Sans:wght@400;500;700",
1091
1091
  "Archivo": "Archivo:wght@400;500;700",
1092
1092
  "Archivo Black": "Archivo+Black",
1093
1093
  "Syne": "Syne:wght@600;700;800",
1094
- "Space Mono": "Space+Mono:wght@400;700"
1094
+ "Space Mono": "Space+Mono:wght@400;700",
1095
+ "Poppins": "Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400",
1096
+ "Roboto": "Roboto:ital,wght@0,300;0,400;0,500;0,700;1,400",
1097
+ "Lato": "Lato:ital,wght@0,300;0,400;0,700;1,400",
1098
+ "Open Sans": "Open+Sans:ital,wght@0,300;0,400;0,600;0,700;1,400",
1099
+ "Montserrat": "Montserrat:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400",
1100
+ "Raleway": "Raleway:wght@300;400;500;600;700",
1101
+ "Playfair Display": "Playfair+Display:ital,wght@0,400;0,500;0,700;1,400",
1102
+ "Oswald": "Oswald:wght@300;400;500;600;700",
1103
+ "Ubuntu": "Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,400"
1095
1104
  };
1096
1105
  const SPEC_SCHEMES = {
1097
1106
  "default": {
@@ -1748,7 +1757,6 @@ const SPEC_FIELD_CLASS_BASELINE = {
1748
1757
  const SPEC_LEGACY_ALIASES = {
1749
1758
  "radius-control": "var(--iox-button-radius)",
1750
1759
  "radius-surface": "var(--iox-card-radius)",
1751
- "space-control": "var(--iox-button-pad)",
1752
1760
  "space-stack": "var(--iox-space-gap)"
1753
1761
  };
1754
1762
  const SPEC_FLAVOR_PACKS = [
@@ -9498,13 +9506,61 @@ function styleKeyToKebab(key) {
9498
9506
  return key.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
9499
9507
  }
9500
9508
 
9509
+ /**
9510
+ * Which `--iox-<name>` a piece of COMPONENT CSS may reference (design-system §3b-bis: "component CSS
9511
+ * may reference ONLY tokens defined in this spec"). DS2 shipped seven names nobody defines
9512
+ * (`card-media-gap`, `gallery-overlay`, `dialog-close-bg`…): a token the contract does not define can
9513
+ * never be set by a flavor, so it is a literal wearing a costume. One guard, run over BOTH
9514
+ * renderers' component CSS (iox-ui's spec; the engine's `verify:token-names`).
9515
+ */
9516
+ /** The 161 names of the contract's two tiers, plus its legacy aliases. */
9517
+ function specTokenNames() {
9518
+ return new Set([
9519
+ ...SPEC_TIERS.semantic.map(t => t.name),
9520
+ ...SPEC_TIERS.component.map(t => t.name),
9521
+ ...Object.keys(SPEC_LEGACY_ALIASES),
9522
+ ]);
9523
+ }
9524
+ const REFERENCE$1 = /var\(\s*--iox-([a-z0-9-]+)/gi;
9525
+ /** Every distinct token a stylesheet READS (`var(--iox-x…)`), in first-seen order. */
9526
+ function tokenReferences(css) {
9527
+ return [...new Set(Array.from(css.matchAll(REFERENCE$1), m => m[1]))];
9528
+ }
9529
+ /** The referenced names that are neither spec tokens nor in `alsoAllowed`. */
9530
+ function unknownTokenReferences(css, alsoAllowed = []) {
9531
+ const spec = specTokenNames();
9532
+ const extra = new Set(alsoAllowed);
9533
+ return tokenReferences(css).filter(name => !spec.has(name) && !extra.has(name));
9534
+ }
9535
+ const LEGACY_SCOPE = /:not\(\s*\.iox-es\s*\)/;
9536
+ const RULE = /([^{}]+)\{([^{}]*)\}/g;
9537
+ /**
9538
+ * Tokens read by a LEGACY rule — any rule whose selector is scoped `:not(.iox-es)` (the pre-DS2 look
9539
+ * of a node that wears no element style, design-system §3b-bis). Such a rule must read NO token at
9540
+ * all: a token it reads repaints a pre-DS2 page the moment the org's rows are seeded or a flavor is
9541
+ * applied (D5). Returns `selector → names` for each offending rule; comments are ignored.
9542
+ */
9543
+ function legacyRuleTokenReferences(css) {
9544
+ const text = String(css ?? '').replace(/\/\*[\s\S]*?\*\//g, '');
9545
+ const out = [];
9546
+ for (const m of text.matchAll(RULE)) {
9547
+ const selector = m[1].trim().replace(/\s+/g, ' ');
9548
+ if (!LEGACY_SCOPE.test(selector))
9549
+ continue;
9550
+ const tokens = tokenReferences(m[2]);
9551
+ if (tokens.length)
9552
+ out.push({ selector, tokens });
9553
+ }
9554
+ return out;
9555
+ }
9556
+
9501
9557
  /** What `prefers-reduced-motion: reduce` collapses — the contract's complete list (`a11y.reducedMotion`). */
9502
9558
  const REDUCED_MOTION_OVERRIDES = SPEC_A11Y.reducedMotion;
9503
9559
  /** What `prefers-reduced-transparency: reduce` makes solid — the contract's `a11y.reducedTransparency`. */
9504
9560
  const REDUCED_TRANSPARENCY_OVERRIDES = SPEC_A11Y.reducedTransparency;
9505
9561
  const ROOT = ':root';
9506
9562
  const DEFAULT_SCHEME = 'default';
9507
- const REFERENCE$1 = 'var(';
9563
+ const REFERENCE = 'var(';
9508
9564
  const MEDIA_DARK = '(prefers-color-scheme: dark)';
9509
9565
  const ACCESSIBILITY_MEDIA = [
9510
9566
  { query: '(prefers-reduced-motion: reduce)', overrides: REDUCED_MOTION_OVERRIDES },
@@ -9549,6 +9605,16 @@ const PLACEHOLDER = /\{\{\s*([\w-]+)\s*\}\}/g;
9549
9605
  const LIGHT = 'light';
9550
9606
  const DARK = 'dark';
9551
9607
  const modeValue = (t, mode) => (mode === DARK ? darkOf(t) ?? t.value : t.value);
9608
+ /** The rows as ONE mode: each row's value in that mode, no dark override left. `auto` (or none) →
9609
+ * the rows unchanged. */
9610
+ function rowsForMode(rows, mode) {
9611
+ if (mode !== LIGHT && mode !== DARK)
9612
+ return [...rows];
9613
+ return rows.map(t => {
9614
+ const { values: _dropped, ...rest } = t;
9615
+ return { ...rest, value: modeValue(t, mode) };
9616
+ });
9617
+ }
9552
9618
  /** Every token's ROOT value in one mode, with `var(--iox-…)` references resolved against the same
9553
9619
  * set (recursively; a reference cycle or an unknown token is left as written). */
9554
9620
  function resolveTokenValues(tokens, mode = LIGHT) {
@@ -9614,7 +9680,7 @@ function compileSchemeCss(tokens, scope) {
9614
9680
  const resolved = resolveTokenValues(rows, LIGHT);
9615
9681
  return nonDefaultSchemes().map(([id, set]) => rule(schemeSelector(id, scope), [
9616
9682
  ...schemeEntries(set, resolved),
9617
- ...rows.filter(t => !(t.name in set) && t.value.includes(REFERENCE$1))
9683
+ ...rows.filter(t => !(t.name in set) && t.value.includes(REFERENCE))
9618
9684
  .map((t) => [t.name, t.value]),
9619
9685
  ])).filter(Boolean).join('\n');
9620
9686
  }
@@ -9625,7 +9691,7 @@ function darkSchemeRules(rows, scope) {
9625
9691
  const dark = resolveTokenValues(rows, DARK);
9626
9692
  const redeclared = rows.filter(t => {
9627
9693
  const d = darkOf(t);
9628
- return d !== null && (t.value.includes(REFERENCE$1) || d.includes(REFERENCE$1));
9694
+ return d !== null && (t.value.includes(REFERENCE) || d.includes(REFERENCE));
9629
9695
  });
9630
9696
  return nonDefaultSchemes().map(([id, set]) => {
9631
9697
  const swapped = Object.fromEntries(Object.entries(set).filter(([, v]) => hasPlaceholder(v)));
@@ -9641,10 +9707,14 @@ function presentOverrides(rows, overrides, onlyWithDark) {
9641
9707
  }
9642
9708
  /** The token CSS as the two halves a renderer places around its node rules. */
9643
9709
  function compileTokenCssBlocks(tokens, opts = {}) {
9644
- const rows = usableTokens(tokens);
9710
+ const rows = rowsForMode(usableTokens(tokens), opts.mode);
9645
9711
  const root = opts.scope ?? ROOT;
9712
+ const present = new Set(rows.map(t => t.name));
9713
+ const unset = opts.isolate
9714
+ ? [...specTokenNames()].filter(name => !present.has(name)).map((name) => [name, 'initial'])
9715
+ : [];
9646
9716
  const base = [
9647
- rule(root, rows.map((t) => [t.name, t.value])),
9717
+ rule(root, [...unset, ...rows.map((t) => [t.name, t.value])]),
9648
9718
  compileSchemeCss(rows, opts.scope),
9649
9719
  ...ACCESSIBILITY_MEDIA.map(({ query, overrides }) => media(query, [rule(root, presentOverrides(rows, overrides, false), ' ')])),
9650
9720
  ].filter(Boolean).join('\n');
@@ -9676,9 +9746,9 @@ function validateFlavorPack(pack) {
9676
9746
  return errors;
9677
9747
  }
9678
9748
  /**
9679
- * Swiss — the F1 pack: four tokens (control/surface radius, control/stack spacing) and the hue
9680
- * opinion F2's brand step reads. Kept until DS4 migrates `flavorId: swiss` orgs to Crisp; its four
9681
- * rows are the spec's `legacyAliases`.
9749
+ * Swiss — the retired F1 pack: four tokens (control/surface radius, control/stack spacing) and the
9750
+ * hue opinion F2's brand step read. Three of its rows become the spec's `legacyAliases` on migration;
9751
+ * `space-control` is a v2 token in every pack and takes Crisp's value.
9682
9752
  */
9683
9753
  const SWISS_FLAVOR = {
9684
9754
  id: 'swiss',
@@ -9890,33 +9960,16 @@ function switchElementStyle(target, fromId, toId, baseline = {}) {
9890
9960
  }
9891
9961
  /**
9892
9962
  * The element-style MARKER (design-system §3b-bis, spec `elementStyleMarker`). A node that wears an
9893
- * element style renders `iox-es iox-es-<id>` in BOTH renderers; every piece of component CSS that
9894
- * carries the system look is scoped to it, so a node saved before DS2 (no `elementStyle`) keeps its
9895
- * exact pre-DS2 component CSS no matter which tokens the org holds. This is the ONLY thing a
9896
- * renderer reads `elementStyle` for.
9963
+ * element style renders `iox-es iox-es-<id>` in BOTH renderers, through the one node-class helper
9964
+ * `nodeDesignClasses` (node-classes.ts); every piece of component CSS that carries the system look is
9965
+ * scoped to it, so a node saved before DS2 (no `elementStyle`) keeps its exact pre-DS2 component CSS
9966
+ * no matter which tokens the org holds.
9897
9967
  */
9898
9968
  const ELEMENT_STYLE_MARKER_CLASS = 'iox-es';
9899
- const MARKER_PREFIX = `${ELEMENT_STYLE_MARKER_CLASS}-`;
9969
+ const MARKER_PREFIX$1 = `${ELEMENT_STYLE_MARKER_CLASS}-`;
9900
9970
  /** `iox-es-<id>` for a known style, else null — an unknown id never reaches a class attribute. */
9901
9971
  function elementStyleClass(id) {
9902
- return elementStyleById(id) ? `${MARKER_PREFIX}${id}` : null;
9903
- }
9904
- /** The marker classes a node renders: `'iox-es iox-es-<id>'`, or `''` for a node with no (known) style. */
9905
- function elementStyleClasses(node) {
9906
- const own = elementStyleClass(node?.elementStyle);
9907
- return own ? `${ELEMENT_STYLE_MARKER_CLASS} ${own}` : '';
9908
- }
9909
- /** Make an element's marker match `elementStyle` — the live switch on the canvas, and the
9910
- * component path's one-time stamp. Removes a stale `iox-es-<old>`; touches no other class. */
9911
- function syncElementStyleMarker(el, elementStyle) {
9912
- if (!el?.classList)
9913
- return;
9914
- const stale = Array.from(el.classList).filter(c => c === ELEMENT_STYLE_MARKER_CLASS || c.startsWith(MARKER_PREFIX));
9915
- for (const c of stale)
9916
- el.classList.remove(c);
9917
- const classes = elementStyleClasses({ elementStyle });
9918
- if (classes)
9919
- el.classList.add(...classes.split(' '));
9972
+ return elementStyleById(id) ? `${MARKER_PREFIX$1}${id}` : null;
9920
9973
  }
9921
9974
  /**
9922
9975
  * The spec's `headingLevelRule`: changing a Heading's level moves it to the new level's DEFAULT
@@ -9934,6 +9987,117 @@ function applyHeadingLevelRule(target, oldLevel, newLevel, baseline = {}) {
9934
9987
  return switchElementStyle(target, from, to, baseline);
9935
9988
  }
9936
9989
 
9990
+ /**
9991
+ * The design-system classes a node renders, in BOTH renderers (builder canvas, engine component
9992
+ * path, engine string path) — ONE helper, so the three can never disagree about which class a node
9993
+ * carries:
9994
+ *
9995
+ * - the element-style MARKER `iox-es iox-es-<id>` (design-system §3b-bis) — component CSS carrying
9996
+ * the system look is scoped to it, so a node saved before DS2 keeps its pre-DS2 look;
9997
+ * - the section colour scheme `iox-scheme-<id>` (§3c) for a non-default `colorScheme` — the class
9998
+ * `compileSchemeCss` re-declares tokens on, so everything inside re-resolves.
9999
+ *
10000
+ * Renderers read `elementStyle` and `colorScheme` for exactly this and nothing else.
10001
+ */
10002
+ /** The section colour schemes — the contract's `schemes` keys. */
10003
+ var ColorScheme;
10004
+ (function (ColorScheme) {
10005
+ ColorScheme["Default"] = "default";
10006
+ ColorScheme["Subtle"] = "subtle";
10007
+ ColorScheme["Inverse"] = "inverse";
10008
+ ColorScheme["Accent"] = "accent";
10009
+ })(ColorScheme || (ColorScheme = {}));
10010
+ /** The content trait (stored in `props`, not a style prop) that picks a node's scheme. */
10011
+ const COLOR_SCHEME_TRAIT = 'colorScheme';
10012
+ /** The node types that offer a scheme (spec §3c: Section/Container; LinkedContainer is a Container that links). */
10013
+ const COLOR_SCHEME_TYPES = ['Section', 'Container', 'LinkedContainer'];
10014
+ /** Every scheme in contract order, with the contract's label (Default · Subtle · Inverse · Brand). */
10015
+ const COLOR_SCHEMES = Object.keys(SPEC_SCHEMES).map(id => ({ id, label: SPEC_SCHEMES[id].label }));
10016
+ const SCHEME_IDS = new Set(Object.keys(SPEC_SCHEMES));
10017
+ const SCHEME_PREFIX = 'iox-scheme-';
10018
+ const MARKER_PREFIX = `${ELEMENT_STYLE_MARKER_CLASS}-`;
10019
+ /** `iox-scheme-<id>` for a known NON-default scheme, else null — an unknown id never reaches a class attribute. */
10020
+ function colorSchemeClass(id) {
10021
+ return typeof id === 'string' && id !== ColorScheme.Default && SCHEME_IDS.has(id) ? `${SCHEME_PREFIX}${id}` : null;
10022
+ }
10023
+ /** A node's `colorScheme`, from `props` (serialized) or its trait (builder). */
10024
+ function colorSchemeOf(node) {
10025
+ if (!node)
10026
+ return null;
10027
+ const fromProps = node.props?.[COLOR_SCHEME_TRAIT];
10028
+ if (fromProps !== undefined)
10029
+ return typeof fromProps === 'string' ? fromProps : null;
10030
+ const trait = node.traits?.find(t => t?.name === COLOR_SCHEME_TRAIT)?.default;
10031
+ return typeof trait === 'string' ? trait : null;
10032
+ }
10033
+ /** True when the node wears a known element style — the marker is on it. */
10034
+ function hasElementStyleMarker(node) {
10035
+ return elementStyleClass(node?.elementStyle) !== null;
10036
+ }
10037
+ /** The classes a node renders: `'iox-es iox-es-card iox-scheme-inverse'`, or `''` for none. */
10038
+ function nodeDesignClasses(node) {
10039
+ const marker = elementStyleClass(node?.elementStyle);
10040
+ return [marker ? `${ELEMENT_STYLE_MARKER_CLASS} ${marker}` : '', colorSchemeClass(colorSchemeOf(node)) ?? '']
10041
+ .filter(Boolean).join(' ');
10042
+ }
10043
+ const isDesignClass = (c) => c === ELEMENT_STYLE_MARKER_CLASS || c.startsWith(MARKER_PREFIX) || c.startsWith(SCHEME_PREFIX);
10044
+ /** Make an element's design classes match the node — the live switch on the canvas and the
10045
+ * component path's one-time stamp. Removes stale ones; touches no other class. */
10046
+ function syncNodeDesignClasses(el, node) {
10047
+ if (!el?.classList)
10048
+ return;
10049
+ for (const c of Array.from(el.classList).filter(isDesignClass))
10050
+ el.classList.remove(c);
10051
+ const classes = nodeDesignClasses(node);
10052
+ if (classes)
10053
+ el.classList.add(...classes.split(' '));
10054
+ }
10055
+
10056
+ /**
10057
+ * Empty images (design-system DS3). A MARKED Image (`.iox-es-media`) with no source renders a box in
10058
+ * the `surface-2` token with its aspect ratio kept — on the canvas with an "Add image" label, on the
10059
+ * live site with nothing in it. An UNMARKED empty Image renders exactly as before (no `src`).
10060
+ *
10061
+ * Why a placeholder `src` instead of a src-less `<img>`: Chromium paints a src-less image as a broken
10062
+ * image (a frame, an icon and the alt text) and ignores `aspect-ratio` once alt text is present, and no
10063
+ * pseudo-element paints on an `<img>`. A real image with no natural size of its own (an SVG with no
10064
+ * width/height) is a proper replaced element: `aspect-ratio`/`object-fit` apply, the background shows
10065
+ * through, and its default object size (300×150) is the SAME for the canvas label and the live blank,
10066
+ * so both renderers lay the box out identically. The fill itself is shared CSS (`iox-design.css`)
10067
+ * keyed on {@link EMPTY_IMAGE_ATTRIBUTE}.
10068
+ */
10069
+ /** Set on a marked Image while it has no source — the hook the shared fill rule keys on. */
10070
+ const EMPTY_IMAGE_ATTRIBUTE = 'data-iox-empty';
10071
+ const svgUri = (svg) => `data:image/svg+xml,${encodeURIComponent(svg)}`;
10072
+ /** The live site's blank: an SVG with no intrinsic size and nothing in it. */
10073
+ const EMPTY_IMAGE_SRC = svgUri('<svg xmlns="http://www.w3.org/2000/svg"/>');
10074
+ const xmlEscape = (s) => s.replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`);
10075
+ /**
10076
+ * The canvas blank: the same no-size SVG with `label` centred as a chip. The chip carries its own
10077
+ * light ground, so it reads on every flavor's `surface-2`, light or dark; it is builder chrome, never
10078
+ * site content. Width is estimated from the label length (an image cannot measure text).
10079
+ */
10080
+ function emptyImageLabelSrc(label) {
10081
+ const text = xmlEscape(label);
10082
+ const width = Math.round(28 + label.length * 7);
10083
+ return svgUri('<svg xmlns="http://www.w3.org/2000/svg"><svg x="50%" y="50%" overflow="visible">'
10084
+ + `<rect x="${-width / 2}" y="-13" width="${width}" height="26" rx="13" fill="#ffffff" fill-opacity="0.92"/>`
10085
+ + '<text x="0" y="0" text-anchor="middle" dominant-baseline="central" font-family="system-ui, -apple-system, \'Segoe UI\', sans-serif" '
10086
+ + `font-size="12" font-weight="500" fill="#1f1d1b">${text}</text></svg></svg>`);
10087
+ }
10088
+ /**
10089
+ * The `src` a node's Image renders when its own source is empty: the canvas label (when `label` is
10090
+ * given) or the live blank for a MARKED node; `null` — render exactly as before — for an unmarked
10091
+ * node or one that has a source.
10092
+ */
10093
+ function emptyImageSrc(node, src, label) {
10094
+ if (src != null && src !== '')
10095
+ return null;
10096
+ if (!hasElementStyleMarker(node))
10097
+ return null;
10098
+ return label ? emptyImageLabelSrc(label) : EMPTY_IMAGE_SRC;
10099
+ }
10100
+
9937
10101
  function parseHexColor(hex) {
9938
10102
  const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec((hex ?? '').trim());
9939
10103
  if (!m)
@@ -10273,19 +10437,39 @@ function parseFontFamilies(stack) {
10273
10437
  .map(f => f.trim().replace(/^(['"])(.*)\1$/, '$2').trim())
10274
10438
  .filter(Boolean);
10275
10439
  }
10440
+ /** Where Google serves the stylesheets, and the origin the font FILES come from (preconnect). */
10441
+ const GOOGLE_FONTS_CSS_ORIGIN = 'https://fonts.googleapis.com';
10442
+ const GOOGLE_FONTS_FILE_ORIGIN = 'https://fonts.gstatic.com';
10443
+ /** The ONE stylesheet URL for a catalog id — both renderers link exactly this (one per family, T7). */
10444
+ function googleFontHref(id) {
10445
+ return `${GOOGLE_FONTS_CSS_ORIGIN}/css2?family=${id}&display=swap`;
10446
+ }
10447
+ const CATALOG_BY_LOWER = new Map(Object.entries(FONT_CATALOG).map(([family, id]) => [family.toLowerCase(), id]));
10448
+ /** The catalog id of a family name (case-insensitive), or null — a system or uploaded face has none. */
10449
+ function catalogFontId(family) {
10450
+ return CATALOG_BY_LOWER.get(String(family ?? '').trim().toLowerCase()) ?? null;
10451
+ }
10452
+ const MONO_ROLE = 'font-mono';
10453
+ const ALWAYS_LOADED_ROLES = FONT_FAMILY_TOKENS.filter(role => role !== MONO_ROLE);
10454
+ const MONO_REFERENCE = /var\(\s*--iox-font-mono\b/;
10276
10455
  /**
10277
- * The Google css2 ids to load for a token set: every catalog family named by a font role token,
10278
- * one id per family (T7 — one `<link>` per family, so one bad id cannot sink the others).
10456
+ * The Google css2 ids to load for a token set: every catalog family named by the display / body /
10457
+ * label roles, plus mono when referenced (above). One id per family (T7 — one `<link>` per family, so
10458
+ * one bad id cannot sink the others), in role order.
10279
10459
  */
10280
- function fontsForTokens(tokens) {
10460
+ function fontsForTokens(tokens, opts = {}) {
10461
+ const rows = (tokens ?? []).filter(t => !!t && typeof t.name === 'string');
10462
+ const monoRead = MONO_REFERENCE.test(opts.usedCss ?? '')
10463
+ || rows.some(t => t.name !== MONO_ROLE && MONO_REFERENCE.test(`${t.value ?? ''} ${t.values?.dark ?? ''}`));
10464
+ const roles = monoRead ? FONT_FAMILY_TOKENS : ALWAYS_LOADED_ROLES;
10281
10465
  const seen = new Set();
10282
10466
  const ids = [];
10283
- for (const role of FONT_FAMILY_TOKENS) {
10284
- const row = (tokens ?? []).find(t => t?.name === role);
10467
+ for (const role of roles) {
10468
+ const row = rows.find(t => t.name === role);
10285
10469
  for (const family of parseFontFamilies(row?.value ?? '')) {
10286
- const id = FONT_CATALOG[family];
10287
- if (id && !seen.has(family)) {
10288
- seen.add(family);
10470
+ const id = catalogFontId(family);
10471
+ if (id && !seen.has(id)) {
10472
+ seen.add(id);
10289
10473
  ids.push(id);
10290
10474
  }
10291
10475
  }
@@ -10430,42 +10614,51 @@ function planFlavorRevert(current, snapshot) {
10430
10614
  }
10431
10615
  return plan;
10432
10616
  }
10617
+ const expectationFor = (baseline, row) => !baseline ? undefined : typeof baseline === 'function' ? baseline(row) : baseline.get(row.name);
10618
+ const sameValue = (row, e) => row.value === e.value && (row.values?.dark ?? '') === (e.dark ?? '');
10433
10619
  /**
10434
10620
  * The seeding MERGE: desired tokens vs existing rows, deciding create / update / keep. The §10 C2
10435
10621
  * rule — "an upgrade only rewrites untouched rows" — is a comparison against what the PREVIOUS
10436
- * seed wrote (`oldExpected`): a seeded row whose value still equals it is the machine's and may be
10437
- * rewritten; any drift means the author took it over, and their hand wins. A row without a
10438
- * `flavor:` stamp is never touched at all. `group`/`tier` ride every write.
10622
+ * seed wrote (`baseline`): a seeded row whose value still equals it is the machine's and may be
10623
+ * rewritten; any drift means the author took it over, and their hand wins. A row the design did not
10624
+ * write is never touched: no `flavor:` stamp, unless it is a `user` row (a font the author CHOSE in
10625
+ * the design) whose value is exactly what the previous design wrote — that row is the design's own
10626
+ * choice, so a new choice may replace it. `group`/`tier` ride every write; a desired row's own
10627
+ * `source` (a chosen font's `user`) wins over the pack stamp.
10439
10628
  */
10440
- function planFlavorSeed(desired, existing, stamp, oldExpected) {
10629
+ function planFlavorSeed(desired, existing, stamp, baseline) {
10441
10630
  const byName = new Map((existing ?? []).map(r => [r.name, r]));
10442
10631
  const plan = { create: [], update: [], kept: [] };
10443
10632
  for (const def of desired ?? []) {
10444
10633
  if (!def?.name)
10445
10634
  continue;
10446
10635
  const row = byName.get(def.name);
10636
+ const source = def.source ?? stamp;
10447
10637
  if (!row) {
10448
10638
  plan.create.push({
10449
10639
  name: def.name,
10450
10640
  type: def.type ?? DEFAULT_TOKEN_TYPE,
10451
10641
  value: def.value,
10452
10642
  ...(def.values ? { values: def.values } : {}),
10453
- source: stamp,
10643
+ source,
10454
10644
  ...classification(def),
10455
10645
  });
10456
10646
  continue;
10457
10647
  }
10458
- if (!isSeeded(row)) {
10648
+ const old = expectationFor(baseline, row);
10649
+ const designOwned = isSeeded(row) || (row.source === USER_TOKEN_SOURCE && !!old && sameValue(row, old));
10650
+ if (!designOwned) {
10459
10651
  plan.kept.push(def.name);
10460
10652
  continue;
10461
10653
  }
10462
10654
  const sameAsDesired = row.value === def.value && (row.values?.dark ?? '') === (def.values?.dark ?? '');
10463
- if (sameAsDesired)
10464
- continue; // already exactly this — nothing to write, nothing kept-against
10465
- const old = oldExpected?.get(def.name);
10466
- const untouched = !!old && row.value === old.value && (row.values?.dark ?? '') === (old.dark ?? '');
10467
- if (untouched && row._id) {
10468
- plan.update.push({ id: row._id, data: { value: def.value, values: def.values ?? {}, source: stamp, ...classification(def) } });
10655
+ if (sameAsDesired && (row.source ?? '') === source)
10656
+ continue; // already exactly this
10657
+ if (sameAsDesired && isSeeded(row) && source !== USER_TOKEN_SOURCE)
10658
+ continue; // same value, another pack's stamp — nothing to write
10659
+ const untouched = !!old && sameValue(row, old);
10660
+ if ((untouched || sameAsDesired) && row._id) {
10661
+ plan.update.push({ id: row._id, data: { value: def.value, values: def.values ?? {}, source, ...classification(def) } });
10469
10662
  }
10470
10663
  else {
10471
10664
  plan.kept.push(def.name); // edited since seeding (or unverifiable) — the author's hand wins
@@ -10522,30 +10715,254 @@ function springToLinear(stiffness, damping) {
10522
10715
  }
10523
10716
 
10524
10717
  /**
10525
- * Which `--iox-<name>` a piece of COMPONENT CSS may reference (design-system §3b-bis: "component CSS
10526
- * may reference ONLY tokens defined in this spec"). DS2 shipped seven names nobody defines
10527
- * (`card-media-gap`, `gallery-overlay`, `dialog-close-bg`…): a token the contract does not define can
10528
- * never be set by a flavor, so it is a literal wearing a costume. One guard, run over BOTH
10529
- * renderers' component CSS (iox-ui's spec; the engine's `verify:token-names`).
10718
+ * The `settings.design` row (design-system plan §3f), read and written by ONE pair of functions so
10719
+ * the builder, the server's public page read (the engine's mode) and the site build agree on what a
10720
+ * stored row MEANS. The row is the settings collection's generic `config: [{ key, value }]`; values
10721
+ * are strings there (Boolean "true"/"false", Object as JSON), but a reader also accepts the typed
10722
+ * value a client control may have sent.
10530
10723
  */
10531
- /** The 161 names of the contract's two tiers, plus its legacy aliases. */
10532
- function specTokenNames() {
10533
- return new Set([
10534
- ...SPEC_TIERS.semantic.map(t => t.name),
10535
- ...SPEC_TIERS.component.map(t => t.name),
10536
- ...Object.keys(SPEC_LEGACY_ALIASES),
10537
- ]);
10724
+ /** The look a studio wears until it picks one (DS2's interim seed, and DS6's first-run selection). */
10725
+ const DEFAULT_FLAVOR_ID = 'crisp';
10726
+ var DesignConfigKey;
10727
+ (function (DesignConfigKey) {
10728
+ DesignConfigKey["FlavorId"] = "flavorId";
10729
+ DesignConfigKey["FlavorVersion"] = "flavorVersion";
10730
+ DesignConfigKey["BrandSeed"] = "brandSeed";
10731
+ DesignConfigKey["PairId"] = "pairId";
10732
+ DesignConfigKey["Shape"] = "shape";
10733
+ DesignConfigKey["Density"] = "density";
10734
+ DesignConfigKey["Motion"] = "motion";
10735
+ DesignConfigKey["PillButtons"] = "pillButtons";
10736
+ DesignConfigKey["Mode"] = "mode";
10737
+ DesignConfigKey["Fonts"] = "fonts";
10738
+ DesignConfigKey["Revert"] = "revert";
10739
+ })(DesignConfigKey || (DesignConfigKey = {}));
10740
+ const MODES = SPEC_AXES.modes;
10741
+ const FONT_ROLE_TOKENS = SPEC_FONT_ROLES.map(r => r.token);
10742
+ /** The pack for an id; a retired id (Swiss) resolves to the pack that `replaces` it. */
10743
+ function flavorPackById(id) {
10744
+ if (!id)
10745
+ return undefined;
10746
+ return SPEC_FLAVORS.find(f => f.id === id) ?? SPEC_FLAVORS.find(f => f.replaces === id);
10538
10747
  }
10539
- const REFERENCE = /var\(\s*--iox-([a-z0-9-]+)/gi;
10540
- /** Every distinct token a stylesheet READS (`var(--iox-x…)`), in first-seen order. */
10541
- function tokenReferences(css) {
10542
- return [...new Set(Array.from(css.matchAll(REFERENCE), m => m[1]))];
10748
+ /** True when `id` names a retired flavor that a v2 pack replaces (Swiss → Crisp). */
10749
+ function isRetiredFlavorId(id) {
10750
+ return !!id && !SPEC_FLAVORS.some(f => f.id === id) && SPEC_FLAVORS.some(f => f.replaces === id);
10543
10751
  }
10544
- /** The referenced names that are neither spec tokens nor in `alsoAllowed`. */
10545
- function unknownTokenReferences(css, alsoAllowed = []) {
10546
- const spec = specTokenNames();
10547
- const extra = new Set(alsoAllowed);
10548
- return tokenReferences(css).filter(name => !spec.has(name) && !extra.has(name));
10752
+ const defaultPack = () => flavorPackById(DEFAULT_FLAVOR_ID);
10753
+ const asString = (v) => (typeof v === 'string' && v.trim() !== '' ? v.trim() : null);
10754
+ function asBoolean(v) {
10755
+ if (v === true || v === 'true')
10756
+ return true;
10757
+ if (v === false || v === 'false')
10758
+ return false;
10759
+ return null;
10760
+ }
10761
+ function asObject(v) {
10762
+ if (v && typeof v === 'object')
10763
+ return v;
10764
+ if (typeof v !== 'string' || v.trim() === '')
10765
+ return null;
10766
+ try {
10767
+ const parsed = JSON.parse(v);
10768
+ return parsed && typeof parsed === 'object' ? parsed : null;
10769
+ }
10770
+ catch {
10771
+ return null;
10772
+ }
10773
+ }
10774
+ function asFonts(v) {
10775
+ const raw = asObject(v);
10776
+ if (!raw)
10777
+ return undefined;
10778
+ const fonts = {};
10779
+ for (const role of FONT_ROLE_TOKENS) {
10780
+ const family = raw[role];
10781
+ if (typeof family === 'string' && family.trim() !== '')
10782
+ fonts[role] = family.trim();
10783
+ }
10784
+ return Object.keys(fonts).length ? fonts : undefined;
10785
+ }
10786
+ const keyOf = (table, v) => v !== null && Object.prototype.hasOwnProperty.call(table, v) ? v : null;
10787
+ /** A config array → the key/value map (last entry wins, as the settings form writes it). */
10788
+ function configMap(config) {
10789
+ const map = new Map();
10790
+ for (const kv of config ?? [])
10791
+ if (kv && typeof kv.key === 'string')
10792
+ map.set(kv.key, kv.value);
10793
+ return map;
10794
+ }
10795
+ /**
10796
+ * A partial set of choices completed against its pack: every missing or invalid value falls back to
10797
+ * the pack's own default (an unknown pair → the pack's first, a bad seed → the pack's seed).
10798
+ */
10799
+ function resolveDesignChoices(partial) {
10800
+ const pack = flavorPackById(partial?.flavorId) ?? defaultPack();
10801
+ const seed = asString(partial?.seed);
10802
+ const pairId = asString(partial?.pairId);
10803
+ const fonts = asFonts(partial?.fonts);
10804
+ return {
10805
+ flavorId: pack.id,
10806
+ seed: seed && parseHexColor(seed) ? seed : pack.seed,
10807
+ pairId: pairId && pack.pairs.some(p => p.id === pairId) ? pairId : pack.pairs[0]?.id ?? '',
10808
+ shape: keyOf(SPEC_AXES.shapes, asString(partial?.shape)) ?? pack.defaults.shape,
10809
+ density: keyOf(SPEC_AXES.densities, asString(partial?.density)) ?? pack.defaults.density,
10810
+ motion: keyOf(SPEC_AXES.motions, asString(partial?.motion)) ?? pack.defaults.motion,
10811
+ pillButtons: asBoolean(partial?.pillButtons) ?? pack.defaults.pillButtons,
10812
+ mode: (MODES.includes(asString(partial?.mode) ?? '') ? asString(partial?.mode) : pack.defaults.mode),
10813
+ ...(fonts ? { fonts } : {}),
10814
+ };
10815
+ }
10816
+ /** The pack's own defaults for `flavorId` — what picking a card means before anything is tuned. */
10817
+ function packDesignChoices(flavorId) {
10818
+ return resolveDesignChoices({ flavorId });
10819
+ }
10820
+ /** A stored `settings.design` config → the resolved design. No row (or no config) → Crisp's defaults. */
10821
+ function readDesignConfig(config, hasRow = !!config) {
10822
+ const map = configMap(config);
10823
+ const storedFlavorId = asString(map.get(DesignConfigKey.FlavorId));
10824
+ const storedSeed = asString(map.get(DesignConfigKey.BrandSeed));
10825
+ const choices = resolveDesignChoices({
10826
+ flavorId: storedFlavorId ?? undefined,
10827
+ seed: storedSeed ?? undefined,
10828
+ pairId: asString(map.get(DesignConfigKey.PairId)) ?? undefined,
10829
+ shape: map.get(DesignConfigKey.Shape),
10830
+ density: map.get(DesignConfigKey.Density),
10831
+ motion: map.get(DesignConfigKey.Motion),
10832
+ pillButtons: asBoolean(map.get(DesignConfigKey.PillButtons)) ?? undefined,
10833
+ mode: map.get(DesignConfigKey.Mode),
10834
+ fonts: map.get(DesignConfigKey.Fonts),
10835
+ });
10836
+ return {
10837
+ choices,
10838
+ storedFlavorId,
10839
+ storedSeed,
10840
+ flavorVersion: asString(map.get(DesignConfigKey.FlavorVersion)),
10841
+ revert: asObject(map.get(DesignConfigKey.Revert)),
10842
+ hasRow,
10843
+ };
10844
+ }
10845
+ /** The colour mode a stored config compiles in — what the public page read hands the engine. */
10846
+ function designModeOf(config) {
10847
+ return readDesignConfig(config).choices.mode;
10848
+ }
10849
+ /** Resolved choices (+ the revert snapshot) → the config array a settings row stores. */
10850
+ function designConfigEntries(choices, flavorVersion, revert) {
10851
+ const c = resolveDesignChoices(choices);
10852
+ return [
10853
+ { key: DesignConfigKey.FlavorId, value: c.flavorId },
10854
+ { key: DesignConfigKey.FlavorVersion, value: String(flavorVersion) },
10855
+ { key: DesignConfigKey.BrandSeed, value: c.seed },
10856
+ { key: DesignConfigKey.PairId, value: c.pairId },
10857
+ { key: DesignConfigKey.Shape, value: c.shape },
10858
+ { key: DesignConfigKey.Density, value: c.density },
10859
+ { key: DesignConfigKey.Motion, value: c.motion },
10860
+ { key: DesignConfigKey.PillButtons, value: String(c.pillButtons) },
10861
+ { key: DesignConfigKey.Mode, value: c.mode },
10862
+ ...(c.fonts ? [{ key: DesignConfigKey.Fonts, value: JSON.stringify(c.fonts) }] : []),
10863
+ ...(revert ? [{ key: DesignConfigKey.Revert, value: JSON.stringify(revert) }] : []),
10864
+ ];
10865
+ }
10866
+ /** Two choice sets describe the same look (fonts compared by role). */
10867
+ function sameDesignChoices(a, b) {
10868
+ const x = resolveDesignChoices(a);
10869
+ const y = resolveDesignChoices(b);
10870
+ return x.flavorId === y.flavorId && x.seed.toLowerCase() === y.seed.toLowerCase() && x.pairId === y.pairId
10871
+ && x.shape === y.shape && x.density === y.density && x.motion === y.motion
10872
+ && x.pillButtons === y.pillButtons && x.mode === y.mode
10873
+ && JSON.stringify(x.fonts ?? {}) === JSON.stringify(y.fonts ?? {});
10874
+ }
10875
+
10876
+ /**
10877
+ * Applying a look (design-system DS4): the COMPLETE `buildFlavorTokens` set for the chosen
10878
+ * `DesignChoices`, merged into the org's rows by the §10 C2 rule — rewrite only what the previous
10879
+ * seed wrote and nobody touched since, keep the author's rows, report what was kept. And the one
10880
+ * migration the contract names: an org still on Swiss becomes Crisp.
10881
+ * Pure plans; each caller (builder, the server-side site build) applies them with its own transport.
10882
+ */
10883
+ const SEED_STAMP = /^flavor:([^@]+)@/;
10884
+ const expectations = (rows) => new Map(rows.map(r => [r.name, { value: r.value, ...(r.values?.dark ? { dark: r.values.dark } : {}) }]));
10885
+ /** Every row a design writes — `buildFlavorTokens` for its pack. Empty for an unknown pack. */
10886
+ function designTokenRows(choices) {
10887
+ const pack = flavorPackById(choices.flavorId);
10888
+ return pack ? buildFlavorTokens(pack, { ...choices, flavorId: pack.id }) : [];
10889
+ }
10890
+ /** What the F1/F2 Swiss seeding wrote: the four Swiss rows + the brand set derived from its seed. */
10891
+ function swissExpectations(seed) {
10892
+ const rows = SWISS_FLAVOR.tokens.map(t => ({ name: t.name, value: t.value, values: t.values }));
10893
+ if (seed) {
10894
+ try {
10895
+ rows.push(...deriveBrandTokens(seed, SWISS_FLAVOR).map(t => ({ name: t.name, value: t.value, values: t.values })));
10896
+ }
10897
+ catch { /* an unparseable stored seed proves nothing about the rows */ }
10898
+ }
10899
+ return expectations(rows);
10900
+ }
10901
+ /**
10902
+ * What the PREVIOUS design wrote, as a baseline keyed by the row's own stamp: a row Swiss stamped is
10903
+ * judged against what Swiss wrote, every other row against `buildFlavorTokens` of the stored choices
10904
+ * (for an org with no design row, Crisp's defaults — exactly what DS2's interim seed wrote). In a
10905
+ * Swiss org the only v2 rows are that interim seed's, so they are judged against Crisp's DEFAULTS,
10906
+ * never against the brand-seeded Crisp the migration is about to write.
10907
+ */
10908
+ function designBaseline(previous) {
10909
+ const retired = isRetiredFlavorId(previous.storedFlavorId);
10910
+ const current = expectations(designTokenRows(retired ? packDesignChoices(previous.choices.flavorId) : previous.choices));
10911
+ const swiss = swissExpectations(retired ? previous.storedSeed : null);
10912
+ return (row) => {
10913
+ const stampedBy = SEED_STAMP.exec(row.source ?? '')?.[1];
10914
+ return stampedBy === SWISS_FLAVOR.id ? swiss.get(row.name) : current.get(row.name);
10915
+ };
10916
+ }
10917
+ /** Apply `next` over the org's rows, judged against `previous` — the plan FlavorApplyService runs. */
10918
+ function planDesignApply(existing, previous, next) {
10919
+ const pack = flavorPackById(next.flavorId);
10920
+ if (!pack)
10921
+ return { create: [], update: [], kept: [] };
10922
+ return planFlavorSeed(designTokenRows(next), existing, flavorSourceStamp(pack.id), designBaseline(previous));
10923
+ }
10924
+ /** The rows a design is missing by name — the builder-load top-up (never touches an existing row). */
10925
+ function missingDesignRows(existing, choices) {
10926
+ const have = new Set((existing ?? []).map(r => r.name));
10927
+ return designTokenRows(choices).filter(r => !have.has(r.name));
10928
+ }
10929
+ const LEGACY_ALIASES = SPEC_LEGACY_ALIASES;
10930
+ /**
10931
+ * Swiss → Crisp (spec `legacyAliases`; D1). Needed when the design row says a retired flavor, or when
10932
+ * there is no design row but the org holds Swiss-stamped alias rows (F1 seeded those before the row
10933
+ * existed). Idempotent: once the row says Crisp nothing is needed, and re-running the plan writes
10934
+ * nothing.
10935
+ *
10936
+ * - The look: `planDesignApply` from Swiss to Crisp — Swiss-stamped rows untouched since Swiss seeded
10937
+ * them (incl. F2's `surface` / `line` / `accent`) become Crisp's; edited ones are kept.
10938
+ * - The aliases: `radius-control` / `radius-surface` / `space-control` / `space-stack` are rewritten to
10939
+ * their alias reference ONLY while still machine-owned and untouched, so a node saved since F1 that
10940
+ * reads `var(--iox-radius-control)` keeps resolving — now to Crisp's button radius.
10941
+ */
10942
+ function planSwissMigration(existing, stored) {
10943
+ const swissAliasRows = (existing ?? []).filter(r => r.name in LEGACY_ALIASES && SEED_STAMP.exec(r.source ?? '')?.[1] === SWISS_FLAVOR.id);
10944
+ const needed = isRetiredFlavorId(stored.storedFlavorId) || (!stored.hasRow && swissAliasRows.length > 0);
10945
+ if (!needed)
10946
+ return null;
10947
+ const choices = stored.choices;
10948
+ const previous = isRetiredFlavorId(stored.storedFlavorId) ? stored : { ...stored, storedFlavorId: SWISS_FLAVOR.id };
10949
+ const plan = planDesignApply(existing, previous, choices);
10950
+ const stamp = flavorSourceStamp(choices.flavorId);
10951
+ const swiss = swissExpectations(null);
10952
+ for (const row of (existing ?? []).filter(r => r.name in LEGACY_ALIASES)) {
10953
+ const alias = LEGACY_ALIASES[row.name];
10954
+ if (row.value === alias)
10955
+ continue;
10956
+ const was = swiss.get(row.name);
10957
+ const untouched = SEED_STAMP.test(row.source ?? '') && !!was && row.value === was.value && !row.values?.dark;
10958
+ if (untouched && row._id) {
10959
+ plan.update.push({ id: row._id, data: { value: alias, values: {}, source: stamp } });
10960
+ }
10961
+ else {
10962
+ plan.kept.push(row.name);
10963
+ }
10964
+ }
10965
+ return { choices, plan };
10549
10966
  }
10550
10967
 
10551
10968
  /**
@@ -10563,5 +10980,5 @@ function unknownTokenReferences(css, alsoAllowed = []) {
10563
10980
  * Generated bundle index. Do not edit.
10564
10981
  */
10565
10982
 
10566
- export { ACCENT_TOKEN_NAMES, BUTTON_VARIANT_TO_STYLE, DESIGN_TOKEN_GROUPS, DESIGN_TOKEN_TIERS, DESIGN_TOKEN_TYPES, ELEMENT_STYLES, ELEMENT_STYLE_MARKER_CLASS, ELEMENT_STYLE_STATES, ElementStyleState, FLAVOR_SPEC_VERSION, FONT_CATALOG, FONT_FAMILY_TOKENS, OwnedTraitLink, PALETTE_PRESETS, REDUCED_MOTION_OVERRIDES, REDUCED_TRANSPARENCY_OVERRIDES, REVERT_WINDOW_MS, SPEC_A11Y, SPEC_AXES, SPEC_BUTTON_VARIANT_TO_STYLE, SPEC_CONTRAST, SPEC_DEFAULT_ELEMENT_STYLE, SPEC_ELEMENT_STYLES, SPEC_FIELD_CLASS_BASELINE, SPEC_FLAVORS, SPEC_FLAVOR_PACKS, SPEC_FONTS, SPEC_FONT_ROLES, SPEC_LEGACY_ALIASES, SPEC_PALETTE_PRESETS, SPEC_SCHEMES, SPEC_SPRINGS, SPEC_TIERS, SPEC_TIER_IDS, SPEC_TOKEN_TYPES, SWISS_FLAVOR, USER_TOKEN_SOURCE, WCAG_AA_TEXT, apcaLc, applyElementStyle, applyHeadingLevelRule, buildFlavorTokens, buildFontFaceCss, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, defaultElementStyleFor, deriveAccentTokens, deriveBrandTokens, elementStyleById, elementStyleClass, elementStyleClasses, elementStylesFor, flavorSourceStamp, fontsForTokens, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, oklabToRgb, ownedTraitStatus, parseFontFamilies, parseHexColor, planFlavorRevert, planFlavorSeed, relativeLuminance, resolveTokenReferences, resolveTokenValues, revertAvailable, rgbToOklab, specTokenNames, springToLinear, styleKeyToKebab, switchElementStyle, syncElementStyleMarker, toHexColor, tokenReferences, tokensToDarkVarMap, tokensToVarMap, unknownTokenReferences, userFontStack, validateFlavorPack, wcagContrastRatio };
10983
+ export { ACCENT_TOKEN_NAMES, BUTTON_VARIANT_TO_STYLE, COLOR_SCHEMES, COLOR_SCHEME_TRAIT, COLOR_SCHEME_TYPES, ColorScheme, DEFAULT_FLAVOR_ID, DESIGN_TOKEN_GROUPS, DESIGN_TOKEN_TIERS, DESIGN_TOKEN_TYPES, DesignConfigKey, ELEMENT_STYLES, ELEMENT_STYLE_MARKER_CLASS, ELEMENT_STYLE_STATES, EMPTY_IMAGE_ATTRIBUTE, EMPTY_IMAGE_SRC, ElementStyleState, FLAVOR_SPEC_VERSION, FONT_CATALOG, FONT_FAMILY_TOKENS, GOOGLE_FONTS_CSS_ORIGIN, GOOGLE_FONTS_FILE_ORIGIN, OwnedTraitLink, PALETTE_PRESETS, REDUCED_MOTION_OVERRIDES, REDUCED_TRANSPARENCY_OVERRIDES, REVERT_WINDOW_MS, SPEC_A11Y, SPEC_AXES, SPEC_BUTTON_VARIANT_TO_STYLE, SPEC_CONTRAST, SPEC_DEFAULT_ELEMENT_STYLE, SPEC_ELEMENT_STYLES, SPEC_FIELD_CLASS_BASELINE, SPEC_FLAVORS, SPEC_FLAVOR_PACKS, SPEC_FONTS, SPEC_FONT_ROLES, SPEC_LEGACY_ALIASES, SPEC_PALETTE_PRESETS, SPEC_SCHEMES, SPEC_SPRINGS, SPEC_TIERS, SPEC_TIER_IDS, SPEC_TOKEN_TYPES, SWISS_FLAVOR, USER_TOKEN_SOURCE, WCAG_AA_TEXT, apcaLc, applyElementStyle, applyHeadingLevelRule, buildFlavorTokens, buildFontFaceCss, catalogFontId, colorSchemeClass, colorSchemeOf, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, defaultElementStyleFor, deriveAccentTokens, deriveBrandTokens, designBaseline, designConfigEntries, designModeOf, designTokenRows, elementStyleById, elementStyleClass, elementStylesFor, emptyImageLabelSrc, emptyImageSrc, flavorPackById, flavorSourceStamp, fontsForTokens, googleFontHref, hasElementStyleMarker, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, isRetiredFlavorId, legacyRuleTokenReferences, missingDesignRows, nodeDesignClasses, oklabToRgb, ownedTraitStatus, packDesignChoices, parseFontFamilies, parseHexColor, planDesignApply, planFlavorRevert, planFlavorSeed, planSwissMigration, readDesignConfig, relativeLuminance, resolveDesignChoices, resolveTokenReferences, resolveTokenValues, revertAvailable, rgbToOklab, sameDesignChoices, specTokenNames, springToLinear, styleKeyToKebab, switchElementStyle, syncNodeDesignClasses, toHexColor, tokenReferences, tokensToDarkVarMap, tokensToVarMap, unknownTokenReferences, userFontStack, validateFlavorPack, wcagContrastRatio };
10567
10984
  //# sourceMappingURL=vectoriox-iox-ui-design-core.mjs.map