@vectoriox/iox-ui 5.33.0 → 5.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1607,6 +1607,21 @@ declare const LEGACY_DIRECTION_KEY = "direction";
1607
1607
  * node renders. Pure; returns the map unchanged (same reference) when there is nothing to move.
1608
1608
  */
1609
1609
  declare function migrateLegacyDirection(styleProps: Record<string, any>): Record<string, any>;
1610
+ /** The Align trait's values: LOGICAL, so a text starts at the inline start of whatever direction it
1611
+ * reads in — the left in LTR, the right in RTL (vectoriox/iox-ui#16). */
1612
+ declare enum TextAlign {
1613
+ Start = "start",
1614
+ Center = "center",
1615
+ End = "end",
1616
+ Justify = "justify"
1617
+ }
1618
+ declare const TEXT_ALIGN_KEY = "textAlign";
1619
+ /**
1620
+ * A saved node's own style map with the serialised default `textAlign: left` read as the logical
1621
+ * `start` it compiles to — so the builder's panel highlights Start, and the next save stores it.
1622
+ * Pure; returns the map unchanged (same reference) when there is nothing to move.
1623
+ */
1624
+ declare function migrateLegacyTextAlign(styleProps: Record<string, any>): Record<string, any>;
1610
1625
  declare function isGradientPaint(value: unknown): boolean;
1611
1626
  /** The whole-value backdrop trait (DS2). A plain key — it is copied, not stripped — but it feeds
1612
1627
  * the backdrop group's output, so a breakpoint layer that sets it must count as touching it. */
@@ -3152,5 +3167,5 @@ declare class GalleryLoop {
3152
3167
  private emit;
3153
3168
  }
3154
3169
 
3155
- export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, RESERVED_ALIAS_PREFIX, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SectionComponent, SwiperModuleName, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildInteractionKeyframes, buildPartSelector, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, computeRelative, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, excludeSelf, feelFor, finalKeyframe, getByPath, getSeoValue, hasHoverState, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, motionAllowed, nearestCommonAncestorId, needsAccessibleName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, partClassName, planDataSource, planResponsiveRules, prefersReducedMotion, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperModuleNames, syncNodeA11yAttributes, variableAliases, variableItemsFrom, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
3170
+ export { A11yAttribute, A11yTrait, ACCESSIBLE_NAME_TYPES, AOSService, ARIA_CURRENT_PAGE, BACKDROP_TWIN_KEY, BACKDROP_WHOLE_KEY, BREAKPOINTS, Breakpoint, BreakpointQueryKind, BuilderButtonComponent, BuilderDividerComponent, BuilderHeadingComponent, BuilderIconComponent, BuilderImageComponent, BuilderLinkComponent, BuilderSpacerComponent, ButtonBlockComponent, COMPONENT_STATE_SELECTOR, CONTAINER_HTML_TAGS, CONTEXT_ACTION_TYPES, CURRENT_LINK_TYPES, CardComponent, ClientGalleryComponent, ClientListComponent, ClientListItemComponent, ClientRepeaterComponent, ClientSliderContainerComponent, ClientSliderSlideComponent, ComponentInstanceRegistryService, ComponentState, ContainerComponent, ContextStore, DECORATIVE_TYPES, DEFAULT_GALLERY_SCHEDULER, DEFAULT_PAGE_TRANSITION, DEFAULT_PAGE_TRANSITION_DURATION, DEFAULT_PAGE_TRANSITION_EASING, DEFAULT_PAGE_TRANSITION_PERSPECTIVE, DEFAULT_PHOSPHOR_WEIGHT, DEFAULT_WIDTH_BY_TYPE, DIALOG_CLOSE_ABOVE_ATTRIBUTE, DIALOG_NODE_TYPE, DeclarationContext, ENTRANCE_ACTION_TYPES, EXIT_ACTION_TYPES, FormRenderState, FormRuntimeService, GALLERY_EFFECTS, GALLERY_EFFECT_DESCRIPTORS, GalleryLoop, HOVER_DEFAULT_TRANSITION, HOVER_STATES, INTERACTION_HIDDEN_ATTRIBUTE, IOX_BUILDER_EVENTS, IOX_FORM_MESSAGES, IOX_FORM_NAVIGATOR, IOX_FORM_TRANSPORT, IS_PREVIEW, IoxAnimateContainer, IoxAosDirective, IoxAosModule, IoxBuilderComponentsModule, IoxComponentRegistryService, IoxDialogComponent, IoxFieldComponent, IoxFormComponent, IoxFormErrorComponent, IoxFormSuccessComponent, IoxPageComponent, IoxPageModule, IoxSubmitButtonComponent, IoxUiModule, LEGACY_DIRECTION_KEY, LEGACY_HOVER_TRANSITION, LinkedContainerComponent, MANAGED_A11Y_ATTRIBUTES, MAX_LINES_KEY, PAGE_STYLE_TOKENS, PAGE_TOKEN_FALLBACKS, PAGE_TRANSITION_CATEGORIES, PAGE_TRANSITION_PRESETS, PART_CLASS_NAMES, PIPE_DESCRIPTORS, PIPE_REGISTRY, PageScrollProvider, PageStyleField, PageStyleSource, REDUCED_MOTION_QUERY, RESERVED_ALIAS_PREFIX, STATE_PSEUDO_CLASS, SUPPRESSED_STYLE_PROPS_BY_TYPE, SWIPER_A11Y_OPTIONS, SWIPER_RESPONSIVE_TRAITS, SectionComponent, SwiperModuleName, TEXT_ALIGN_KEY, TEXT_DIRECTION_KEY, TRANSITION_FEELS, TextAlign, TextBlockComponent, VALIDATION_STATES, VALIDATION_STATE_CLASS, VIRTUAL_TRAIT_GROUPS, VIRTUAL_TRAIT_KEYS, ViewPositionDirective, ViewPositionModule, VisibilityActionType, activeActions, applyBindingPipes, applyFeel, breakpointDef, breakpointForWidth, breakpointQueryFor, buildDataSourceFilter, buildInteractionKeyframes, buildPartSelector, buildRelatedFilter, buildSwiperBreakpoints, buildTargetedStateSelector, buildTransitionTimeline, cascadeChain, clearInputOverride, clearOverride, closeAboveMediaQuery, collectContextSlotKeys, collectReferencedAliases, compileDeclarations, compilePageStyle, componentStatesFor, composeVirtualTraits, computeRelative, containerHtmlTag, contextActionEffect, contextSlotKeys, countOverrides, dataSourcePlanToRequest, declaredInputNames, decomposeBackdropFilter, defaultHoverTransition, defaultMessageResolver, deriveSeoFromContent, describePageStyle, describeSeoFields, dialogCloseAboveMinWidth, effectiveActionType, effectiveTransitionId, entrancePreStateStyle, excludeSelf, feelFor, finalKeyframe, getByPath, getSeoValue, hasHoverState, hasOwnTransition, initialSlotValues, isActionEnabled, isContextAction, isCurrentLink, isExternalOrFragment, isGradientPaint, isInputOverriddenAt, isOverriddenAt, isReservedAlias, isValidSlotKey, isWholeBackdropSet, keyframePropertyName, keyframeStyleEntries, legacyEnterToTransition, matchRouteParams, mediaQueryFor, mergeSwiperBreakpoints, migrateLegacyDirection, migrateLegacyTextAlign, motionAllowed, nearestCommonAncestorId, needsAccessibleName, nodeA11yAttributes, nodeCssId, nodeTraitValue, nodeUsesContext, normalizeAppPath, normalizeCollectionResult, normalizeGalleryImage, normalizeIconClass, parentBreakpoint, parseUrlList, partClassName, planDataSource, planResponsiveRules, prefersReducedMotion, referencedDataSources, referencedDataSourcesDeep, renderDefaultDeclarations, resetPageStyleField, resetSeoField, resolveBoundValue, resolveContextValue, resolveDerivedInto, resolveGalleryEffect, resolveGalleryUrls, resolveInputChain, resolveInputs, resolvePageTransition, resolvePath, resolveRelativeInto, resolveRepeaterItems, resolveSeo, resolveSiblings, resolveSingleItemId, resolveStateStyles, resolveStyleProps, resolveToggleVisibility, resolveValueChain, rewriteViewportUnits, safeSetInput, serializeA11yAttributes, setInputOverride, setOverride, setSeoAuto, setSeoBinding, setSeoOverride, setSeoValue, stateNamesIn, statePseudoClass, stateSelectorSuffix, stripSuppressedProps, swiperAutoplayAllowed, swiperModuleNames, syncNodeA11yAttributes, variableAliases, variableItemsFrom, withContextSlots, withDefaultHoverTransition, wrapInBreakpointQuery, wrapInMediaQuery };
3156
3171
  export type { A11yNodeSource, BoundValueSpec, BreakpointDef, BuildRelatedFilterOptions, ContextActionEffect, ContextActionLike, ContextItemRef, ContextSlotDefault, ContextValueSpec, ContextWriteScope, CurrentLinkOptions, DataSourceDomain, DataSourceEndpoints, DataSourceMode, DataSourcePlan, DataSourceQueryFilter, DataSourceRequest, DataSourceRequestSpec, DataSourceRouteFilter, DataSourceSpec, FormMessageResolver, GalleryBoundSource, GalleryDurations, GalleryEffect, GalleryEffectDescriptor, GalleryImage, GalleryLoopOptions, GalleryLoopScheduler, GalleryLoopState, GallerySlideState, IBuilderEventEmitter, InputMap, InteractionKeyframe, IoxContextSlot, IoxPipe, MatchMediaHost, NodeA11yContext, OverrideBreakpoint, PageStyleFieldState, PageStyleOptions, PageStyleSettings, PageTransitionCategory, PageTransitionCategoryMeta, PageTransitionOptions, PageTransitionPreset, PipeArgSpec, PipeContext, PipeDescriptor, RelativeInputs, RelativeSourceKind, RelativeSourceSpec, ResolveSiblingsOptions, ResolvedPageTransition, ResponsiveLayer, ResponsiveOverrides, ResponsiveRuleSet, RouteContext, SeoDerivedContent, SeoFieldSource, SeoFieldState, SeoResolveContext, SetContextParams, SiblingsResult, StateStyleMap, StyleMap, StyleTreeNode, SwiperBreakpointMap, SwiperModuleFlags, ToggleableAction, TransitionFeel, TransitionPhase, TransitionTimelineInput, ValueSource, VirtualTraitGroup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectoriox/iox-ui",
3
- "version": "5.33.0",
3
+ "version": "5.35.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=20.0.0",
6
6
  "@angular/core": ">=20.0.0",
@@ -66,8 +66,9 @@ const PILL_BASE: Readonly<Record<string, string>> = {
66
66
  };
67
67
  const PILL_ICON: Readonly<Record<string, string>> = { fontSize: '1em' };
68
68
  const PILL_WORDS: Readonly<Record<string, string>> = { width: 'auto', minHeight: 'auto', padding: '0px 0px 0px 0px', margin: '0px 0px 0px 0px' };
69
- /** The pill's words inherit the pill's type and ink (the contract's inherit-everything text style). */
70
- const PILL_WORDS_STYLE = 'button-label';
69
+ /** The pill's words wear the pill's OWN label style (Card v2.1): its type from the badge tokens, its leading,
70
+ * tracking, case and ink inherited from the pill — never the button's label style. */
71
+ export const PILL_LABEL_STYLE = 'badge-label';
71
72
 
72
73
  export interface PillOptions {
73
74
  text?: string;
@@ -81,7 +82,7 @@ function buildPill(create: CompositeNodeFactory, elementStyle: string, text: str
81
82
  writeStyles(pill, PILL_BASE);
82
83
  const glyph = create({ key: `${key}-icon`, type: 'Icon', props: { iconClass: icon }, elementStyle: 'icon' });
83
84
  writeStyles(glyph, PILL_ICON);
84
- const words = create({ key: `${key}-text`, type: 'Text', props: { content: text, tag: 'span' }, elementStyle: PILL_WORDS_STYLE });
85
+ const words = create({ key: `${key}-text`, type: 'Text', props: { content: text, tag: 'span' }, elementStyle: PILL_LABEL_STYLE });
85
86
  writeStyles(words, PILL_WORDS);
86
87
  pill.children = [glyph, words];
87
88
  return pill;
@@ -1,4 +1,4 @@
1
- import { elementStyleById, LinkType, switchElementStyle } from '@vectoriox/iox-ui/design-core';
1
+ import { COLOR_SCHEME_TRAIT, elementStyleById, LinkType, setColorScheme, switchElementStyle } from '@vectoriox/iox-ui/design-core';
2
2
 
3
3
  import { BadgeTone, buildBadge } from './badge';
4
4
  import {
@@ -119,7 +119,6 @@ enum Scheme {
119
119
  Inverse = 'inverse',
120
120
  }
121
121
 
122
- const COLOR_SCHEME = 'colorScheme';
123
122
  const LINK_TYPE = 'linkType';
124
123
  const SAVE_ICON = 'ph ph-bookmark-simple';
125
124
 
@@ -336,8 +335,9 @@ function placeBadge(card: CompositeNode, badge: CompositeNode, onMedia: boolean)
336
335
  writeStyles(badge, { position: overMedia ? 'absolute' : 'static' });
337
336
  }
338
337
 
338
+ /** Through design-core's ONE scheme writer — the same setting Section colours edits everywhere. */
339
339
  function writeScheme(node: CompositeNode, scheme: Scheme): void {
340
- writeProp(node, COLOR_SCHEME, scheme);
340
+ setColorScheme(node, scheme);
341
341
  }
342
342
 
343
343
  /** Write one role's layout values from a spec (base + layers). */
@@ -424,7 +424,7 @@ function matches(card: CompositeNode, spec: CardLayoutSpec): boolean {
424
424
  const content = findByRole(card, CardRole.Content);
425
425
  if (content) {
426
426
  if (content.elementStyle !== spec.contentStyle || !holds(content, spec.content)) return false;
427
- if (!same(readProp(content, COLOR_SCHEME) ?? Scheme.Default, spec.contentScheme)) return false;
427
+ if (!same(readProp(content, COLOR_SCHEME_TRAIT) ?? Scheme.Default, spec.contentScheme)) return false;
428
428
  }
429
429
  const title = findByRole(card, CardRole.Title);
430
430
  if (title && !holds(title, spec.title)) return false;
@@ -3,6 +3,7 @@ import { WCAG_AA_UI } from './brand-neutrals';
3
3
  import { compositeOver, paintColours, parseCssColour, quantizeRgb, Rgba, ringColour } from './colour-eval';
4
4
  import { ElementStyle, ELEMENT_STYLES } from './element-styles';
5
5
  import { SPEC_CONTRAST_AUDIT, SPEC_INK_SCOPES, SPEC_SCHEMES } from './flavor-spec.generated';
6
+ import { SCHEME_INK } from './node-classes';
6
7
  import { DesignTokenLike, ResolvedMode, resolveSchemeTokenValues } from './token-css';
7
8
 
8
9
  /**
@@ -19,7 +20,10 @@ import { DesignTokenLike, ResolvedMode, resolveSchemeTokenValues } from './token
19
20
  * its focus ring; the form parts the field baseline paints (`contrastAudit.formParts`); the form's
20
21
  * Success / Error notices, in every scheme and container;
21
22
  * - a Badge's or Chip's icon through the same `inkScopes`, and text over an image (Card v2's "Image behind
22
- * text"): the block's ink over its scrim composited on the WORST-CASE image (`contrastAudit.scrims`).
23
+ * text"): the block's ink over its scrim composited on the WORST-CASE image (`contrastAudit.scrims`);
24
+ * - a container that WEARS a scheme inside a section of any scheme (a Featured card on a plain page): the
25
+ * text it holds inherits the scheme's ink (`SCHEME_INK`, iox-ui#15) on the inner scheme's fill, composited
26
+ * over the OUTER section — the pairing the token pairs cannot see.
23
27
  *
24
28
  * WCAG 2: 4.5:1 for text, 3:1 for glyphs, UI boundaries and focus indicators (1.4.11), judged on 8-bit
25
29
  * colours. The same audit gates the contract (`colour.mjs auditContrast`, every flavor × tint × a seed
@@ -65,6 +69,8 @@ const GLYPH_GROUP = 'Media';
65
69
  const FORMS_GROUP = 'Forms';
66
70
  const DEFAULT_SCHEME = 'default';
67
71
  const SECTION = 'section';
72
+ /** The element a scheme'd container's running text is reported as (iox-ui#15). */
73
+ export const INHERITED_TEXT = 'inherited text';
68
74
  const FIELD = 'field';
69
75
  const WHITE: Rgba = { r: 1, g: 1, b: 1, a: 1 };
70
76
  const SINGLE_REF = /^var\(--iox-([\w-]+)\)$/;
@@ -187,10 +193,53 @@ export function auditDesignContrast(rows: ReadonlyArray<DesignTokenLike>, mode:
187
193
  }
188
194
  }
189
195
  }
196
+ findings.push(...schemedContainerFindings(rows, mode, section, containers.filter((c): c is ElementStyle => !!c)));
190
197
  for (const scrim of SCRIMS) findings.push(...scrimFindings(rows, mode, scrim, byId.get(scrim.style)));
191
198
  return findings;
192
199
  }
193
200
 
201
+ /** What a section in `values` paints under its content: its own fill over the page, or the page. */
202
+ function sectionGround(values: Values): Rgba[] {
203
+ const pageInk = parseCssColour(values['text']);
204
+ const page = paintColours(values['page-bg'], pageInk);
205
+ const sectionPaint = paintColours(values['section-bg'], pageInk);
206
+ return opaque(sectionPaint) ? sectionPaint.map(c => (c.a < 1 ? compositeOver(c, page[0] ?? WHITE) : c)) : page;
207
+ }
208
+
209
+ /**
210
+ * A container that wears a NON-default scheme, inside a section of every scheme (vectoriox/iox-ui#15 — the
211
+ * Featured card is a Card in Brand on a Default page). Its tokens are its own scheme's laid over the outer
212
+ * section's (what it does not set, it inherits); a translucent fill composites over the OUTER section; the running text it holds (`body`, `lead`, a label: `color: inherit`)
213
+ * reads the scheme's ink, which is what the scheme-ink rule and `withSchemeInk` make CSS compute.
214
+ */
215
+ function schemedContainerFindings(rows: ReadonlyArray<DesignTokenLike>, mode: ResolvedMode, section: StyleMap,
216
+ containers: ReadonlyArray<ElementStyle>): ContrastFinding[] {
217
+ const out: ContrastFinding[] = [];
218
+ for (const outer of SCHEME_IDS) {
219
+ const ground = sectionGround(resolveSchemeTokenValues(rows, mode, outer));
220
+ for (const inner of SCHEME_IDS.filter(id => id !== DEFAULT_SCHEME)) {
221
+ const values = resolveSchemeTokenValues(rows, mode, [outer, inner]);
222
+ const schemeInk = parseCssColour(resolveIn(values, SCHEME_INK), parseCssColour(resolveIn(values, section['color'])));
223
+ for (const box of containers) {
224
+ const style = ownStyle(box);
225
+ const bgs = overAll(paintColours(resolveIn(values, style['backgroundColor']), schemeInk), ground);
226
+ const ink = style['color'] && style['color'] !== 'inherit' ? parseCssColour(resolveIn(values, style['color']), schemeInk) : schemeInk;
227
+ if (!ink) continue;
228
+ for (const g of bgs) {
229
+ const bg = quantizeRgb(g);
230
+ const f = quantizeRgb(ink.a < 1 ? compositeOver(ink, bg) : ink);
231
+ const ratio = wcagContrastRatio(f, bg);
232
+ out.push({
233
+ scheme: inner, container: `${box.id} in a ${outer} section`, element: INHERITED_TEXT, part: 'text',
234
+ fg: toHexColor(f), bg: toHexColor(bg), ratio: Math.round(ratio * 100) / 100, min: WCAG_AA_TEXT, pass: ratio >= WCAG_AA_TEXT - 1e-9,
235
+ });
236
+ }
237
+ }
238
+ }
239
+ }
240
+ return out;
241
+ }
242
+
194
243
  /** Text over an image: the block's ink — and every token its ink scope pins to it — over the scrim
195
244
  * composited on the WORST-CASE image, in the scheme the block wears. Never over the section: the image
196
245
  * covers it. */
@@ -1348,6 +1348,7 @@ export const SPEC_SCHEMES = {
1348
1348
  "set": {
1349
1349
  "section-bg": "{{accent}}",
1350
1350
  "bg": "{{accent}}",
1351
+ "nav-bg": "{{accent}}",
1351
1352
  "surface": "{{accent-hover}}",
1352
1353
  "surface-2": "{{accent-hover}}",
1353
1354
  "surface-glass": "{{accent-hover}}",
@@ -1371,6 +1372,8 @@ export const SPEC_SCHEMES = {
1371
1372
  }
1372
1373
  } as const;
1373
1374
 
1375
+ export const SPEC_SCHEME_INK = "var(--iox-text)" as const;
1376
+
1374
1377
  export const SPEC_A11Y = {
1375
1378
  "reducedMotion": {
1376
1379
  "dur-fast": "0ms",
@@ -2012,6 +2015,23 @@ export const SPEC_ELEMENT_STYLES = [
2012
2015
  "fontWeight": "var(--iox-weight-label)"
2013
2016
  }
2014
2017
  },
2018
+ {
2019
+ "id": "badge-label",
2020
+ "label": "Badge label",
2021
+ "group": "Content blocks",
2022
+ "appliesTo": [
2023
+ "Text"
2024
+ ],
2025
+ "base": {
2026
+ "fontFamily": "var(--iox-font-label)",
2027
+ "fontSize": "var(--iox-badge-size)",
2028
+ "fontWeight": "var(--iox-weight-label)",
2029
+ "lineHeight": "inherit",
2030
+ "letterSpacing": "inherit",
2031
+ "textTransform": "inherit",
2032
+ "color": "inherit"
2033
+ }
2034
+ },
2015
2035
  {
2016
2036
  "id": "media-scrim",
2017
2037
  "label": "Text over image",
@@ -1,5 +1,5 @@
1
1
  import { ELEMENT_STYLE_MARKER_CLASS, elementStyleClass } from './element-styles';
2
- import { SPEC_SCHEMES } from './flavor-spec.generated';
2
+ import { SPEC_SCHEME_INK, SPEC_SCHEMES } from './flavor-spec.generated';
3
3
 
4
4
  /**
5
5
  * The design-system classes a node renders, in BOTH renderers (builder canvas, engine component
@@ -104,6 +104,54 @@ export function colorSchemeOf(node: DesignClassSource | null | undefined): strin
104
104
  return typeof trait === 'string' ? trait : null;
105
105
  }
106
106
 
107
+ /**
108
+ * The ink a section colour scheme gives everything inside it (vectoriox/iox-ui#15). A scheme re-declares
109
+ * tokens only, so without this a Container wearing one kept the colour it INHERITED from the section around
110
+ * it: a Featured (Brand) card's body text stayed the page's dark ink on the Brand fill, 2.54:1. Two halves,
111
+ * one rule: `compileSchemeCss` gives every `iox-scheme-*` element `color: var(--iox-text)` (the node rule,
112
+ * emitted after it at the same specificity, still wins), and {@link withSchemeInk} reads a scheme'd node's
113
+ * own `inherit` — what the card / box / panel element styles write for "no colour of my own" — as this ink.
114
+ * The value is the contract's `schemeInk`.
115
+ */
116
+ export const SCHEME_INK: string = SPEC_SCHEME_INK;
117
+
118
+ /** The values that mean "no colour of my own" — `color` is inherited, so these equal no declaration. */
119
+ const INHERITED_INK: ReadonlySet<string> = new Set(['inherit', 'unset']);
120
+
121
+ /**
122
+ * A node's style map as its renderer compiles it under a section colour scheme: a NON-default scheme turns
123
+ * an inherited `color` into {@link SCHEME_INK}; anything else — no scheme, an author's own colour, a token —
124
+ * is returned untouched (the same object). Every render path calls it on every rule it compiles for the
125
+ * node (base, states, breakpoint layers): the builder's StyleRegistryService and both engine paths.
126
+ */
127
+ export function withSchemeInk<T extends Record<string, unknown>>(styles: T, scheme: unknown): T {
128
+ if (!styles || !colorSchemeClass(scheme)) return styles;
129
+ const color = styles['color'];
130
+ if (typeof color !== 'string' || !INHERITED_INK.has(color.trim().toLowerCase())) return styles;
131
+ return { ...styles, color: SCHEME_INK };
132
+ }
133
+
134
+ /**
135
+ * Write a node's section colour scheme — THE one writer, so every control that offers Section colours (the
136
+ * Style panel's Element style block, Card ▸ Card) edits the same setting: the `colorScheme` content trait on
137
+ * a builder node, `props.colorScheme` on a saved one. False when nothing changed: an unknown id, the value it
138
+ * already holds, or a builder node with no such trait (a type that offers no scheme).
139
+ */
140
+ export function setColorScheme(
141
+ node: { props?: Record<string, unknown> | null; traits?: Array<{ name?: string; default?: unknown }> | null } | null | undefined,
142
+ id: string,
143
+ ): boolean {
144
+ if (!node || !SCHEME_IDS.has(id) || (colorSchemeOf(node) ?? ColorScheme.Default) === id) return false;
145
+ if (Array.isArray(node.traits)) {
146
+ const trait = node.traits.find(t => t?.name === COLOR_SCHEME_TRAIT);
147
+ if (!trait) return false;
148
+ trait.default = id;
149
+ return true;
150
+ }
151
+ node.props = { ...(node.props ?? {}), [COLOR_SCHEME_TRAIT]: id };
152
+ return true;
153
+ }
154
+
107
155
  /** True when the node wears a known element style — the marker is on it. */
108
156
  export function hasElementStyleMarker(node: { elementStyle?: string | null } | null | undefined): boolean {
109
157
  return elementStyleClass(node?.elementStyle) !== null;
@@ -1,5 +1,6 @@
1
1
  import { DesignMode } from './design-token.model';
2
2
  import { SPEC_A11Y, SPEC_SCHEMES } from './flavor-spec.generated';
3
+ import { SCHEME_INK } from './node-classes';
3
4
  import { specTokenNames } from './token-names';
4
5
 
5
6
  /**
@@ -10,7 +11,8 @@ import { specTokenNames } from './token-names';
10
11
  *
11
12
  * Block order (design-system plan §3e, T8):
12
13
  * 1. base `:root { --iox-*: … }`
13
- * 2. schemes `.iox-scheme-<id> { overrides; every var()-referencing token re-declared }`
14
+ * 2. schemes `.iox-scheme-<id> { overrides; every var()-referencing token re-declared }`, then the
15
+ * ONE scheme-ink rule `.iox-scheme-subtle, … { color: var(--iox-text) }` (iox-ui#15)
14
16
  * 3. reduced motion `@media (prefers-reduced-motion: reduce)`
15
17
  * 4. reduced transparency `@media (prefers-reduced-transparency: reduce)`
16
18
  * 5. dark — LAST `@media (prefers-color-scheme: dark)`
@@ -167,19 +169,29 @@ export function resolveTokenValues(
167
169
  * Every token's value INSIDE a section wearing `schemeId`, in one mode — what CSS computes there: the
168
170
  * scheme's overrides (`{{token}}` / `{{~token}}` placeholders substituted from the root), every other
169
171
  * row as declared, and `var()` references resolved within the section (the §3c re-declaration rule
170
- * makes that the section's own resolution). `default` (or an unknown id) is the root.
172
+ * makes that the section's own resolution). `default` (or an unknown id) is the root. A CHAIN
173
+ * (`['inverse', 'accent']`, outermost first) is an element wearing the last scheme inside elements wearing
174
+ * the ones before it: a token the inner scheme does not set is the outer one's (custom properties inherit).
171
175
  */
172
176
  export function resolveSchemeTokenValues(
173
177
  tokens: ReadonlyArray<DesignTokenLike> | null | undefined,
174
178
  mode: ResolvedMode,
175
- schemeId: string,
179
+ schemeId: string | ReadonlyArray<string>,
176
180
  ): Record<string, string> {
177
181
  const rows = usableTokens(tokens);
178
- const set = schemeId === DEFAULT_SCHEME ? undefined : (SPEC_SCHEMES as Readonly<Record<string, { set: Readonly<Record<string, string>> }>>)[schemeId]?.set;
179
- if (!set) return resolveTokenValues(rows, mode);
182
+ const sets = (typeof schemeId === 'string' ? [schemeId] : schemeId)
183
+ .map(id => (id === DEFAULT_SCHEME ? undefined : (SPEC_SCHEMES as Readonly<Record<string, { set: Readonly<Record<string, string>> }>>)[id]?.set));
184
+ if (!sets.some(Boolean)) return resolveTokenValues(rows, mode);
180
185
  const root = resolveTokenValues(rows, mode);
181
186
  const other = resolveTokenValues(rows, mode === DARK ? LIGHT : DARK);
182
- const overrides = new Map(schemeEntries(set, root, other));
187
+ // Nested (`[outer, inner]` — a Featured card in an Inverse section): each scheme block re-declares the
188
+ // rows that reference a token, so at every level those are restored before the block's own set.
189
+ const overrides = new Map<string, string>();
190
+ for (const set of sets) {
191
+ if (!set) continue;
192
+ for (const t of rows) if (modeValue(t, mode).includes(REFERENCE)) overrides.delete(t.name);
193
+ for (const [name, value] of schemeEntries(set, root, other)) overrides.set(name, value);
194
+ }
183
195
  return resolveTokenValues(rows.map(t => ({ name: t.name, value: overrides.get(t.name) ?? modeValue(t, mode) })), LIGHT);
184
196
  }
185
197
 
@@ -243,11 +255,27 @@ export function compileSchemeCss(
243
255
  if (!rows.length) return '';
244
256
  const resolved = resolveTokenValues(rows, LIGHT);
245
257
  const other = opposite ?? resolveTokenValues(rows, DARK);
246
- return nonDefaultSchemes().map(([id, set]) => rule(schemeSelector(id, scope), [
247
- ...schemeEntries(set, resolved, other),
248
- ...rows.filter(t => !(t.name in set) && t.value.includes(REFERENCE))
249
- .map((t): [string, string] => [t.name, t.value]),
250
- ])).filter(Boolean).join('\n');
258
+ return [
259
+ ...nonDefaultSchemes().map(([id, set]) => rule(schemeSelector(id, scope), [
260
+ ...schemeEntries(set, resolved, other),
261
+ ...rows.filter(t => !(t.name in set) && t.value.includes(REFERENCE))
262
+ .map((t): [string, string] => [t.name, t.value]),
263
+ ])),
264
+ schemeInkRule(scope),
265
+ ].filter(Boolean).join('\n');
266
+ }
267
+
268
+ /**
269
+ * Every element wearing a scheme takes the scheme's ink (vectoriox/iox-ui#15): the tokens re-resolve inside
270
+ * it, but `color` is inherited as a COMPUTED value, so without this a scheme'd Container's running text kept
271
+ * the outer section's ink. One rule, specificity (0,1,0) in every renderer — a builder scope is wrapped in
272
+ * `:where()` — emitted with the token CSS, BEFORE every node rule: a node's own colour (same specificity,
273
+ * later) still wins. A node's `color: inherit` is read as this ink by `withSchemeInk`.
274
+ */
275
+ function schemeInkRule(scope?: string): string {
276
+ const where = scope ? `:where(${scope}) ` : '';
277
+ const selectors = nonDefaultSchemes().map(([id]) => `${where}.iox-scheme-${id}`);
278
+ return selectors.length ? `${selectors.join(', ')} {\n color: ${SCHEME_INK};\n}` : '';
251
279
  }
252
280
 
253
281
  /** The dark twin of both scheme rules: a re-declared token with a dark value would otherwise pin
@@ -52,7 +52,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
52
52
  "lineHeight": "1.5",
53
53
  "letterSpacing": "0px",
54
54
  "textTransform": "none",
55
- "textAlign": "left",
55
+ "textAlign": "start",
56
56
  "fontStyle": "normal",
57
57
  "textDecoration": "none",
58
58
  "WebkitTextStrokeWidth": "0px",
@@ -243,7 +243,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
243
243
  "lineHeight": "1.5",
244
244
  "letterSpacing": "0px",
245
245
  "textTransform": "none",
246
- "textAlign": "left",
246
+ "textAlign": "start",
247
247
  "fontStyle": "normal",
248
248
  "textDecoration": "none",
249
249
  "WebkitTextStrokeWidth": "0px",
@@ -435,7 +435,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
435
435
  "lineHeight": "1.5",
436
436
  "letterSpacing": "0px",
437
437
  "textTransform": "none",
438
- "textAlign": "left",
438
+ "textAlign": "start",
439
439
  "fontStyle": "normal",
440
440
  "textDecoration": "none",
441
441
  "WebkitTextStrokeWidth": "0px",
@@ -621,7 +621,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
621
621
  "lineHeight": "1.5",
622
622
  "letterSpacing": "0px",
623
623
  "textTransform": "none",
624
- "textAlign": "left",
624
+ "textAlign": "start",
625
625
  "fontStyle": "normal",
626
626
  "textDecoration": "none",
627
627
  "WebkitTextStrokeWidth": "0px",
@@ -810,7 +810,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
810
810
  "lineHeight": "1.5",
811
811
  "letterSpacing": "0px",
812
812
  "textTransform": "none",
813
- "textAlign": "left",
813
+ "textAlign": "start",
814
814
  "fontStyle": "normal",
815
815
  "textDecoration": "none",
816
816
  "WebkitTextStrokeWidth": "0px",
@@ -1001,7 +1001,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1001
1001
  "lineHeight": "1.5",
1002
1002
  "letterSpacing": "0px",
1003
1003
  "textTransform": "none",
1004
- "textAlign": "left",
1004
+ "textAlign": "start",
1005
1005
  "fontStyle": "normal",
1006
1006
  "textDecoration": "none",
1007
1007
  "WebkitTextStrokeWidth": "0px",
@@ -1188,7 +1188,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1188
1188
  "lineHeight": "1.5",
1189
1189
  "letterSpacing": "0px",
1190
1190
  "textTransform": "none",
1191
- "textAlign": "left",
1191
+ "textAlign": "start",
1192
1192
  "fontStyle": "normal",
1193
1193
  "textDecoration": "none",
1194
1194
  "WebkitTextStrokeWidth": "0px",
@@ -1377,7 +1377,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1377
1377
  "lineHeight": "1.5",
1378
1378
  "letterSpacing": "0px",
1379
1379
  "textTransform": "none",
1380
- "textAlign": "left",
1380
+ "textAlign": "start",
1381
1381
  "fontStyle": "normal",
1382
1382
  "textDecoration": "none",
1383
1383
  "WebkitTextStrokeWidth": "0px",
@@ -1571,7 +1571,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1571
1571
  "lineHeight": "1.5",
1572
1572
  "letterSpacing": "0px",
1573
1573
  "textTransform": "none",
1574
- "textAlign": "left",
1574
+ "textAlign": "start",
1575
1575
  "fontStyle": "normal",
1576
1576
  "textDecoration": "none",
1577
1577
  "WebkitTextStrokeWidth": "0px",
@@ -1760,7 +1760,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1760
1760
  "lineHeight": "1.5",
1761
1761
  "letterSpacing": "0px",
1762
1762
  "textTransform": "none",
1763
- "textAlign": "left",
1763
+ "textAlign": "start",
1764
1764
  "fontStyle": "normal",
1765
1765
  "textDecoration": "none",
1766
1766
  "WebkitTextStrokeWidth": "0px",
@@ -1943,7 +1943,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
1943
1943
  "lineHeight": "0",
1944
1944
  "letterSpacing": "0px",
1945
1945
  "textTransform": "none",
1946
- "textAlign": "left",
1946
+ "textAlign": "start",
1947
1947
  "fontStyle": "normal",
1948
1948
  "textDecoration": "none",
1949
1949
  "WebkitTextStrokeWidth": "0px",
@@ -2124,7 +2124,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2124
2124
  "lineHeight": "1.5",
2125
2125
  "letterSpacing": "0px",
2126
2126
  "textTransform": "none",
2127
- "textAlign": "left",
2127
+ "textAlign": "start",
2128
2128
  "fontStyle": "normal",
2129
2129
  "textDecoration": "none",
2130
2130
  "WebkitTextStrokeWidth": "0px",
@@ -2303,7 +2303,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2303
2303
  "lineHeight": "1.5",
2304
2304
  "letterSpacing": "0px",
2305
2305
  "textTransform": "none",
2306
- "textAlign": "left",
2306
+ "textAlign": "start",
2307
2307
  "fontStyle": "normal",
2308
2308
  "textDecoration": "none",
2309
2309
  "WebkitTextStrokeWidth": "0px",
@@ -2491,7 +2491,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2491
2491
  "lineHeight": "1.5",
2492
2492
  "letterSpacing": "0px",
2493
2493
  "textTransform": "none",
2494
- "textAlign": "left",
2494
+ "textAlign": "start",
2495
2495
  "fontStyle": "normal",
2496
2496
  "textDecoration": "none",
2497
2497
  "WebkitTextStrokeWidth": "0px",
@@ -2668,7 +2668,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2668
2668
  "lineHeight": "1.5",
2669
2669
  "letterSpacing": "0px",
2670
2670
  "textTransform": "none",
2671
- "textAlign": "left",
2671
+ "textAlign": "start",
2672
2672
  "fontStyle": "normal",
2673
2673
  "textDecoration": "none",
2674
2674
  "WebkitTextStrokeWidth": "0px",
@@ -2852,7 +2852,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
2852
2852
  "lineHeight": "1.5",
2853
2853
  "letterSpacing": "0px",
2854
2854
  "textTransform": "none",
2855
- "textAlign": "left",
2855
+ "textAlign": "start",
2856
2856
  "fontStyle": "normal",
2857
2857
  "textDecoration": "none",
2858
2858
  "WebkitTextStrokeWidth": "0px",
@@ -3254,7 +3254,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3254
3254
  "lineHeight": "1.5",
3255
3255
  "letterSpacing": "0px",
3256
3256
  "textTransform": "none",
3257
- "textAlign": "left",
3257
+ "textAlign": "start",
3258
3258
  "fontStyle": "normal",
3259
3259
  "textDecoration": "none",
3260
3260
  "WebkitTextStrokeWidth": "0px",
@@ -3564,7 +3564,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3564
3564
  "lineHeight": "1.5",
3565
3565
  "letterSpacing": "0px",
3566
3566
  "textTransform": "none",
3567
- "textAlign": "left",
3567
+ "textAlign": "start",
3568
3568
  "fontStyle": "normal",
3569
3569
  "textDecoration": "none",
3570
3570
  "WebkitTextStrokeWidth": "0px",
@@ -3759,7 +3759,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3759
3759
  "lineHeight": "1.5",
3760
3760
  "letterSpacing": "0px",
3761
3761
  "textTransform": "none",
3762
- "textAlign": "left",
3762
+ "textAlign": "start",
3763
3763
  "fontStyle": "normal",
3764
3764
  "textDecoration": "none",
3765
3765
  "WebkitTextStrokeWidth": "0px",
@@ -3958,7 +3958,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
3958
3958
  "lineHeight": "1.5",
3959
3959
  "letterSpacing": "0px",
3960
3960
  "textTransform": "none",
3961
- "textAlign": "left",
3961
+ "textAlign": "start",
3962
3962
  "fontStyle": "normal",
3963
3963
  "textDecoration": "none",
3964
3964
  "WebkitTextStrokeWidth": "0px",
@@ -4143,7 +4143,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4143
4143
  "lineHeight": "1.5",
4144
4144
  "letterSpacing": "0px",
4145
4145
  "textTransform": "none",
4146
- "textAlign": "left",
4146
+ "textAlign": "start",
4147
4147
  "fontStyle": "normal",
4148
4148
  "textDecoration": "none",
4149
4149
  "WebkitTextStrokeWidth": "0px",
@@ -4328,7 +4328,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4328
4328
  "lineHeight": "1.5",
4329
4329
  "letterSpacing": "0px",
4330
4330
  "textTransform": "none",
4331
- "textAlign": "left",
4331
+ "textAlign": "start",
4332
4332
  "fontStyle": "normal",
4333
4333
  "textDecoration": "none",
4334
4334
  "WebkitTextStrokeWidth": "0px",
@@ -4511,7 +4511,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4511
4511
  "lineHeight": "1.5",
4512
4512
  "letterSpacing": "0px",
4513
4513
  "textTransform": "none",
4514
- "textAlign": "left",
4514
+ "textAlign": "start",
4515
4515
  "fontStyle": "normal",
4516
4516
  "textDecoration": "none",
4517
4517
  "WebkitTextStrokeWidth": "0px",
@@ -4699,7 +4699,7 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
4699
4699
  "lineHeight": "1.5",
4700
4700
  "letterSpacing": "0px",
4701
4701
  "textTransform": "none",
4702
- "textAlign": "left",
4702
+ "textAlign": "start",
4703
4703
  "fontStyle": "normal",
4704
4704
  "textDecoration": "none",
4705
4705
  "WebkitTextStrokeWidth": "0px",
@@ -4888,7 +4888,7 @@ export const NEUTRAL_STYLE_PROPS: Readonly<Record<string, unknown>> = {
4888
4888
  "lineHeight": "1.5",
4889
4889
  "letterSpacing": "0px",
4890
4890
  "textTransform": "none",
4891
- "textAlign": "left",
4891
+ "textAlign": "start",
4892
4892
  "fontStyle": "normal",
4893
4893
  "textDecoration": "none",
4894
4894
  "WebkitTextStrokeWidth": "0px",