@vectoriox/iox-ui 5.16.0 → 5.18.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.
@@ -8655,7 +8655,9 @@ type SeedBaseline = ReadonlyMap<string, SeedExpectation> | ((row: StoredDesignTo
8655
8655
  * write is never touched: no `flavor:` stamp, unless it is a `user` row (a font the author CHOSE in
8656
8656
  * the design) whose value is exactly what the previous design wrote — that row is the design's own
8657
8657
  * choice, so a new choice may replace it. `group`/`tier` ride every write; a desired row's own
8658
- * `source` (a chosen font's `user`) wins over the pack stamp.
8658
+ * `source` (a chosen font's `user`) wins over the pack stamp. A machine-owned row that already holds
8659
+ * the desired value but another pack's stamp is RE-STAMPED (§10 C2 provenance: the stamp names the
8660
+ * design that owns the row, so the next switch judges it against the right baseline).
8659
8661
  */
8660
8662
  declare function planFlavorSeed(desired: ReadonlyArray<FlavorTokenDef & {
8661
8663
  source?: string;
@@ -8692,8 +8694,43 @@ declare const FONT_FAMILY_TOKENS: readonly ["font-display", "font-body", "font-l
8692
8694
  * uploaded family named `</style><script>` would otherwise close the element.
8693
8695
  */
8694
8696
  declare function cssString(value: string): string;
8695
- /** One `@font-face` per file, carrying its real weight and style — so bold is never synthesised. */
8697
+ /** The file extensions a studio may upload as a font, and the descriptor format each one IS (§5b). */
8698
+ declare const FONT_FILE_FORMATS: Readonly<Record<string, FontFormat>>;
8699
+ declare const FONT_FILE_EXTENSIONS: ReadonlyArray<string>;
8700
+ /** For a file input's `accept` — the same list the server validates against. */
8701
+ declare const FONT_FILE_ACCEPT: string;
8702
+ /** The CSS weights a face can be declared at, for an editor that offers them. */
8703
+ declare const FONT_WEIGHTS: ReadonlyArray<number>;
8704
+ declare const FONT_STYLES: ReadonlyArray<FontStyle>;
8705
+ /** A stored key's original file name: the folder prefix and the `-<timestamp>` upload suffix removed. */
8706
+ declare function fontFileName(key: string | null | undefined): string;
8707
+ /** The descriptor format of a font file, from its extension — null for anything we do not serve. */
8708
+ declare function fontFormatOf(fileNameOrKey: string | null | undefined): FontFormat | null;
8709
+ /** The family a file had before DS5 — its name without the extension. Legacy files with no parsed
8710
+ * name table keep it, so a page saved with `font-family: 'Rubik-Regular'` keeps its face (D5). */
8711
+ declare function fontFileStem(key: string | null | undefined): string;
8712
+ /** A weight as CSS can use it: 100–900, a bad value → 400. */
8713
+ declare function normalizeFontWeight(w: unknown): number;
8714
+ declare function normalizeFontStyle(style: unknown): FontStyle;
8715
+ /** CSS family names match case-insensitively; this is the one comparison key. */
8716
+ declare function fontFamilyKey(family: string | null | undefined): string;
8717
+ /**
8718
+ * One `@font-face` per FACE (family × weight × style), carrying its real weight and style — so bold is
8719
+ * never synthesised. Files that declare the same face (a woff2 and a ttf of one cut) share one rule
8720
+ * whose `src` lists them best format first, which is what the two retired forks did per file stem.
8721
+ */
8696
8722
  declare function buildFontFaceCss(descriptors: ReadonlyArray<FontDescriptor> | null | undefined): string;
8723
+ /** An uploaded family: every file sharing a family name (case-insensitively), with its real cuts. */
8724
+ interface FontFamilyGroup {
8725
+ family: string;
8726
+ /** Distinct weights, ascending. */
8727
+ weights: number[];
8728
+ styles: FontStyle[];
8729
+ /** Every file of the family, lightest first. */
8730
+ faces: FontDescriptor[];
8731
+ }
8732
+ /** Files → families (§5b "Group into families"): the Files tile, the typeface dropdown and Design ▸ Type. */
8733
+ declare function groupFontFamilies(descriptors: ReadonlyArray<FontDescriptor> | null | undefined): FontFamilyGroup[];
8697
8734
  /** The family names in a CSS `font-family` stack, unquoted, in order. */
8698
8735
  declare function parseFontFamilies(stack: string): string[];
8699
8736
  /** Where Google serves the stylesheets, and the origin the font FILES come from (preconnect). */
@@ -8717,6 +8754,28 @@ interface FontsForTokensOptions {
8717
8754
  * one bad id cannot sink the others), in role order.
8718
8755
  */
8719
8756
  declare function fontsForTokens(tokens: ReadonlyArray<DesignTokenLike> | null | undefined, opts?: FontsForTokensOptions): string[];
8757
+ type FontFamilyToken = (typeof FONT_FAMILY_TOKENS)[number];
8758
+ /** True when `stack` (a CSS `font-family` value) names `family` anywhere in it. */
8759
+ declare function stackNamesFamily(stack: string | null | undefined, family: string | null | undefined): boolean;
8760
+ /**
8761
+ * The font tokens whose value names `family` — what the delete guard asks before an uploaded family's
8762
+ * last file goes (§5b): deleting it would silently drop those roles to their fallback.
8763
+ */
8764
+ declare function fontRoleReferences(tokens: ReadonlyArray<DesignTokenLike> | null | undefined, family: string | null | undefined): FontFamilyToken[];
8765
+ /**
8766
+ * The face CSS font matching picks for `weight` from `faces` (CSS Fonts §5.2): 400 tries 500 next,
8767
+ * 500 tries 400; lighter weights look down first, heavier ones up. So an `h1` at 600 over a family
8768
+ * uploaded at 400 + 700 renders the real 700 cut — the one worth preloading.
8769
+ */
8770
+ declare function matchFontWeight<T extends {
8771
+ weight: number;
8772
+ }>(faces: ReadonlyArray<T>, weight: number): T | null;
8773
+ /**
8774
+ * The uploaded files the SSR head preloads (§5b — no flash of the fallback face): for the display and
8775
+ * body roles, the family each token names (its first uploaded family), the woff2 normal-style cut CSS
8776
+ * matching would pick for that role's weights. One file per weight that renders, never the family.
8777
+ */
8778
+ declare function fontPreloads(descriptors: ReadonlyArray<FontDescriptor> | null | undefined, tokens: ReadonlyArray<DesignTokenLike> | null | undefined): FontDescriptor[];
8720
8779
 
8721
8780
  /**
8722
8781
  * Which `--iox-<name>` a piece of COMPONENT CSS may reference (design-system §3b-bis: "component CSS
@@ -8735,7 +8794,9 @@ declare function unknownTokenReferences(css: string, alsoAllowed?: Iterable<stri
8735
8794
  * Tokens read by a LEGACY rule — any rule whose selector is scoped `:not(.iox-es)` (the pre-DS2 look
8736
8795
  * of a node that wears no element style, design-system §3b-bis). Such a rule must read NO token at
8737
8796
  * all: a token it reads repaints a pre-DS2 page the moment the org's rows are seeded or a flavor is
8738
- * applied (D5). Returns `selector → names` for each offending rule; comments are ignored.
8797
+ * applied (D5). Returns `selector → names` for each offending rule; comments are ignored. SCSS
8798
+ * nesting is resolved (DS4.1): `.x:where(:not(.iox-es)) { &:hover { … } }` is the legacy rule
8799
+ * `.x:where(:not(.iox-es)):hover`, and an at-rule (`@media`) between them does not break the chain.
8739
8800
  */
8740
8801
  declare function legacyRuleTokenReferences(css: string): Array<{
8741
8802
  selector: string;
@@ -8792,6 +8853,12 @@ declare function isRetiredFlavorId(id: string | null | undefined): boolean;
8792
8853
  declare function resolveDesignChoices(partial: Partial<DesignChoices> | null | undefined): DesignChoices;
8793
8854
  /** The pack's own defaults for `flavorId` — what picking a card means before anything is tuned. */
8794
8855
  declare function packDesignChoices(flavorId: string): DesignChoices;
8856
+ /**
8857
+ * What picking a pack card means, given the design the studio wears: the applied pack → the applied
8858
+ * choices; another pack → that pack's defaults, CARRYING the author's chosen fonts (§5b — a font the
8859
+ * author chose survives a pack switch unless the author changes it). Both picker homes call this.
8860
+ */
8861
+ declare function choicesForPack(flavorId: string, applied: DesignChoices | null | undefined): DesignChoices;
8795
8862
  /** A stored `settings.design` config → the resolved design. No row (or no config) → Crisp's defaults. */
8796
8863
  declare function readDesignConfig(config: ReadonlyArray<DesignConfigEntry> | null | undefined, hasRow?: boolean): StoredDesign;
8797
8864
  /** The colour mode a stored config compiles in — what the public page read hands the engine. */
@@ -8817,6 +8884,15 @@ declare function planDesignApply(existing: ReadonlyArray<StoredDesignTokenRow>,
8817
8884
  declare function missingDesignRows(existing: ReadonlyArray<{
8818
8885
  name: string;
8819
8886
  }>, choices: DesignChoices): DesignTokenRow[];
8887
+ /**
8888
+ * The builder-load REPAIR (DS4.1): rows the design owns and nobody touched — stamped by the current
8889
+ * pack, light value exactly the pack's — whose dark override differs from the pack's are rewritten.
8890
+ * iox-cms dropped `values.dark` on every write from F0 to DS4 (`values` was an implicit relation),
8891
+ * and a revert could leave the previous apply's dark behind; both leave a row whose light half is the
8892
+ * design's and whose dark half is not. No author edit can produce that shape — the Tokens panel edits
8893
+ * the light value only — so rewriting it heals existing orgs without the author doing anything.
8894
+ */
8895
+ declare function designTokenRepairs(existing: ReadonlyArray<StoredDesignTokenRow>, choices: DesignChoices): FlavorSeedPlan['update'];
8820
8896
  interface SwissMigration {
8821
8897
  /** The design the org now wears: Crisp, carrying the stored brand seed when it is a colour. */
8822
8898
  choices: DesignChoices;
@@ -8831,11 +8907,33 @@ interface SwissMigration {
8831
8907
  *
8832
8908
  * - The look: `planDesignApply` from Swiss to Crisp — Swiss-stamped rows untouched since Swiss seeded
8833
8909
  * them (incl. F2's `surface` / `line` / `accent`) become Crisp's; edited ones are kept.
8834
- * - The aliases: `radius-control` / `radius-surface` / `space-control` / `space-stack` are rewritten to
8835
- * their alias reference ONLY while still machine-owned and untouched, so a node saved since F1 that
8836
- * reads `var(--iox-radius-control)` keeps resolving — now to Crisp's button radius.
8910
+ * - The aliases (`SPEC_LEGACY_ALIASES`: `radius-control` / `radius-surface` / `space-stack`) are rewritten
8911
+ * to their alias reference ONLY while still machine-owned and untouched, so a node saved since F1 that
8912
+ * reads `var(--iox-radius-control)` keeps resolving — now to Crisp's button radius. `space-control` is
8913
+ * NOT an alias: it is a v2 semantic token (`button-pad` reads it), and aliasing it closed a `var()`
8914
+ * cycle; it migrates like any other Swiss row.
8837
8915
  */
8838
8916
  declare function planSwissMigration(existing: ReadonlyArray<StoredDesignTokenRow>, stored: StoredDesign): SwissMigration | null;
8917
+ /** What deleting an uploaded family does to the look (§5b delete guard). */
8918
+ interface FontFamilyRemoval {
8919
+ /** The font tokens that name the family — empty means nothing references it and nothing changes. */
8920
+ roles: FontFamilyToken[];
8921
+ /** The design without the family: every font role it held goes back to the pack's pairing. */
8922
+ choices: DesignChoices;
8923
+ /** The token writes: the look's own merge, plus any OTHER row still naming the family (one typed
8924
+ * in the Tokens panel), rewritten to the pack's value too — a deleted face must not stay named. */
8925
+ plan: FlavorSeedPlan;
8926
+ /** Per role, the family it falls back to — what the guard tells the author before they confirm. */
8927
+ fallbacks: Array<{
8928
+ role: FontFamilyToken;
8929
+ family: string;
8930
+ }>;
8931
+ }
8932
+ /**
8933
+ * Plan the removal of an uploaded family from the look: the roles naming it fall back to the pack's
8934
+ * own font (the design's pairing), as the guard promises — "Nothing else changes".
8935
+ */
8936
+ declare function planFontFamilyRemoval(existing: ReadonlyArray<StoredDesignTokenRow>, previous: StoredDesign, family: string): FontFamilyRemoval;
8839
8937
 
8840
- 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, emptyImagePlaceholder, 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 };
8841
- export type { AccentBackgrounds, AccentTokenName, ApplyElementStyleResult, DensityId, DesignChoices, DesignClassSource, DesignConfigEntry, DesignMode, DesignTokenGroup, DesignTokenLike, DesignTokenRow, DesignTokenTier, DesignTokenType, ElementStyle, ElementStyleTarget, ElementStyleTraitLike, FlavorPack, FlavorPackV2, FlavorRevertPlan, FlavorRevertSnapshot, FlavorSeedPlan, FlavorSpecToken, FlavorTokenDef, FontDescriptor, FontFormat, FontPair, FontRoleToken, FontStyle, FontsForTokensOptions, LinearSpring, MotionId, OkLab, OwnedTrait, OwnedTraitStatus, PalettePreset, ResolvedMode, Rgb, SchemeId, SeedBaseline, SeedExpectation, ShapeId, StoredDesign, StoredDesignTokenRow, SwissMigration, SwitchElementStyleResult, TokenCssBlocks, TokenCssOptions };
8938
+ 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, FONT_FILE_ACCEPT, FONT_FILE_EXTENSIONS, FONT_FILE_FORMATS, FONT_STYLES, FONT_WEIGHTS, 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, choicesForPack, colorSchemeClass, colorSchemeOf, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, defaultElementStyleFor, deriveAccentTokens, deriveBrandTokens, designBaseline, designConfigEntries, designModeOf, designTokenRepairs, designTokenRows, elementStyleById, elementStyleClass, elementStylesFor, emptyImageLabelSrc, emptyImagePlaceholder, emptyImageSrc, flavorPackById, flavorSourceStamp, fontFamilyKey, fontFileName, fontFileStem, fontFormatOf, fontPreloads, fontRoleReferences, fontsForTokens, googleFontHref, groupFontFamilies, hasElementStyleMarker, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, isRetiredFlavorId, legacyRuleTokenReferences, matchFontWeight, missingDesignRows, nodeDesignClasses, normalizeFontStyle, normalizeFontWeight, oklabToRgb, ownedTraitStatus, packDesignChoices, parseFontFamilies, parseHexColor, planDesignApply, planFlavorRevert, planFlavorSeed, planFontFamilyRemoval, planSwissMigration, readDesignConfig, relativeLuminance, resolveDesignChoices, resolveTokenReferences, resolveTokenValues, revertAvailable, rgbToOklab, sameDesignChoices, specTokenNames, springToLinear, stackNamesFamily, styleKeyToKebab, switchElementStyle, syncNodeDesignClasses, toHexColor, tokenReferences, tokensToDarkVarMap, tokensToVarMap, unknownTokenReferences, userFontStack, validateFlavorPack, wcagContrastRatio };
8939
+ export type { AccentBackgrounds, AccentTokenName, ApplyElementStyleResult, DensityId, DesignChoices, DesignClassSource, DesignConfigEntry, DesignMode, DesignTokenGroup, DesignTokenLike, DesignTokenRow, DesignTokenTier, DesignTokenType, ElementStyle, ElementStyleTarget, ElementStyleTraitLike, FlavorPack, FlavorPackV2, FlavorRevertPlan, FlavorRevertSnapshot, FlavorSeedPlan, FlavorSpecToken, FlavorTokenDef, FontDescriptor, FontFamilyGroup, FontFamilyRemoval, FontFamilyToken, FontFormat, FontPair, FontRoleToken, FontStyle, FontsForTokensOptions, LinearSpring, MotionId, OkLab, OwnedTrait, OwnedTraitStatus, PalettePreset, ResolvedMode, Rgb, SchemeId, SeedBaseline, SeedExpectation, ShapeId, StoredDesign, StoredDesignTokenRow, SwissMigration, SwitchElementStyleResult, TokenCssBlocks, TokenCssOptions };
@@ -9533,23 +9533,68 @@ function unknownTokenReferences(css, alsoAllowed = []) {
9533
9533
  return tokenReferences(css).filter(name => !spec.has(name) && !extra.has(name));
9534
9534
  }
9535
9535
  const LEGACY_SCOPE = /:not\(\s*\.iox-es\s*\)/;
9536
- const RULE = /([^{}]+)\{([^{}]*)\}/g;
9536
+ /** Comments out: block comments, and SCSS `//` line comments (never the `//` of a `url(https://…)`). */
9537
+ const stripComments = (css) => String(css ?? '')
9538
+ .replace(/\/\*[\s\S]*?\*\//g, '')
9539
+ .replace(/(^|[^:\\])\/\/[^\n]*/g, '$1');
9540
+ /** A nested SCSS selector against its parent's: `&` is the parent; no `&` means a descendant. */
9541
+ function resolveNested(parent, selector) {
9542
+ if (!parent)
9543
+ return selector;
9544
+ return selector.split(',').map(part => {
9545
+ const own = part.trim();
9546
+ return own.includes('&') ? own.replace(/&/g, parent) : `${parent} ${own}`;
9547
+ }).join(', ');
9548
+ }
9537
9549
  /**
9538
9550
  * Tokens read by a LEGACY rule — any rule whose selector is scoped `:not(.iox-es)` (the pre-DS2 look
9539
9551
  * of a node that wears no element style, design-system §3b-bis). Such a rule must read NO token at
9540
9552
  * 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.
9553
+ * applied (D5). Returns `selector → names` for each offending rule; comments are ignored. SCSS
9554
+ * nesting is resolved (DS4.1): `.x:where(:not(.iox-es)) { &:hover { … } }` is the legacy rule
9555
+ * `.x:where(:not(.iox-es)):hover`, and an at-rule (`@media`) between them does not break the chain.
9542
9556
  */
9543
9557
  function legacyRuleTokenReferences(css) {
9544
- const text = String(css ?? '').replace(/\/\*[\s\S]*?\*\//g, '');
9558
+ const text = stripComments(css);
9545
9559
  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]);
9560
+ const stack = [];
9561
+ const report = (frame) => {
9562
+ if (!LEGACY_SCOPE.test(frame.selector))
9563
+ return;
9564
+ const tokens = tokenReferences(frame.body);
9551
9565
  if (tokens.length)
9552
- out.push({ selector, tokens });
9566
+ out.push({ selector: frame.selector, tokens });
9567
+ };
9568
+ let buffer = '';
9569
+ for (const ch of text) {
9570
+ if (ch === '{') {
9571
+ const head = buffer.trim().replace(/\s+/g, ' ');
9572
+ buffer = '';
9573
+ // Declarations before a nested block belong to the parent (`color: x; &:hover {`).
9574
+ const cut = head.lastIndexOf(';');
9575
+ const prelude = cut >= 0 ? head.slice(cut + 1).trim() : head;
9576
+ const parent = stack[stack.length - 1];
9577
+ if (parent && cut >= 0)
9578
+ parent.body += head.slice(0, cut + 1);
9579
+ const inherited = parent?.selector ?? '';
9580
+ const selector = prelude.startsWith('@') ? inherited : resolveNested(inherited, prelude);
9581
+ stack.push({ selector, body: '' });
9582
+ }
9583
+ else if (ch === '}') {
9584
+ const frame = stack.pop();
9585
+ if (frame) {
9586
+ frame.body += buffer;
9587
+ report(frame);
9588
+ }
9589
+ buffer = '';
9590
+ }
9591
+ else if (ch === ';' && stack.length) {
9592
+ stack[stack.length - 1].body += buffer + ch;
9593
+ buffer = '';
9594
+ }
9595
+ else {
9596
+ buffer += ch;
9597
+ }
9553
9598
  }
9554
9599
  return out;
9555
9600
  }
@@ -10416,28 +10461,107 @@ function cssString(value) {
10416
10461
  .replace(/>/g, '\\3e ');
10417
10462
  return `'${body}'`;
10418
10463
  }
10419
- const clampWeight = (w) => {
10464
+ /** The file extensions a studio may upload as a font, and the descriptor format each one IS (§5b). */
10465
+ const FONT_FILE_FORMATS = Object.freeze({
10466
+ woff2: 'woff2', woff: 'woff', ttf: 'truetype', otf: 'opentype',
10467
+ });
10468
+ const FONT_FILE_EXTENSIONS = Object.keys(FONT_FILE_FORMATS);
10469
+ /** For a file input's `accept` — the same list the server validates against. */
10470
+ const FONT_FILE_ACCEPT = FONT_FILE_EXTENSIONS.map(ext => `.${ext}`).join(',');
10471
+ /** The CSS weights a face can be declared at, for an editor that offers them. */
10472
+ const FONT_WEIGHTS = [100, 200, 300, 400, 500, 600, 700, 800, 900];
10473
+ const FONT_STYLES = ['normal', 'italic'];
10474
+ /** When two files declare the same face, the browser should try the smallest, best-supported first. */
10475
+ const FORMAT_PREFERENCE = ['woff2', 'woff', 'opentype', 'truetype'];
10476
+ const UPLOAD_SUFFIX = /-\d+$/;
10477
+ /** A stored key's original file name: the folder prefix and the `-<timestamp>` upload suffix removed. */
10478
+ function fontFileName(key) {
10479
+ const name = String(key ?? '');
10480
+ return name.substring(name.lastIndexOf('/') + 1).replace(UPLOAD_SUFFIX, '');
10481
+ }
10482
+ /** The descriptor format of a font file, from its extension — null for anything we do not serve. */
10483
+ function fontFormatOf(fileNameOrKey) {
10484
+ const ext = /\.([a-z0-9]+)$/i.exec(fontFileName(fileNameOrKey))?.[1]?.toLowerCase() ?? '';
10485
+ return FONT_FILE_FORMATS[ext] ?? null;
10486
+ }
10487
+ /** The family a file had before DS5 — its name without the extension. Legacy files with no parsed
10488
+ * name table keep it, so a page saved with `font-family: 'Rubik-Regular'` keeps its face (D5). */
10489
+ function fontFileStem(key) {
10490
+ return fontFileName(key).replace(/\.[^.]+$/, '');
10491
+ }
10492
+ /** A weight as CSS can use it: 100–900, a bad value → 400. */
10493
+ function normalizeFontWeight(w) {
10420
10494
  const n = Math.round(Number(w));
10421
10495
  return Number.isFinite(n) && n > 0 ? Math.min(MAX_WEIGHT, Math.max(MIN_WEIGHT, n)) : DEFAULT_WEIGHT;
10496
+ }
10497
+ function normalizeFontStyle(style) {
10498
+ return style === 'italic' ? 'italic' : 'normal';
10499
+ }
10500
+ /** CSS family names match case-insensitively; this is the one comparison key. */
10501
+ function fontFamilyKey(family) {
10502
+ return String(family ?? '').trim().toLowerCase();
10503
+ }
10504
+ const usableDescriptor = (d) => !!d && typeof d.family === 'string' && d.family.trim() !== '' && typeof d.url === 'string' && d.url !== '';
10505
+ const preference = (format) => {
10506
+ const i = FORMAT_PREFERENCE.indexOf(format);
10507
+ return i < 0 ? FORMAT_PREFERENCE.length : i;
10422
10508
  };
10423
- /** One `@font-face` per file, carrying its real weight and style — so bold is never synthesised. */
10509
+ /**
10510
+ * One `@font-face` per FACE (family × weight × style), carrying its real weight and style — so bold is
10511
+ * never synthesised. Files that declare the same face (a woff2 and a ttf of one cut) share one rule
10512
+ * whose `src` lists them best format first, which is what the two retired forks did per file stem.
10513
+ */
10424
10514
  function buildFontFaceCss(descriptors) {
10425
- return (descriptors ?? [])
10426
- .filter(d => !!d && typeof d.family === 'string' && d.family.trim() !== '' && typeof d.url === 'string' && d.url !== '')
10427
- .map(d => {
10428
- const format = FONT_FORMATS.includes(d.format) ? ` format(${cssString(d.format)})` : '';
10515
+ const faces = new Map();
10516
+ for (const d of (descriptors ?? []).filter(usableDescriptor)) {
10517
+ const family = d.family.trim();
10518
+ const weight = normalizeFontWeight(d.weight);
10519
+ const style = normalizeFontStyle(d.style);
10520
+ const id = `${fontFamilyKey(family)}|${weight}|${style}`;
10521
+ const face = faces.get(id) ?? { family, weight, style, files: [] };
10522
+ face.files.push(d);
10523
+ faces.set(id, face);
10524
+ }
10525
+ return [...faces.values()]
10526
+ .map(face => {
10527
+ const src = [...face.files]
10528
+ .sort((a, b) => preference(a.format) - preference(b.format))
10529
+ .map(d => `url(${cssString(d.url)})${FONT_FORMATS.includes(d.format) ? ` format(${cssString(d.format)})` : ''}`)
10530
+ .join(', ');
10429
10531
  return [
10430
10532
  '@font-face {',
10431
- ` font-family: ${cssString(d.family.trim())};`,
10432
- ` src: url(${cssString(d.url)})${format};`,
10433
- ` font-weight: ${clampWeight(d.weight)};`,
10434
- ` font-style: ${d.style === 'italic' ? 'italic' : 'normal'};`,
10533
+ ` font-family: ${cssString(face.family)};`,
10534
+ ` src: ${src};`,
10535
+ ` font-weight: ${face.weight};`,
10536
+ ` font-style: ${face.style};`,
10435
10537
  ' font-display: swap;',
10436
10538
  '}',
10437
10539
  ].join('\n');
10438
10540
  })
10439
10541
  .join('\n');
10440
10542
  }
10543
+ /** Files → families (§5b "Group into families"): the Files tile, the typeface dropdown and Design ▸ Type. */
10544
+ function groupFontFamilies(descriptors) {
10545
+ const groups = new Map();
10546
+ for (const d of (descriptors ?? []).filter(usableDescriptor)) {
10547
+ const key = fontFamilyKey(d.family);
10548
+ const group = groups.get(key) ?? { family: d.family.trim(), weights: [], styles: [], faces: [] };
10549
+ group.faces.push(d);
10550
+ groups.set(key, group);
10551
+ }
10552
+ return [...groups.values()]
10553
+ .map(g => {
10554
+ const faces = [...g.faces].sort((a, b) => normalizeFontWeight(a.weight) - normalizeFontWeight(b.weight)
10555
+ || normalizeFontStyle(a.style).localeCompare(normalizeFontStyle(b.style)));
10556
+ return {
10557
+ family: g.family,
10558
+ weights: [...new Set(faces.map(f => normalizeFontWeight(f.weight)))].sort((a, b) => a - b),
10559
+ styles: FONT_STYLES.filter(s => faces.some(f => normalizeFontStyle(f.style) === s)),
10560
+ faces,
10561
+ };
10562
+ })
10563
+ .sort((a, b) => a.family.localeCompare(b.family));
10564
+ }
10441
10565
  /** The family names in a CSS `font-family` stack, unquoted, in order. */
10442
10566
  function parseFontFamilies(stack) {
10443
10567
  return String(stack ?? '')
@@ -10484,6 +10608,70 @@ function fontsForTokens(tokens, opts = {}) {
10484
10608
  }
10485
10609
  return ids;
10486
10610
  }
10611
+ /** True when `stack` (a CSS `font-family` value) names `family` anywhere in it. */
10612
+ function stackNamesFamily(stack, family) {
10613
+ const key = fontFamilyKey(family);
10614
+ return key !== '' && parseFontFamilies(stack ?? '').some(f => fontFamilyKey(f) === key);
10615
+ }
10616
+ /**
10617
+ * The font tokens whose value names `family` — what the delete guard asks before an uploaded family's
10618
+ * last file goes (§5b): deleting it would silently drop those roles to their fallback.
10619
+ */
10620
+ function fontRoleReferences(tokens, family) {
10621
+ const rows = (tokens ?? []).filter(t => !!t && typeof t.name === 'string');
10622
+ return FONT_FAMILY_TOKENS.filter(role => rows.some(t => t.name === role && stackNamesFamily(t.value, family)));
10623
+ }
10624
+ /**
10625
+ * The face CSS font matching picks for `weight` from `faces` (CSS Fonts §5.2): 400 tries 500 next,
10626
+ * 500 tries 400; lighter weights look down first, heavier ones up. So an `h1` at 600 over a family
10627
+ * uploaded at 400 + 700 renders the real 700 cut — the one worth preloading.
10628
+ */
10629
+ function matchFontWeight(faces, weight) {
10630
+ if (!faces?.length)
10631
+ return null;
10632
+ const want = normalizeFontWeight(weight);
10633
+ const exact = faces.find(f => normalizeFontWeight(f.weight) === want);
10634
+ if (exact)
10635
+ return exact;
10636
+ const w = (f) => normalizeFontWeight(f.weight);
10637
+ const below = (max) => [...faces].filter(f => w(f) < max).sort((a, b) => w(b) - w(a))[0] ?? null;
10638
+ const above = (min) => [...faces].filter(f => w(f) > min).sort((a, b) => w(a) - w(b))[0] ?? null;
10639
+ if (want >= 400 && want <= 500) {
10640
+ const within = [...faces].filter(f => w(f) > want && w(f) <= 500).sort((a, b) => w(a) - w(b))[0];
10641
+ return within ?? below(want) ?? above(500);
10642
+ }
10643
+ return want < 400 ? (below(want) ?? above(want)) : (above(want) ?? below(want));
10644
+ }
10645
+ /** The roles a preload serves, and the weight tokens whose cuts render above the fold. */
10646
+ const PRELOAD_ROLES = [
10647
+ { role: 'font-display', weights: ['weight-display', 'weight-heading'], fallback: 700 },
10648
+ { role: 'font-body', weights: ['weight-body', 'weight-strong'], fallback: DEFAULT_WEIGHT },
10649
+ ];
10650
+ const PRELOAD_FORMAT = 'woff2';
10651
+ /**
10652
+ * The uploaded files the SSR head preloads (§5b — no flash of the fallback face): for the display and
10653
+ * body roles, the family each token names (its first uploaded family), the woff2 normal-style cut CSS
10654
+ * matching would pick for that role's weights. One file per weight that renders, never the family.
10655
+ */
10656
+ function fontPreloads(descriptors, tokens) {
10657
+ const families = groupFontFamilies(descriptors);
10658
+ if (!families.length)
10659
+ return [];
10660
+ const resolved = resolveTokenValues(tokens);
10661
+ const out = new Map();
10662
+ for (const { role, weights, fallback } of PRELOAD_ROLES) {
10663
+ const named = parseFontFamilies(resolved[role] ?? '').map(fontFamilyKey);
10664
+ const family = named.map(key => families.find(f => fontFamilyKey(f.family) === key)).find(Boolean);
10665
+ const faces = family?.faces.filter(f => f.format === PRELOAD_FORMAT && normalizeFontStyle(f.style) === 'normal') ?? [];
10666
+ for (const token of weights) {
10667
+ const weight = Number.parseInt(resolved[token] ?? '', 10);
10668
+ const face = matchFontWeight(faces, Number.isFinite(weight) ? weight : fallback);
10669
+ if (face)
10670
+ out.set(face.url, face);
10671
+ }
10672
+ }
10673
+ return [...out.values()];
10674
+ }
10487
10675
 
10488
10676
  /**
10489
10677
  * A flavor's COMPLETE token set for a studio's choices (design-system plan §3a/§3e). A flavor IS
@@ -10606,14 +10794,16 @@ function planFlavorRevert(current, snapshot) {
10606
10794
  snapNames.add(snap.name);
10607
10795
  const live = byName.get(snap.name);
10608
10796
  if (!live) {
10609
- plan.create.push({ name: snap.name, type: snap.type, value: snap.value, values: snap.values, source: snap.source, ...classification(snap) });
10797
+ plan.create.push({ name: snap.name, type: snap.type, value: snap.value, ...(snap.values?.dark ? { values: snap.values } : {}), source: snap.source, ...classification(snap) });
10610
10798
  continue;
10611
10799
  }
10612
10800
  const differs = live.value !== snap.value
10613
10801
  || (live.values?.dark ?? null) !== (snap.values?.dark ?? null)
10614
10802
  || (live.source ?? null) !== (snap.source ?? null);
10803
+ // `values` is ALWAYS sent: an absent key is dropped by JSON and a `$set` then keeps the
10804
+ // apply's dark override — a snapshot row with no dark must clear it, so it sends `{}`.
10615
10805
  if (differs && live._id) {
10616
- plan.update.push({ id: live._id, data: { value: snap.value, values: snap.values, source: snap.source } });
10806
+ plan.update.push({ id: live._id, data: { value: snap.value, values: snap.values ?? {}, source: snap.source } });
10617
10807
  }
10618
10808
  }
10619
10809
  for (const row of current ?? []) {
@@ -10632,7 +10822,9 @@ const sameValue = (row, e) => row.value === e.value && (row.values?.dark ?? '')
10632
10822
  * write is never touched: no `flavor:` stamp, unless it is a `user` row (a font the author CHOSE in
10633
10823
  * the design) whose value is exactly what the previous design wrote — that row is the design's own
10634
10824
  * choice, so a new choice may replace it. `group`/`tier` ride every write; a desired row's own
10635
- * `source` (a chosen font's `user`) wins over the pack stamp.
10825
+ * `source` (a chosen font's `user`) wins over the pack stamp. A machine-owned row that already holds
10826
+ * the desired value but another pack's stamp is RE-STAMPED (§10 C2 provenance: the stamp names the
10827
+ * design that owns the row, so the next switch judges it against the right baseline).
10636
10828
  */
10637
10829
  function planFlavorSeed(desired, existing, stamp, baseline) {
10638
10830
  const byName = new Map((existing ?? []).map(r => [r.name, r]));
@@ -10662,8 +10854,6 @@ function planFlavorSeed(desired, existing, stamp, baseline) {
10662
10854
  const sameAsDesired = row.value === def.value && (row.values?.dark ?? '') === (def.values?.dark ?? '');
10663
10855
  if (sameAsDesired && (row.source ?? '') === source)
10664
10856
  continue; // already exactly this
10665
- if (sameAsDesired && isSeeded(row) && source !== USER_TOKEN_SOURCE)
10666
- continue; // same value, another pack's stamp — nothing to write
10667
10857
  const untouched = !!old && sameValue(row, old);
10668
10858
  if ((untouched || sameAsDesired) && row._id) {
10669
10859
  plan.update.push({ id: row._id, data: { value: def.value, values: def.values ?? {}, source, ...classification(def) } });
@@ -10825,6 +11015,17 @@ function resolveDesignChoices(partial) {
10825
11015
  function packDesignChoices(flavorId) {
10826
11016
  return resolveDesignChoices({ flavorId });
10827
11017
  }
11018
+ /**
11019
+ * What picking a pack card means, given the design the studio wears: the applied pack → the applied
11020
+ * choices; another pack → that pack's defaults, CARRYING the author's chosen fonts (§5b — a font the
11021
+ * author chose survives a pack switch unless the author changes it). Both picker homes call this.
11022
+ */
11023
+ function choicesForPack(flavorId, applied) {
11024
+ const pack = flavorPackById(flavorId) ?? defaultPack();
11025
+ if (applied && resolveDesignChoices(applied).flavorId === pack.id)
11026
+ return resolveDesignChoices(applied);
11027
+ return resolveDesignChoices({ flavorId: pack.id, fonts: applied?.fonts });
11028
+ }
10828
11029
  /** A stored `settings.design` config → the resolved design. No row (or no config) → Crisp's defaults. */
10829
11030
  function readDesignConfig(config, hasRow = !!config) {
10830
11031
  const map = configMap(config);
@@ -10934,6 +11135,31 @@ function missingDesignRows(existing, choices) {
10934
11135
  const have = new Set((existing ?? []).map(r => r.name));
10935
11136
  return designTokenRows(choices).filter(r => !have.has(r.name));
10936
11137
  }
11138
+ /**
11139
+ * The builder-load REPAIR (DS4.1): rows the design owns and nobody touched — stamped by the current
11140
+ * pack, light value exactly the pack's — whose dark override differs from the pack's are rewritten.
11141
+ * iox-cms dropped `values.dark` on every write from F0 to DS4 (`values` was an implicit relation),
11142
+ * and a revert could leave the previous apply's dark behind; both leave a row whose light half is the
11143
+ * design's and whose dark half is not. No author edit can produce that shape — the Tokens panel edits
11144
+ * the light value only — so rewriting it heals existing orgs without the author doing anything.
11145
+ */
11146
+ function designTokenRepairs(existing, choices) {
11147
+ const pack = flavorPackById(choices.flavorId);
11148
+ if (!pack)
11149
+ return [];
11150
+ const stamp = flavorSourceStamp(pack.id);
11151
+ const desired = new Map(designTokenRows(choices).map(r => [r.name, r]));
11152
+ const out = [];
11153
+ for (const row of existing ?? []) {
11154
+ const want = desired.get(row.name);
11155
+ if (!want || !row._id || row.source !== stamp || want.source !== stamp || row.value !== want.value)
11156
+ continue;
11157
+ if ((row.values?.dark ?? '') === (want.values?.dark ?? ''))
11158
+ continue;
11159
+ out.push({ id: row._id, data: { value: want.value, values: want.values ?? {}, source: stamp, ...(want.group ? { group: want.group } : {}), ...(want.tier ? { tier: want.tier } : {}) } });
11160
+ }
11161
+ return out;
11162
+ }
10937
11163
  const LEGACY_ALIASES = SPEC_LEGACY_ALIASES;
10938
11164
  /**
10939
11165
  * Swiss → Crisp (spec `legacyAliases`; D1). Needed when the design row says a retired flavor, or when
@@ -10943,9 +11169,11 @@ const LEGACY_ALIASES = SPEC_LEGACY_ALIASES;
10943
11169
  *
10944
11170
  * - The look: `planDesignApply` from Swiss to Crisp — Swiss-stamped rows untouched since Swiss seeded
10945
11171
  * them (incl. F2's `surface` / `line` / `accent`) become Crisp's; edited ones are kept.
10946
- * - The aliases: `radius-control` / `radius-surface` / `space-control` / `space-stack` are rewritten to
10947
- * their alias reference ONLY while still machine-owned and untouched, so a node saved since F1 that
10948
- * reads `var(--iox-radius-control)` keeps resolving — now to Crisp's button radius.
11172
+ * - The aliases (`SPEC_LEGACY_ALIASES`: `radius-control` / `radius-surface` / `space-stack`) are rewritten
11173
+ * to their alias reference ONLY while still machine-owned and untouched, so a node saved since F1 that
11174
+ * reads `var(--iox-radius-control)` keeps resolving — now to Crisp's button radius. `space-control` is
11175
+ * NOT an alias: it is a v2 semantic token (`button-pad` reads it), and aliasing it closed a `var()`
11176
+ * cycle; it migrates like any other Swiss row.
10949
11177
  */
10950
11178
  function planSwissMigration(existing, stored) {
10951
11179
  const swissAliasRows = (existing ?? []).filter(r => r.name in LEGACY_ALIASES && SEED_STAMP.exec(r.source ?? '')?.[1] === SWISS_FLAVOR.id);
@@ -10972,6 +11200,34 @@ function planSwissMigration(existing, stored) {
10972
11200
  }
10973
11201
  return { choices, plan };
10974
11202
  }
11203
+ /**
11204
+ * Plan the removal of an uploaded family from the look: the roles naming it fall back to the pack's
11205
+ * own font (the design's pairing), as the guard promises — "Nothing else changes".
11206
+ */
11207
+ function planFontFamilyRemoval(existing, previous, family) {
11208
+ const roles = fontRoleReferences(existing, family);
11209
+ const fonts = Object.fromEntries(Object.entries(previous.choices.fonts ?? {})
11210
+ .filter(([, chosen]) => fontFamilyKey(chosen) !== fontFamilyKey(family)));
11211
+ const choices = { ...previous.choices };
11212
+ if (Object.keys(fonts).length)
11213
+ choices.fonts = fonts;
11214
+ else
11215
+ delete choices.fonts;
11216
+ if (!roles.length)
11217
+ return { roles, choices: previous.choices, plan: { create: [], update: [], kept: [] }, fallbacks: [] };
11218
+ const plan = planDesignApply(existing, previous, choices);
11219
+ const desired = new Map(designTokenRows(choices).map(r => [r.name, r]));
11220
+ const planned = new Set(plan.update.map(u => u.id));
11221
+ for (const row of existing ?? []) {
11222
+ const want = desired.get(row.name);
11223
+ if (!row._id || !want || planned.has(row._id) || !roles.includes(row.name))
11224
+ continue;
11225
+ plan.update.push({ id: row._id, data: { value: want.value, values: want.values ?? {}, source: want.source } });
11226
+ plan.kept = plan.kept.filter(name => name !== row.name);
11227
+ }
11228
+ const fallbacks = roles.map(role => ({ role, family: parseFontFamilies(desired.get(role)?.value ?? '')[0] ?? '' }));
11229
+ return { roles, choices, plan, fallbacks };
11230
+ }
10975
11231
 
10976
11232
  /**
10977
11233
  * @vectoriox/iox-ui/design-core — the shared, FRAMEWORK-FREE core of the design system
@@ -10988,5 +11244,5 @@ function planSwissMigration(existing, stored) {
10988
11244
  * Generated bundle index. Do not edit.
10989
11245
  */
10990
11246
 
10991
- 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, emptyImagePlaceholder, 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 };
11247
+ 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, FONT_FILE_ACCEPT, FONT_FILE_EXTENSIONS, FONT_FILE_FORMATS, FONT_STYLES, FONT_WEIGHTS, 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, choicesForPack, colorSchemeClass, colorSchemeOf, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, defaultElementStyleFor, deriveAccentTokens, deriveBrandTokens, designBaseline, designConfigEntries, designModeOf, designTokenRepairs, designTokenRows, elementStyleById, elementStyleClass, elementStylesFor, emptyImageLabelSrc, emptyImagePlaceholder, emptyImageSrc, flavorPackById, flavorSourceStamp, fontFamilyKey, fontFileName, fontFileStem, fontFormatOf, fontPreloads, fontRoleReferences, fontsForTokens, googleFontHref, groupFontFamilies, hasElementStyleMarker, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, isRetiredFlavorId, legacyRuleTokenReferences, matchFontWeight, missingDesignRows, nodeDesignClasses, normalizeFontStyle, normalizeFontWeight, oklabToRgb, ownedTraitStatus, packDesignChoices, parseFontFamilies, parseHexColor, planDesignApply, planFlavorRevert, planFlavorSeed, planFontFamilyRemoval, planSwissMigration, readDesignConfig, relativeLuminance, resolveDesignChoices, resolveTokenReferences, resolveTokenValues, revertAvailable, rgbToOklab, sameDesignChoices, specTokenNames, springToLinear, stackNamesFamily, styleKeyToKebab, switchElementStyle, syncNodeDesignClasses, toHexColor, tokenReferences, tokensToDarkVarMap, tokensToVarMap, unknownTokenReferences, userFontStack, validateFlavorPack, wcagContrastRatio };
10992
11248
  //# sourceMappingURL=vectoriox-iox-ui-design-core.mjs.map