@vectoriox/iox-ui 5.11.0 → 5.12.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.
@@ -8193,10 +8193,17 @@ declare const REDUCED_TRANSPARENCY_OVERRIDES: Readonly<Record<string, string>>;
8193
8193
  declare function tokensToVarMap(tokens: ReadonlyArray<DesignTokenLike> | null | undefined): Record<string, string>;
8194
8194
  /** Only the tokens that carry a dark override, as `{ 'iox-name': darkValue }`. */
8195
8195
  declare function tokensToDarkVarMap(tokens: ReadonlyArray<DesignTokenLike> | null | undefined): Record<string, string>;
8196
- type Mode = 'light' | 'dark';
8196
+ /** A compiled colour mode — the contract's modes minus `auto`, which is a compile CHOICE, not values. */
8197
+ type ResolvedMode = Exclude<DesignMode, 'auto'>;
8197
8198
  /** Every token's ROOT value in one mode, with `var(--iox-…)` references resolved against the same
8198
8199
  * set (recursively; a reference cycle or an unknown token is left as written). */
8199
- declare function resolveTokenValues(tokens: ReadonlyArray<DesignTokenLike> | null | undefined, mode?: Mode): Record<string, string>;
8200
+ declare function resolveTokenValues(tokens: ReadonlyArray<DesignTokenLike> | null | undefined, mode?: ResolvedMode): Record<string, string>;
8201
+ /**
8202
+ * `value` with every `var(--iox-…)` replaced by that token's resolved root value in `mode` — what
8203
+ * the Style panel's Detach writes, so the node keeps exactly what it showed as a literal. An unknown
8204
+ * token falls back to its declared fallback, else stays as written.
8205
+ */
8206
+ declare function resolveTokenReferences(value: string, tokens: ReadonlyArray<DesignTokenLike> | null | undefined, mode?: ResolvedMode): string;
8200
8207
  /**
8201
8208
  * Section colour schemes (design-system plan §3c). Two rules, both about WHERE a custom property
8202
8209
  * resolves:
@@ -8217,17 +8224,11 @@ declare function compileTokenCssBlocks(tokens: ReadonlyArray<DesignTokenLike> |
8217
8224
  declare function compileTokenCss(tokens: ReadonlyArray<DesignTokenLike> | null | undefined, opts?: TokenCssOptions): string;
8218
8225
 
8219
8226
  /**
8220
- * Flavors (flavors-and-theming-plan.md, F1): the design language a studio launches with,
8221
- * expressed as data. THE invariant — a flavor never writes a literal into a node; it writes
8222
- * tokens, and element defaults that REFERENCE tokens. To that end a pack's `defaults` map
8223
- * holds TOKEN NAMES, not CSS values: the resolver builds `var(--iox-<name>, <packValue>)`,
8224
- * so smuggling a literal in is impossible by construction, and the validator only has to
8225
- * check that every referenced token exists.
8226
- *
8227
- * The fallback is the bootstrap: until F2 seeds the org's `designtokens` rows, no `--iox-*`
8228
- * var is defined anywhere, and `var(--iox-radius-control, 4px)` must still paint identically
8229
- * in the builder canvas and the SSR page. Once tokens ARE seeded (or re-themed), the var wins
8230
- * over the fallback everywhere — which is exactly the §3 propagation story.
8227
+ * Flavors v1 (flavors-and-theming-plan.md, F1–F2): a pack is its TOKENS plus the accent hue
8228
+ * opinion the brand derivation reads. The per-type `defaults` map and its resolvers are gone
8229
+ * (design-system plan §3e, DS2): a new element is born wearing an ELEMENT STYLE
8230
+ * (`element-styles.ts`), which references tokens and is flavor-independent. Swiss survives only
8231
+ * until DS4 migrates orgs to Crisp.
8231
8232
  */
8232
8233
  interface FlavorTokenDef {
8233
8234
  name: string;
@@ -8247,36 +8248,121 @@ interface FlavorPack {
8247
8248
  label: string;
8248
8249
  blurb: string;
8249
8250
  tokens: FlavorTokenDef[];
8250
- /** type → styleTrait → TOKEN NAME (never a CSS value — see the module header). */
8251
- defaults: Record<string, Record<string, string>>;
8252
8251
  /** F2: the accent's hue rotation (degrees in OKLab) from the brand seed — a pack opinion. */
8253
8252
  accentHueDeg?: number;
8254
8253
  }
8255
- /** trait → `var(--iox-<token>, <packValue>)` for one node type. Unknown type → {}. */
8256
- declare function resolveFlavorDefaults(type: string, pack: FlavorPack | null | undefined): Record<string, string>;
8254
+ /** A pack's own sanity: every token row names itself and carries a value. Returns errors. */
8255
+ declare function validateFlavorPack(pack: FlavorPack): string[];
8256
+ /**
8257
+ * Swiss — the F1 pack: four tokens (control/surface radius, control/stack spacing) and the hue
8258
+ * opinion F2's brand step reads. Kept until DS4 migrates `flavorId: swiss` orgs to Crisp; its four
8259
+ * rows are the spec's `legacyAliases`.
8260
+ */
8261
+ declare const SWISS_FLAVOR: FlavorPack;
8262
+
8257
8263
  /**
8258
- * The ENGINE's half of the cascade: kebab `prop: var(…)` declarations for every pack default
8259
- * the node's persisted styleProps does NOT set. Appended beside renderDefaultDeclarations —
8260
- * same guard-on-undefined discipline, so a node saved post-F1 (var already serialised via the
8261
- * builder's trait defaults) is untouched, and a legacy node picks the pack default up at
8262
- * render time. Node styleProps always wins.
8264
+ * Element styles — the author's "classes" (design-system plan §3b). A named look is an EXACT trait
8265
+ * map written into a node; every value is a `var(--iox-…)` reference or a flavor-independent
8266
+ * keyword, so a re-theme rewrites token rows and every node wearing the style follows. No renderer
8267
+ * reads `elementStyle`: it records which look a node wears, for the panel row, Reset, adopt and AI.
8268
+ *
8269
+ * Works on two node shapes through one minimal interface, so the builder (a ComponentNode whose
8270
+ * values live in `styleTraits[].traits[].default`) and the server-side composer (a plain serialized
8271
+ * layout node with `styleProps`) share every function here.
8263
8272
  */
8264
- declare function flavorDefaultDeclarations(type: string, styleProps: Record<string, any> | null | undefined, pack: FlavorPack | null | undefined): string[];
8273
+ declare enum ElementStyleState {
8274
+ Hover = "hover",
8275
+ Active = "active",
8276
+ Focus = "focus"
8277
+ }
8278
+ declare const ELEMENT_STYLE_STATES: ReadonlyArray<ElementStyleState>;
8279
+ interface ElementStyle {
8280
+ readonly id: string;
8281
+ readonly label: string;
8282
+ readonly group: string;
8283
+ readonly appliesTo: ReadonlyArray<string>;
8284
+ readonly base: Readonly<Record<string, string>>;
8285
+ readonly hover?: Readonly<Record<string, string>>;
8286
+ readonly active?: Readonly<Record<string, string>>;
8287
+ readonly focus?: Readonly<Record<string, string>>;
8288
+ }
8289
+ declare const ELEMENT_STYLES: ReadonlyArray<ElementStyle>;
8290
+ /** Legacy `Button.variant` → the element style it maps to (adopt, DS7). */
8291
+ declare const BUTTON_VARIANT_TO_STYLE: Readonly<Record<string, string>>;
8292
+ interface PalettePreset {
8293
+ readonly label: string;
8294
+ readonly type: string;
8295
+ readonly elementStyle: string;
8296
+ readonly traits?: Readonly<Record<string, string>>;
8297
+ readonly styleProps: Readonly<Record<string, string>>;
8298
+ }
8299
+ /** Palette entries that are a Container wearing an element style (Card · Panel · Navigation bar). */
8300
+ declare const PALETTE_PRESETS: ReadonlyArray<PalettePreset>;
8301
+ declare function elementStyleById(id: string | null | undefined): ElementStyle | undefined;
8302
+ /** The styles a node of `type` may wear — the Style panel's select. */
8303
+ declare function elementStylesFor(type: string): ElementStyle[];
8304
+ /** The minimal shape both a builder node and a serialized layout node satisfy. */
8305
+ interface ElementStyleTraitLike {
8306
+ name: string;
8307
+ default?: unknown;
8308
+ }
8309
+ interface ElementStyleTarget {
8310
+ type: string;
8311
+ elementStyle?: string;
8312
+ /** Builder shape: values live in `trait.default`. Wins when present. */
8313
+ styleTraits?: ReadonlyArray<{
8314
+ traits: ElementStyleTraitLike[];
8315
+ }>;
8316
+ /** Serialized shape: a flat map. Created on first write when the target has no `styleTraits`. */
8317
+ styleProps?: Record<string, unknown>;
8318
+ stateStyles?: Partial<Record<string, Record<string, unknown>>>;
8319
+ }
8320
+ /** One trait an element style owns: a base trait, or a trait inside a pseudo-state. */
8321
+ interface OwnedTrait {
8322
+ trait: string;
8323
+ state?: ElementStyleState;
8324
+ }
8325
+ declare enum OwnedTraitLink {
8326
+ /** The node still holds the style's value. */
8327
+ Linked = "linked",
8328
+ /** The author replaced it on this node. */
8329
+ Changed = "changed"
8330
+ }
8331
+ interface OwnedTraitStatus extends OwnedTrait {
8332
+ expected: string;
8333
+ actual: unknown;
8334
+ status: OwnedTraitLink;
8335
+ }
8336
+ interface ApplyElementStyleResult {
8337
+ applied: boolean;
8338
+ /** Base traits the target has no slot for — a builder node silently drops these, so a spec
8339
+ * asserts this list is empty for every style × every type it applies to. */
8340
+ unknownTraits: string[];
8341
+ }
8342
+ interface SwitchElementStyleResult {
8343
+ applied: boolean;
8344
+ /** Traits the old style owned whose value the author had changed — kept, never overwritten. */
8345
+ kept: OwnedTrait[];
8346
+ }
8347
+ /** Write the style's exact base map and its hover/active/focus maps into the target. */
8348
+ declare function applyElementStyle(target: ElementStyleTarget, id: string): ApplyElementStyleResult;
8265
8349
  /**
8266
- * The CI gate (flavors plan §10 C4): a pack may only name types and traits that exist in the
8267
- * live component registry, and tokens it defines itself. Returns human-readable errors; a
8268
- * consumer spec feeds it the REAL registry and asserts [].
8350
+ * The element style a NEW node of `type` is born wearing (the spec's `defaultElementStyle`), or null
8351
+ * when the type has none. `traits` is the node's own trait values (props) — a Heading's default
8352
+ * follows its `level`; a Heading with no level matches no rule.
8269
8353
  */
8270
- declare function validateFlavorPack(pack: FlavorPack, knownTraitsByType: Record<string, ReadonlySet<string> | string[]>): string[];
8354
+ declare function defaultElementStyleFor(type: string, traits?: Readonly<Record<string, unknown>>): string | null;
8355
+ /** Per owned trait: does the node still hold the style's value (linked) or the author's (changed)? */
8356
+ declare function ownedTraitStatus(target: ElementStyleTarget, id: string): OwnedTraitStatus[];
8271
8357
  /**
8272
- * Swiss — the default pack (flavors plan §8: "the flavor that can never embarrass anyone").
8273
- * Deliberately SMALL at F1: radius + control padding on the clickables, surface radius on Card.
8274
- * Colour, type and motion arrive with the brand step (F2) — a colour default here would repaint
8275
- * legacy pages before anyone chose anything.
8358
+ * Move a node from one style to another, rewriting ONLY what the old style owned:
8359
+ * - an owned trait still holding the old style's value is released — a base trait goes back to
8360
+ * `baseline[trait]` (the type's unstyled value; deleted when the baseline has none), a state trait
8361
+ * is removed from its state map;
8362
+ * - an owned trait the author changed is KEPT, and the new style does not overwrite it.
8363
+ * Then the new style is written. Traits neither style owns are never touched.
8276
8364
  */
8277
- declare const SWISS_FLAVOR: FlavorPack;
8278
- /** What both renderers use until F2 lets an org choose. */
8279
- declare const DEFAULT_FLAVOR: FlavorPack;
8365
+ declare function switchElementStyle(target: ElementStyleTarget, fromId: string | null | undefined, toId: string, baseline?: Readonly<Record<string, unknown>>): SwitchElementStyleResult;
8280
8366
 
8281
8367
  /**
8282
8368
  * BRAND DERIVATION (flavors plan §6, F2): one colour in, an asserted accessible ramp out.
@@ -8456,5 +8542,5 @@ declare function parseFontFamilies(stack: string): string[];
8456
8542
  */
8457
8543
  declare function fontsForTokens(tokens: ReadonlyArray<DesignTokenLike> | null | undefined): string[];
8458
8544
 
8459
- export { ACCENT_TOKEN_NAMES, DEFAULT_FLAVOR, DESIGN_TOKEN_GROUPS, DESIGN_TOKEN_TIERS, DESIGN_TOKEN_TYPES, FLAVOR_SPEC_VERSION, FONT_CATALOG, FONT_FAMILY_TOKENS, 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, buildFlavorTokens, buildFontFaceCss, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, deriveAccentTokens, deriveBrandTokens, flavorDefaultDeclarations, flavorSourceStamp, fontsForTokens, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, oklabToRgb, parseFontFamilies, parseHexColor, planFlavorRevert, planFlavorSeed, relativeLuminance, resolveFlavorDefaults, resolveTokenValues, revertAvailable, rgbToOklab, springToLinear, styleKeyToKebab, toHexColor, tokensToDarkVarMap, tokensToVarMap, userFontStack, validateFlavorPack, wcagContrastRatio };
8460
- export type { AccentBackgrounds, AccentTokenName, DensityId, DesignChoices, DesignMode, DesignTokenGroup, DesignTokenLike, DesignTokenRow, DesignTokenTier, DesignTokenType, FlavorPack, FlavorPackV2, FlavorRevertPlan, FlavorRevertSnapshot, FlavorSeedPlan, FlavorSpecToken, FlavorTokenDef, FontDescriptor, FontFormat, FontPair, FontRoleToken, FontStyle, LinearSpring, MotionId, OkLab, Rgb, SchemeId, ShapeId, StoredDesignTokenRow, TokenCssBlocks, TokenCssOptions };
8545
+ export { ACCENT_TOKEN_NAMES, BUTTON_VARIANT_TO_STYLE, DESIGN_TOKEN_GROUPS, DESIGN_TOKEN_TIERS, DESIGN_TOKEN_TYPES, ELEMENT_STYLES, 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, buildFlavorTokens, buildFontFaceCss, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, defaultElementStyleFor, deriveAccentTokens, deriveBrandTokens, elementStyleById, elementStylesFor, flavorSourceStamp, fontsForTokens, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, oklabToRgb, ownedTraitStatus, parseFontFamilies, parseHexColor, planFlavorRevert, planFlavorSeed, relativeLuminance, resolveTokenReferences, resolveTokenValues, revertAvailable, rgbToOklab, springToLinear, styleKeyToKebab, switchElementStyle, toHexColor, tokensToDarkVarMap, tokensToVarMap, userFontStack, validateFlavorPack, wcagContrastRatio };
8546
+ export type { AccentBackgrounds, AccentTokenName, ApplyElementStyleResult, DensityId, DesignChoices, DesignMode, DesignTokenGroup, DesignTokenLike, DesignTokenRow, DesignTokenTier, DesignTokenType, ElementStyle, ElementStyleTarget, ElementStyleTraitLike, FlavorPack, FlavorPackV2, FlavorRevertPlan, FlavorRevertSnapshot, FlavorSeedPlan, FlavorSpecToken, FlavorTokenDef, FontDescriptor, FontFormat, FontPair, FontRoleToken, FontStyle, LinearSpring, MotionId, OkLab, OwnedTrait, OwnedTraitStatus, PalettePreset, ResolvedMode, Rgb, SchemeId, ShapeId, StoredDesignTokenRow, SwitchElementStyleResult, TokenCssBlocks, TokenCssOptions };
@@ -9526,10 +9526,12 @@ function tokensToDarkVarMap(tokens) {
9526
9526
  }
9527
9527
  const TOKEN_REF = /var\(\s*--iox-([\w-]+)\s*(?:,([^()]*))?\)/g;
9528
9528
  const PLACEHOLDER = /\{\{\s*([\w-]+)\s*\}\}/g;
9529
- const modeValue = (t, mode) => (mode === 'dark' ? darkOf(t) ?? t.value : t.value);
9529
+ const LIGHT = 'light';
9530
+ const DARK = 'dark';
9531
+ const modeValue = (t, mode) => (mode === DARK ? darkOf(t) ?? t.value : t.value);
9530
9532
  /** Every token's ROOT value in one mode, with `var(--iox-…)` references resolved against the same
9531
9533
  * set (recursively; a reference cycle or an unknown token is left as written). */
9532
- function resolveTokenValues(tokens, mode = 'light') {
9534
+ function resolveTokenValues(tokens, mode = LIGHT) {
9533
9535
  const raw = new Map(usableTokens(tokens).map(t => [t.name, modeValue(t, mode)]));
9534
9536
  const out = {};
9535
9537
  const resolve = (name, trail) => {
@@ -9548,6 +9550,15 @@ function resolveTokenValues(tokens, mode = 'light') {
9548
9550
  }
9549
9551
  return out;
9550
9552
  }
9553
+ /**
9554
+ * `value` with every `var(--iox-…)` replaced by that token's resolved root value in `mode` — what
9555
+ * the Style panel's Detach writes, so the node keeps exactly what it showed as a literal. An unknown
9556
+ * token falls back to its declared fallback, else stays as written.
9557
+ */
9558
+ function resolveTokenReferences(value, tokens, mode = LIGHT) {
9559
+ const resolved = resolveTokenValues(tokens, mode);
9560
+ return value.replace(TOKEN_REF, (ref, name, fallback) => resolved[name] ?? (fallback !== undefined ? fallback.trim() : ref));
9561
+ }
9551
9562
  /** A scheme value with its `{{token}}` placeholders replaced by resolved root values — or null
9552
9563
  * when a placeholder names a token the set does not have (the declaration is then omitted). */
9553
9564
  function substitute(template, resolved) {
@@ -9580,7 +9591,7 @@ function compileSchemeCss(tokens, scope) {
9580
9591
  const rows = usableTokens(tokens);
9581
9592
  if (!rows.length)
9582
9593
  return '';
9583
- const resolved = resolveTokenValues(rows, 'light');
9594
+ const resolved = resolveTokenValues(rows, LIGHT);
9584
9595
  return nonDefaultSchemes().map(([id, set]) => rule(schemeSelector(id, scope), [
9585
9596
  ...schemeEntries(set, resolved),
9586
9597
  ...rows.filter(t => !(t.name in set) && t.value.includes(REFERENCE))
@@ -9590,8 +9601,8 @@ function compileSchemeCss(tokens, scope) {
9590
9601
  /** The dark twin of both scheme rules: a re-declared token with a dark value would otherwise pin
9591
9602
  * the LIGHT value inside the section, and a `{{token}}` swap was resolved from LIGHT values. */
9592
9603
  function darkSchemeRules(rows, scope) {
9593
- const light = resolveTokenValues(rows, 'light');
9594
- const dark = resolveTokenValues(rows, 'dark');
9604
+ const light = resolveTokenValues(rows, LIGHT);
9605
+ const dark = resolveTokenValues(rows, DARK);
9595
9606
  const redeclared = rows.filter(t => {
9596
9607
  const d = darkOf(t);
9597
9608
  return d !== null && (t.value.includes(REFERENCE) || d.includes(REFERENCE));
@@ -9633,74 +9644,21 @@ function compileTokenCss(tokens, opts = {}) {
9633
9644
  return [base, dark].filter(Boolean).join('\n');
9634
9645
  }
9635
9646
 
9636
- /** trait → `var(--iox-<token>, <packValue>)` for one node type. Unknown type → {}. */
9637
- function resolveFlavorDefaults(type, pack) {
9638
- const map = pack?.defaults?.[type];
9639
- if (!map)
9640
- return {};
9641
- const byName = new Map((pack.tokens ?? []).map(t => [t.name, t.value]));
9642
- const out = {};
9643
- for (const [trait, tokenName] of Object.entries(map)) {
9644
- const fallback = byName.get(tokenName);
9645
- if (fallback === undefined)
9646
- continue; // validator fails the build; runtime stays safe
9647
- out[trait] = `var(--iox-${tokenName}, ${fallback})`;
9648
- }
9649
- return out;
9650
- }
9651
- /**
9652
- * The ENGINE's half of the cascade: kebab `prop: var(…)` declarations for every pack default
9653
- * the node's persisted styleProps does NOT set. Appended beside renderDefaultDeclarations —
9654
- * same guard-on-undefined discipline, so a node saved post-F1 (var already serialised via the
9655
- * builder's trait defaults) is untouched, and a legacy node picks the pack default up at
9656
- * render time. Node styleProps always wins.
9657
- */
9658
- function flavorDefaultDeclarations(type, styleProps, pack) {
9659
- const sp = styleProps ?? {};
9660
- const out = [];
9661
- for (const [trait, value] of Object.entries(resolveFlavorDefaults(type, pack))) {
9662
- if (sp[trait] === undefined)
9663
- out.push(`${styleKeyToKebab(trait)}: ${value}`);
9664
- }
9665
- return out;
9666
- }
9667
- /**
9668
- * The CI gate (flavors plan §10 C4): a pack may only name types and traits that exist in the
9669
- * live component registry, and tokens it defines itself. Returns human-readable errors; a
9670
- * consumer spec feeds it the REAL registry and asserts [].
9671
- */
9672
- function validateFlavorPack(pack, knownTraitsByType) {
9647
+ /** A pack's own sanity: every token row names itself and carries a value. Returns errors. */
9648
+ function validateFlavorPack(pack) {
9673
9649
  const errors = [];
9674
- const tokenNames = new Set((pack.tokens ?? []).map(t => t.name));
9675
9650
  for (const t of pack.tokens ?? []) {
9676
9651
  if (!t.name)
9677
9652
  errors.push(`${pack.id}: token with empty name`);
9678
9653
  if (!t.value)
9679
9654
  errors.push(`${pack.id}: token "${t.name}" has no value`);
9680
9655
  }
9681
- for (const [type, traits] of Object.entries(pack.defaults ?? {})) {
9682
- const known = knownTraitsByType[type];
9683
- if (!known) {
9684
- errors.push(`${pack.id}: unknown component type "${type}"`);
9685
- continue;
9686
- }
9687
- const knownSet = known instanceof Set ? known : new Set(known);
9688
- for (const [trait, tokenName] of Object.entries(traits)) {
9689
- if (!knownSet.has(trait))
9690
- errors.push(`${pack.id}: "${type}" has no style trait "${trait}"`);
9691
- if (!tokenNames.has(tokenName))
9692
- errors.push(`${pack.id}: "${type}.${trait}" references undefined token "${tokenName}"`);
9693
- if (/[:(]|^#|\d/.test(tokenName))
9694
- errors.push(`${pack.id}: "${type}.${trait}" must name a token, not a value ("${tokenName}")`);
9695
- }
9696
- }
9697
9656
  return errors;
9698
9657
  }
9699
9658
  /**
9700
- * Swiss — the default pack (flavors plan §8: "the flavor that can never embarrass anyone").
9701
- * Deliberately SMALL at F1: radius + control padding on the clickables, surface radius on Card.
9702
- * Colour, type and motion arrive with the brand step (F2) — a colour default here would repaint
9703
- * legacy pages before anyone chose anything.
9659
+ * Swiss — the F1 pack: four tokens (control/surface radius, control/stack spacing) and the hue
9660
+ * opinion F2's brand step reads. Kept until DS4 migrates `flavorId: swiss` orgs to Crisp; its four
9661
+ * rows are the spec's `legacyAliases`.
9704
9662
  */
9705
9663
  const SWISS_FLAVOR = {
9706
9664
  id: 'swiss',
@@ -9713,20 +9671,203 @@ const SWISS_FLAVOR = {
9713
9671
  { name: 'space-control', value: '10px 18px', type: 'spacing', group: 'space' },
9714
9672
  { name: 'space-stack', value: '12px', type: 'spacing', group: 'space' },
9715
9673
  ],
9716
- defaults: {
9717
- Button: { borderRadius: 'radius-control', padding: 'space-control' },
9718
- ButtonBlock: { borderRadius: 'radius-control', padding: 'space-control' },
9719
- Card: { borderRadius: 'radius-surface' },
9720
- },
9721
- // F2: Swiss turns the seed toward a cool counterpoint. NOTE deliberately: the brand tokens
9722
- // deriveBrandTokens() emits are SEEDED per org — they do not join `defaults` here, because a
9723
- // colour default repaints legacy nodes whose saved styleProps lack the key (the engine fills
9724
- // missing keys from config defaults). Coloured element defaults are a Swiss v2 upgrade
9725
- // decision (§10 C2), taken with the picker in front of the author — never a side effect.
9674
+ // F2: Swiss turns the seed toward a cool counterpoint.
9726
9675
  accentHueDeg: 150,
9727
9676
  };
9728
- /** What both renderers use until F2 lets an org choose. */
9729
- const DEFAULT_FLAVOR = SWISS_FLAVOR;
9677
+
9678
+ /**
9679
+ * Element styles — the author's "classes" (design-system plan §3b). A named look is an EXACT trait
9680
+ * map written into a node; every value is a `var(--iox-…)` reference or a flavor-independent
9681
+ * keyword, so a re-theme rewrites token rows and every node wearing the style follows. No renderer
9682
+ * reads `elementStyle`: it records which look a node wears, for the panel row, Reset, adopt and AI.
9683
+ *
9684
+ * Works on two node shapes through one minimal interface, so the builder (a ComponentNode whose
9685
+ * values live in `styleTraits[].traits[].default`) and the server-side composer (a plain serialized
9686
+ * layout node with `styleProps`) share every function here.
9687
+ */
9688
+ var ElementStyleState;
9689
+ (function (ElementStyleState) {
9690
+ ElementStyleState["Hover"] = "hover";
9691
+ ElementStyleState["Active"] = "active";
9692
+ ElementStyleState["Focus"] = "focus";
9693
+ })(ElementStyleState || (ElementStyleState = {}));
9694
+ const ELEMENT_STYLE_STATES = [ElementStyleState.Hover, ElementStyleState.Active, ElementStyleState.Focus];
9695
+ const ELEMENT_STYLES = SPEC_ELEMENT_STYLES;
9696
+ /** Legacy `Button.variant` → the element style it maps to (adopt, DS7). */
9697
+ const BUTTON_VARIANT_TO_STYLE = SPEC_BUTTON_VARIANT_TO_STYLE;
9698
+ /** Palette entries that are a Container wearing an element style (Card · Panel · Navigation bar). */
9699
+ const PALETTE_PRESETS = SPEC_PALETTE_PRESETS;
9700
+ const BY_ID = new Map(ELEMENT_STYLES.map(s => [s.id, s]));
9701
+ function elementStyleById(id) {
9702
+ return id ? BY_ID.get(id) : undefined;
9703
+ }
9704
+ /** The styles a node of `type` may wear — the Style panel's select. */
9705
+ function elementStylesFor(type) {
9706
+ return ELEMENT_STYLES.filter(s => s.appliesTo.includes(type));
9707
+ }
9708
+ var OwnedTraitLink;
9709
+ (function (OwnedTraitLink) {
9710
+ /** The node still holds the style's value. */
9711
+ OwnedTraitLink["Linked"] = "linked";
9712
+ /** The author replaced it on this node. */
9713
+ OwnedTraitLink["Changed"] = "changed";
9714
+ })(OwnedTraitLink || (OwnedTraitLink = {}));
9715
+ const usesTraits = (t) => Array.isArray(t.styleTraits);
9716
+ function findTrait(target, name) {
9717
+ for (const group of target.styleTraits ?? []) {
9718
+ const hit = group.traits.find(tr => tr.name === name);
9719
+ if (hit)
9720
+ return hit;
9721
+ }
9722
+ return undefined;
9723
+ }
9724
+ function readBase(target, name) {
9725
+ return usesTraits(target) ? findTrait(target, name)?.default : target.styleProps?.[name];
9726
+ }
9727
+ /** False when a builder node has no such trait — the value would be dropped. */
9728
+ function writeBase(target, name, value) {
9729
+ if (usesTraits(target)) {
9730
+ const trait = findTrait(target, name);
9731
+ if (!trait)
9732
+ return false;
9733
+ trait.default = value;
9734
+ return true;
9735
+ }
9736
+ target.styleProps = target.styleProps ?? {};
9737
+ if (value === undefined)
9738
+ delete target.styleProps[name];
9739
+ else
9740
+ target.styleProps[name] = value;
9741
+ return true;
9742
+ }
9743
+ function readState(target, state, name) {
9744
+ return target.stateStyles?.[state]?.[name];
9745
+ }
9746
+ function writeState(target, state, name, value) {
9747
+ target.stateStyles = target.stateStyles ?? {};
9748
+ target.stateStyles[state] = { ...(target.stateStyles[state] ?? {}), [name]: value };
9749
+ }
9750
+ function deleteState(target, state, name) {
9751
+ const map = target.stateStyles?.[state];
9752
+ if (!map)
9753
+ return;
9754
+ const { [name]: _drop, ...rest } = map;
9755
+ if (Object.keys(rest).length)
9756
+ target.stateStyles[state] = rest;
9757
+ else
9758
+ delete target.stateStyles[state];
9759
+ }
9760
+ /** Every (state, trait, value) a style owns, base first. */
9761
+ function ownedEntries(style) {
9762
+ const out = [];
9763
+ for (const [trait, value] of Object.entries(style.base))
9764
+ out.push({ trait, value });
9765
+ for (const state of ELEMENT_STYLE_STATES) {
9766
+ for (const [trait, value] of Object.entries(style[state] ?? {}))
9767
+ out.push({ trait, state, value });
9768
+ }
9769
+ return out;
9770
+ }
9771
+ const sameOwned = (a, b) => a.trait === b.trait && a.state === b.state;
9772
+ function writeEntries(target, style, skip = []) {
9773
+ const unknown = [];
9774
+ for (const entry of ownedEntries(style)) {
9775
+ if (skip.some(s => sameOwned(s, entry)))
9776
+ continue;
9777
+ if (entry.state)
9778
+ writeState(target, entry.state, entry.trait, entry.value);
9779
+ else if (!writeBase(target, entry.trait, entry.value))
9780
+ unknown.push(entry.trait);
9781
+ }
9782
+ target.elementStyle = style.id;
9783
+ return unknown;
9784
+ }
9785
+ /** Write the style's exact base map and its hover/active/focus maps into the target. */
9786
+ function applyElementStyle(target, id) {
9787
+ const style = elementStyleById(id);
9788
+ if (!style)
9789
+ return { applied: false, unknownTraits: [] };
9790
+ return { applied: true, unknownTraits: writeEntries(target, style) };
9791
+ }
9792
+ const RULE_KEY = /^(\w+)(?:\[(\w+)=([^\]]+)\])?$/;
9793
+ const RANGE = /^([a-z]*)(\d+)\.\.\1(\d+)$/;
9794
+ function expandValues(raw) {
9795
+ const range = RANGE.exec(raw);
9796
+ if (!range)
9797
+ return [raw];
9798
+ const [, prefix, from, to] = range;
9799
+ const out = [];
9800
+ for (let n = Number(from); n <= Number(to); n++)
9801
+ out.push(`${prefix}${n}`);
9802
+ return out;
9803
+ }
9804
+ const DEFAULT_RULES = Object.entries(SPEC_DEFAULT_ELEMENT_STYLE).map(([key, style]) => {
9805
+ const m = RULE_KEY.exec(key);
9806
+ if (!m)
9807
+ throw new Error(`defaultElementStyle: unreadable key "${key}"`);
9808
+ const [, type, prop, raw] = m;
9809
+ return prop ? { type, prop, values: expandValues(raw), style } : { type, style };
9810
+ });
9811
+ /**
9812
+ * The element style a NEW node of `type` is born wearing (the spec's `defaultElementStyle`), or null
9813
+ * when the type has none. `traits` is the node's own trait values (props) — a Heading's default
9814
+ * follows its `level`; a Heading with no level matches no rule.
9815
+ */
9816
+ function defaultElementStyleFor(type, traits = {}) {
9817
+ for (const rule of DEFAULT_RULES) {
9818
+ if (rule.type !== type)
9819
+ continue;
9820
+ if (!rule.prop)
9821
+ return rule.style;
9822
+ if (rule.values.includes(String(traits[rule.prop])))
9823
+ return rule.style;
9824
+ }
9825
+ return null;
9826
+ }
9827
+ /** Per owned trait: does the node still hold the style's value (linked) or the author's (changed)? */
9828
+ function ownedTraitStatus(target, id) {
9829
+ const style = elementStyleById(id);
9830
+ if (!style)
9831
+ return [];
9832
+ return ownedEntries(style).map(({ trait, state, value }) => {
9833
+ const actual = state ? readState(target, state, trait) : readBase(target, trait);
9834
+ return {
9835
+ trait, ...(state ? { state } : {}), expected: value, actual,
9836
+ status: actual === value ? OwnedTraitLink.Linked : OwnedTraitLink.Changed,
9837
+ };
9838
+ });
9839
+ }
9840
+ /**
9841
+ * Move a node from one style to another, rewriting ONLY what the old style owned:
9842
+ * - an owned trait still holding the old style's value is released — a base trait goes back to
9843
+ * `baseline[trait]` (the type's unstyled value; deleted when the baseline has none), a state trait
9844
+ * is removed from its state map;
9845
+ * - an owned trait the author changed is KEPT, and the new style does not overwrite it.
9846
+ * Then the new style is written. Traits neither style owns are never touched.
9847
+ */
9848
+ function switchElementStyle(target, fromId, toId, baseline = {}) {
9849
+ const to = elementStyleById(toId);
9850
+ if (!to)
9851
+ return { applied: false, kept: [] };
9852
+ const kept = [];
9853
+ const from = elementStyleById(fromId);
9854
+ if (from) {
9855
+ for (const status of ownedTraitStatus(target, from.id)) {
9856
+ const ref = status.state ? { trait: status.trait, state: status.state } : { trait: status.trait };
9857
+ if (status.status === OwnedTraitLink.Changed) {
9858
+ if (status.actual !== undefined)
9859
+ kept.push(ref);
9860
+ continue;
9861
+ }
9862
+ if (ref.state)
9863
+ deleteState(target, ref.state, ref.trait);
9864
+ else
9865
+ writeBase(target, ref.trait, baseline[ref.trait]);
9866
+ }
9867
+ }
9868
+ writeEntries(target, to, kept);
9869
+ return { applied: true, kept };
9870
+ }
9730
9871
 
9731
9872
  function parseHexColor(hex) {
9732
9873
  const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec((hex ?? '').trim());
@@ -10330,5 +10471,5 @@ function springToLinear(stiffness, damping) {
10330
10471
  * Generated bundle index. Do not edit.
10331
10472
  */
10332
10473
 
10333
- export { ACCENT_TOKEN_NAMES, DEFAULT_FLAVOR, DESIGN_TOKEN_GROUPS, DESIGN_TOKEN_TIERS, DESIGN_TOKEN_TYPES, FLAVOR_SPEC_VERSION, FONT_CATALOG, FONT_FAMILY_TOKENS, 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, buildFlavorTokens, buildFontFaceCss, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, deriveAccentTokens, deriveBrandTokens, flavorDefaultDeclarations, flavorSourceStamp, fontsForTokens, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, oklabToRgb, parseFontFamilies, parseHexColor, planFlavorRevert, planFlavorSeed, relativeLuminance, resolveFlavorDefaults, resolveTokenValues, revertAvailable, rgbToOklab, springToLinear, styleKeyToKebab, toHexColor, tokensToDarkVarMap, tokensToVarMap, userFontStack, validateFlavorPack, wcagContrastRatio };
10474
+ export { ACCENT_TOKEN_NAMES, BUTTON_VARIANT_TO_STYLE, DESIGN_TOKEN_GROUPS, DESIGN_TOKEN_TIERS, DESIGN_TOKEN_TYPES, ELEMENT_STYLES, 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, buildFlavorTokens, buildFontFaceCss, compileSchemeCss, compileTokenCss, compileTokenCssBlocks, cssString, defaultDesignChoices, defaultElementStyleFor, deriveAccentTokens, deriveBrandTokens, elementStyleById, elementStylesFor, flavorSourceStamp, fontsForTokens, isDesignTokenGroup, isDesignTokenTier, isDesignTokenType, oklabToRgb, ownedTraitStatus, parseFontFamilies, parseHexColor, planFlavorRevert, planFlavorSeed, relativeLuminance, resolveTokenReferences, resolveTokenValues, revertAvailable, rgbToOklab, springToLinear, styleKeyToKebab, switchElementStyle, toHexColor, tokensToDarkVarMap, tokensToVarMap, userFontStack, validateFlavorPack, wcagContrastRatio };
10334
10475
  //# sourceMappingURL=vectoriox-iox-ui-design-core.mjs.map