|
|
/**
|
|
|
* theming-sentinel-exceptions — the written adjudications R-5.4 requires.
|
|
|
*
|
|
|
* A public token the sentinel reports as "no effect" is EITHER a lie (retire
|
|
|
* it) OR its silence has a measured reason that lives here, one line per
|
|
|
* token: the part is conditional and the demo does not mount it, the state
|
|
|
* needs forcing, the consumer is a pseudo-element or a composed component the
|
|
|
* instrument cannot read, the value sits behind a media query. "Forced →
|
|
|
* reaches" means the adjudication PROVED the token alive by mounting what it
|
|
|
* needs; an instrument-limit entry names the limit (they are also registered
|
|
|
* in next-features §13).
|
|
|
*
|
|
|
* An entry whose token starts moving again is reported as STALE by the guard —
|
|
|
* prune it (a stale exception is the same debt as a stale allowlist row).
|
|
|
*
|
|
|
* All entries below were measured during the F2-A adversarial review
|
|
|
* (2026-08-21) unless dated otherwise.
|
|
|
*/
|
|
|
|
|
|
const PLANE_SUPERSEDED =
|
|
|
'SUPERSEDED by the depth plane: the part carries data-depth=overlay, and [data-depth=overlay] declares this property at the same specificity (0,1,0) and LATER in the cascade, so the recipe key is bypassed (see next-features 12.9, measured on tooltip, popover and float-panel)';
|
|
|
|
|
|
export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
|
|
|
avatar: {
|
|
|
// 2026-08-23. Five steps of scales the measured stage does not wear, all
|
|
|
// forced on the REAL nodes with transitions frozen. Two of them sit on the
|
|
|
// `xxl` step, which the size sweep of the guard does not reach (the same
|
|
|
// limit `metrics` recorded below).
|
|
|
'size-xxl':
|
|
|
'the size sweep of the guard stops at xl; forced data-size=xxl -> reaches (block-size 96px -> 1234px)',
|
|
|
'font-size-xxl':
|
|
|
'same xxl step, read on the fallback initials (the image carries no font); forced -> reaches (28px -> 37px)',
|
|
|
'radius-none': 'only the step in force paints; forced data-radius=none -> reaches (0px -> 37px)',
|
|
|
'ring-width-sm':
|
|
|
'only the step in force paints (the demo boots ringWidth=md); forced data-ring-width=sm -> reaches (halo 1.5px -> 17px)',
|
|
|
'group-overlap-xxl':
|
|
|
'same xxl step, on AvatarGroup; forced data-size=xxl on the real group -> reaches (margin-inline-start -33.6px -> 37px)'
|
|
|
},
|
|
|
background: {
|
|
|
// 2026-08-23. Its parts are independent OPT-IN layers and only ONE pattern
|
|
|
// renders at a time, so the default stage moved 3 of 37 tokens. The guard
|
|
|
// now switches the scrim on and sweeps `data-pattern` (18/37); what is
|
|
|
// left is measured here, forcing the attribute on the REAL layer — never
|
|
|
// on a node built by hand.
|
|
|
//
|
|
|
// The mask: the guard does not read `mask-image`, so both read dead while
|
|
|
// being perfectly alive (instrument gap, next-features §13).
|
|
|
'fade-size':
|
|
|
'the guard does not snapshot mask-image; measured by hand → reaches (the radial mask changes)',
|
|
|
'fade-at':
|
|
|
'the guard does not snapshot mask-image; measured by hand → reaches (at 50% 35% → at 11% 50%)',
|
|
|
// The ink context: only under `on='dark'` / `on='light'`.
|
|
|
'scrim-fg-over-dark': "only under on='dark'; forced → reaches (the veil colour changes)",
|
|
|
'scrim-fg-over-light': "only under on='light'; forced → reaches (the veil colour changes)",
|
|
|
// Two scales where only the step in force paints (the `section` / `qr-code`
|
|
|
// class): every step measured one by one on the real scrim layer.
|
|
|
'scrim-blur-sm': 'only the step in force paints; forced data-blur=sm → reaches (4px → 33px)',
|
|
|
'scrim-blur-md': 'only the step in force paints; forced data-blur=md → reaches (8px → 33px)',
|
|
|
'scrim-blur-lg': 'only the step in force paints; forced data-blur=lg → reaches (12px → 33px)',
|
|
|
'scrim-blur-xxl': 'only the step in force paints; forced data-blur=xxl → reaches (24px → 33px)',
|
|
|
'scrim-strength-xs':
|
|
|
'only the step in force paints; forced data-strength=xs → reaches (alpha 0.08 → 0.9)',
|
|
|
'scrim-strength-sm':
|
|
|
'only the step in force paints; forced data-strength=sm → reaches (alpha 0.13 → 0.9)',
|
|
|
'scrim-strength-lg':
|
|
|
'only the step in force paints; forced data-strength=lg → reaches (alpha 0.4 → 0.9)',
|
|
|
'scrim-strength-xl':
|
|
|
'only the step in force paints; forced data-strength=xl → reaches (alpha 0.7 → 0.9)',
|
|
|
// A gradient layer EXCLUDES a pattern layer, so the sweep cannot have both.
|
|
|
'gradient-drift-duration':
|
|
|
'the gradient layer is exclusive with the pattern one the sweep needs; forced data-kind=gradient → reaches (24s → 42s)',
|
|
|
// The spotlight is off on the default stage and, once on, it repaints the
|
|
|
// pattern layer it shares a node with — measured on its own pass.
|
|
|
'spotlight-size':
|
|
|
'spotlight is off in the demo; forced data-spotlight → reaches (40% 40% → 42px)',
|
|
|
'spotlight-strength':
|
|
|
'spotlight is off in the demo; forced data-spotlight → reaches (the mix stop moves)',
|
|
|
'spotlight-fg':
|
|
|
'spotlight is off AND the demo layer carries data-color, where --palette-solid wins by design; forced data-spotlight with no data-color → reaches',
|
|
|
// Structural, not conditional: the travel only enters through the
|
|
|
// scroll-linked `background-parallax` keyframes, which need a scroll
|
|
|
// timeline the measured stage does not have.
|
|
|
'parallax-travel':
|
|
|
'the demo boots at speed 0, so no layer carries data-parallax; forced data-parallax + speed 1 on the real layer, the scroll-linked translate follows it (14.6 -> 60.5 px at the same scrollY). The guard would not see it anyway: translate / transform are not in its property snapshot (next-features 13)',
|
|
|
// No node at all: the demo never mounts the pause control.
|
|
|
'pause-offset': 'the pause control is not mounted in the demo (0 nodes on the page)',
|
|
|
'pause-z': 'the pause control is not mounted in the demo (0 nodes on the page)'
|
|
|
},
|
|
|
badge: {
|
|
|
// 2026-08-23. The demo mounts ONE chip, and its `rounded` axis paints only
|
|
|
// the step in force — measured forcing each on the real chip.
|
|
|
'radius-sm': 'only the step in force paints; forced data-rounded=sm → reaches (4px → 37px)',
|
|
|
'radius-lg': 'only the step in force paints; forced data-rounded=lg → reaches (10px → 37px)'
|
|
|
},
|
|
|
'color-swatch': {
|
|
|
// 2026-08-23. The inset ring is OPT-IN (`bordered`, off by default) and NO
|
|
|
// consumer in the repo passes it, so the `::after` that draws it exists on
|
|
|
// no page. Forced `data-bordered` on a real chip of the gradient-builder
|
|
|
// stage, transitions frozen: the ring appears and both tokens follow.
|
|
|
'border-width':
|
|
|
'the `bordered` ring is opt-in and no consumer mounts it; forced data-bordered on the real chip → reaches (inset 0 0 0 1px → 1234px)',
|
|
|
'border-fg':
|
|
|
'same opt-in ring; forced data-bordered on the real chip → reaches (oklch(0.931 0 0) → rgb(1, 2, 3))'
|
|
|
},
|
|
|
form: {
|
|
|
// 2026-08-23.
|
|
|
'grid-min':
|
|
|
"only under [data-layout='grid'], and the computed template only CHANGES when the value alters the column count: forced the layout and 90px -> reaches (502px -> four 110.5px columns)",
|
|
|
'error-summary-list-indent':
|
|
|
'the summary renders EMPTY in the demo (no errors), so it has no <ul> to indent',
|
|
|
'link-underline-offset': 'same empty summary: no <a> inside it',
|
|
|
'auto-fields-array-padding':
|
|
|
"the array branch needs a schema field of type array; the demo's `auto` mode mounts group + field only",
|
|
|
'auto-fields-border-width': 'same array branch',
|
|
|
'auto-fields-radius': 'same array branch',
|
|
|
'auto-fields-item-gap': 'same array branch (the item exists only inside an array)',
|
|
|
'auto-fields-item-padding': 'same array branch',
|
|
|
'widget-padding-inline': 'the widget is the array toolbar affordance; same missing branch',
|
|
|
'widget-radius': 'same missing branch',
|
|
|
'widget-border-width': 'same missing branch',
|
|
|
'grid-column-gap':
|
|
|
"the demo runs layout='stack'; forced data-layout=grid -> reaches (20px -> 37px)",
|
|
|
'invalid-accent':
|
|
|
'only under [data-form][data-invalid]; forced on the form -> reaches (oklch(0.8059 0.1123 59) -> rgb(1,2,3)) on the summary border',
|
|
|
'link-underline-fg': 'same empty summary as the offset above: no <a> to underline',
|
|
|
'auto-fields-array-bg': 'same array branch the demo never mounts',
|
|
|
'auto-fields-item-bg': 'same array branch',
|
|
|
'auto-fields-border': 'same array branch (the group and the field do not draw it)',
|
|
|
'widget-bg': 'same missing branch',
|
|
|
'widget-border': 'same missing branch'
|
|
|
},
|
|
|
dialog: {
|
|
|
// 2026-08-23. Seven tokens of states the demo does not mount: it shows a
|
|
|
// middle-centred `md` dialog, open and neutral. All forced on the real
|
|
|
// content node, transitions frozen.
|
|
|
'content-max-height-sheet':
|
|
|
'only under [data-sheet] (the wrapper sets it below the sm breakpoint); forced -> reaches (850px -> 377px)',
|
|
|
'content-full-inset': "only under [data-size='full']; forced -> reaches (12px -> 37px)",
|
|
|
'content-position-inset':
|
|
|
'only on the 8 non-centred cells of the position grid; forced data-position=top-left -> reaches (16px -> 37px)',
|
|
|
'close-inset':
|
|
|
'the close button carries NO data-position in this demo, and every rule that reads it needs one; forced data-position=top-right -> reaches (12px -> 37px)',
|
|
|
// The exit tint: the morfo writes `data-last-action` before `data-state`
|
|
|
// flips to closed, so both attributes only coexist WHILE the dialog leaves.
|
|
|
'saved-border':
|
|
|
'only during the exit, with [data-state=closed][data-last-action=saved]; forced -> reaches (oklch(0.9582 0.0223 155) -> rgb(1,2,3))',
|
|
|
'failed-border':
|
|
|
'same exit window; forced -> reaches (oklch(0.9555 0.0207 13.8) -> rgb(1,2,3))',
|
|
|
'dismissed-border':
|
|
|
'same exit window (cancelled / dismissed / dismissed-outside share it); forced -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))'
|
|
|
},
|
|
|
'pin-input': {
|
|
|
// 2026-08-23. Six STATE tokens: the demo shows an empty, editable, valid
|
|
|
// pin. Forced on the real nodes — note WHERE: `filled` and `active` are
|
|
|
// per-CELL attributes, but `invalid` and `readonly` are stamped on the
|
|
|
// CONTAINER and cascade down (forcing them on the cell does nothing).
|
|
|
'cell-filled-bg':
|
|
|
'no filled cell in the demo; forced data-filled -> reaches (oklch(0.9588 0.0249 311.71) -> rgb(1,2,3))',
|
|
|
'cell-filled-border':
|
|
|
'same state; forced -> reaches (oklch(0.8043 0.0918 309.69) -> rgb(1,2,3))',
|
|
|
'cell-active-border':
|
|
|
'the cell under the caret; forced data-active -> reaches (oklch(0.5556 0.1829 305.86) -> rgb(1,2,3))',
|
|
|
'cell-invalid-border':
|
|
|
'stamped on the CONTAINER (soma OR-merges it with the enclosing Field); forced there -> reaches (oklch(0.6256 0.1933 23.03) -> rgb(1,2,3))',
|
|
|
'cell-readonly-bg':
|
|
|
'container attribute too; forced there -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))',
|
|
|
'disabled-opacity':
|
|
|
'no disabled pin in the demo; forced data-disabled -> reaches (0.4 -> 0.137)'
|
|
|
},
|
|
|
'spin-field': {
|
|
|
// 2026-08-23. Measured inside number-field (this is a shared LAYER with no
|
|
|
// route of its own), transitions frozen except for the last pair.
|
|
|
'active-control-border':
|
|
|
"the ACTIVE state is the scrubber's [data-scrubbing], not :active; forced on the real scrubber -> reaches (oklch(0.8043 0.0918 309.69) -> rgb(1,2,3))",
|
|
|
'active-control-bg':
|
|
|
'same drag state; forced -> reaches (oklch(0.9932 0.0034 325.6) -> rgb(1,2,3))',
|
|
|
'active-control-fg':
|
|
|
'same drag state; forced -> reaches (oklch(0.5168 0.1733 305.88) -> rgb(1,2,3))',
|
|
|
'focus-control-bg':
|
|
|
'only under :focus-visible, which the guard blurs away on purpose; forced the pseudo via CDP -> reaches (oklch(0.931 0 0) -> rgb(1,2,3))',
|
|
|
'control-disabled-opacity':
|
|
|
'no disabled stepper in the demo; forced data-disabled -> reaches (0.48 -> 0.137)',
|
|
|
'transition-duration':
|
|
|
'frozen by the guard by design; unfrozen pass -> reaches (0.12s -> 11.5s)',
|
|
|
'transition-ease':
|
|
|
'same freeze; unfrozen pass -> reaches (cubic-bezier(0.4,0,0.2,1) -> steps(7))'
|
|
|
},
|
|
|
meter: {
|
|
|
// 2026-08-23. The demo sits on the OPTIMUM zone, so the two off-zone inks
|
|
|
// have no node to paint; forced on the real root, transitions frozen.
|
|
|
'indicator-bg-below':
|
|
|
"only under [data-state='below']; forced -> reaches (oklch(0.6908 0.1909 45.02) -> rgb(1,2,3))",
|
|
|
'indicator-bg-above':
|
|
|
"only under [data-state='above']; forced -> reaches (oklch(0.6256 0.1933 23.03) -> rgb(1,2,3))",
|
|
|
'transition-duration':
|
|
|
'frozen by the guard by design; unfrozen pass -> reaches (0.18s -> 11.5s)',
|
|
|
'transition-ease':
|
|
|
'same freeze; unfrozen pass -> reaches (cubic-bezier(0.4,0,0.2,1) -> steps(7))'
|
|
|
},
|
|
|
progress: {
|
|
|
// 2026-08-23. The guard now sweeps `data-shape` (like its twin meter) and
|
|
|
// switches the demo's indeterminate + vertical controls on, which takes it
|
|
|
// from 22/41 to 40/43. What is left needs a state the demo cannot hold at
|
|
|
// the same time as the ones already forced, or a property the guard freezes.
|
|
|
'indicator-bg-loaded':
|
|
|
"only under [data-state='loaded'], and the run holds `indeterminate` to reach the sweep tokens; forced on the real root -> reaches (oklch(0.6406 0.1329 157.68) -> rgb(1,2,3))",
|
|
|
'transition-duration':
|
|
|
'frozen by the guard by design; unfrozen pass -> reaches (0.18s -> 11.5s)',
|
|
|
'transition-ease':
|
|
|
'same freeze; unfrozen pass -> reaches (cubic-bezier(0.4,0,0.2,1) -> steps(7))'
|
|
|
},
|
|
|
'emoji-picker': {
|
|
|
// 2026-08-23. Four keys of the PORTALED panel. The guard writes the
|
|
|
// sentinel on :root and on the host, and :root does cross the portal
|
|
|
// (verified) — what it cannot do is measure a node the demo never mounts
|
|
|
// or one that carries no attribute of the component.
|
|
|
'tone-marker-size':
|
|
|
'the corner dot lives on [data-tone-capable]::after and the demo mounts ZERO tone-capable cells; forced the attribute on a real option -> reaches (4px -> 37px)',
|
|
|
'tone-marker-radius': 'same pseudo, same absent state; forced -> reaches (9999px -> 37px)',
|
|
|
'tone-marker-bg': 'same pseudo, same absent state; forced -> reaches (-> rgb(1,2,3))',
|
|
|
'preview-name-fg':
|
|
|
'the preview children are bare <span>s stamped inline, with no data-emoji-picker-* attr, so they are outside the measured set; read on the last child -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))',
|
|
|
// NON-DETERMINISTIC, and that is the reason: three runs of the same code
|
|
|
// gave 17/21, 16/21, 16/21 — the panel closes somewhere in the pass and the
|
|
|
// trigger loses [data-state=open], which is what this rule selects on.
|
|
|
// Measured alive FOUR times, the last one replicating the guard's own
|
|
|
// staticPass step by step (moved on the first size): oklch(0.2435 0 0) ->
|
|
|
// rgb(1,2,3), from :root and from the host. An openMarker on the trigger was
|
|
|
// tried and did NOT fix it (still oscillates), so the cause is the reopen
|
|
|
// timing, not the marker. EXPECT THIS ENTRY TO BE FLAGGED STALE on the runs
|
|
|
// where it does reach — do not delete it on that alone. next-features 13.
|
|
|
'open-trigger-fg':
|
|
|
'oscillates run to run (17/21 vs 16/21 on the same code); measured alive four times, including a step-by-step replica of the guard staticPass -> reaches (oklch(0.2435 0 0) -> rgb(1,2,3))'
|
|
|
},
|
|
|
'color-field': {
|
|
|
// 2026-08-23. Thirteen tokens, none of them a lie: the demo boots with a
|
|
|
// value, in `md`, editable, with three formats allowed — so every state
|
|
|
// and every size step but the mounted one has no node to paint. All
|
|
|
// forced on the REAL nodes (never a fabricated one), transitions frozen.
|
|
|
'placeholder-fg':
|
|
|
'no empty field in the demo; forced data-placeholder → reaches (oklch(0.61 0 0) → rgb(1,2,3))',
|
|
|
'segment-invalid-fg':
|
|
|
'invalid is off in the demo; forced data-invalid → reaches (oklch(0.5855 0.1743 42.74) → rgb(1,2,3))',
|
|
|
'segment-readonly-bg':
|
|
|
'readonly is off in the demo; forced data-readonly → reaches (oklch(0.931 0 0) → rgb(1,2,3))',
|
|
|
'segment-readonly-fg':
|
|
|
'readonly is off in the demo; forced data-readonly → reaches (oklch(0.5032 0 0) → rgb(1,2,3))',
|
|
|
'segment-readonly-decoration':
|
|
|
'readonly is off in the demo; forced data-readonly → reaches (underline dotted → overline wavy)',
|
|
|
'disabled-opacity': 'disabled is off in the demo; forced data-disabled → reaches (0.4 → 0.123)',
|
|
|
// The swatch scale: only the step in force paints. Forced each size on the
|
|
|
// real root and measured the swatch — every step reaches.
|
|
|
'swatch-size-xs': 'only the size in force paints; forced data-size=xs → reaches (14px → 77px)',
|
|
|
'swatch-size-sm': 'only the size in force paints; forced data-size=sm → reaches (16px → 77px)',
|
|
|
'swatch-size-lg': 'only the size in force paints; forced data-size=lg → reaches (20px → 77px)',
|
|
|
'swatch-size-xl': 'only the size in force paints; forced data-size=xl → reaches (32px → 77px)',
|
|
|
// The locked-format LABEL only renders when a single format is allowed
|
|
|
// (`isLocked`). Driven through the demo's own chips — not a hand-made node.
|
|
|
'format-select-fg':
|
|
|
'the static label only renders with ONE allowed format; unchecked rgb+hsl in the demo → reaches (oklch(0.5032 0 0) → rgb(1,2,3))',
|
|
|
// The known law: the guard freezes `transition` to make everything else
|
|
|
// measurable, so the tokens that ARE the transition can never move under
|
|
|
// it. Measured in a separate unfrozen pass, on an EDITABLE segment — the
|
|
|
// first segment is a `literal`, which declares `transition: none` of its
|
|
|
// own and reads 0s whatever the token says.
|
|
|
'transition-duration':
|
|
|
'frozen by the guard by design; unfrozen pass on the hex segment → reaches (0.12s → 11.5s)',
|
|
|
'transition-ease':
|
|
|
'frozen by the guard by design; unfrozen pass on the hex segment → reaches (cubic-bezier(0.4,0,0.2,1) → steps(7))'
|
|
|
},
|
|
|
'navigation-menu': {
|
|
|
// 2026-08-23. The rule is `[…-trigger][data-disabled], […-link][data-disabled]`
|
|
|
// and the demo mounts neither — measured with transitions FROZEN, because
|
|
|
// `color` is transitioned on this part and an immediate read returns the
|
|
|
// OLD value (the eyedropper trap: three false diagnoses before freezing).
|
|
|
'disabled-trigger-fg':
|
|
|
'no disabled trigger or link in the demo; forced data-disabled → reaches on BOTH parts (rgb(1,2,3))'
|
|
|
},
|
|
|
command: {
|
|
|
'focus-input-border':
|
|
|
'consumed only under :focus-visible, which the guard blurs away ON PURPOSE (the click-focus poisoning fix); focused by hand → reaches (rgb(9,9,9))',
|
|
|
'loading-height': 'the loading bar is conditional and the demo does not mount it',
|
|
|
'loading-fg': 'the loading bar is conditional and the demo does not mount it',
|
|
|
'viewport-padding': 'the non-ScrollArea viewport variant is not mounted in the demo',
|
|
|
'empty-padding-block': 'the empty state is conditional and not mounted at measurement',
|
|
|
'empty-fg': 'the empty state is conditional and not mounted at measurement'
|
|
|
},
|
|
|
table: {
|
|
|
'header-z':
|
|
|
'consumed only under [data-sticky-header], which the demo does not set; forced → reaches (2 → 4321)',
|
|
|
'row-detail-fg':
|
|
|
'detail rows are closed (display:none) in the demo initial state; forced data-state=open → reaches (rgb(1,2,3))',
|
|
|
'row-detail-padding-block':
|
|
|
'detail rows are closed (display:none) in the demo initial state; forced data-state=open → reaches (12 → 1234)'
|
|
|
},
|
|
|
'media-player': {
|
|
|
'accent-strong': 'consumed by the live indicator, which only paints under [data-live]',
|
|
|
'buffered-bg':
|
|
|
'forwarded into the composed Slider (SecondaryRange) — outside this component attribute space',
|
|
|
'captions-bg': 'captions are conditional and the demo video carries no track',
|
|
|
'captions-fg': 'captions are conditional and the demo video carries no track',
|
|
|
'captions-radius': 'captions are conditional and the demo video carries no track',
|
|
|
'captions-padding-block': 'captions are conditional and the demo video carries no track',
|
|
|
'captions-padding-inline': 'captions are conditional and the demo video carries no track',
|
|
|
'captions-line-height': 'captions are conditional and the demo video carries no track',
|
|
|
'volume-vertical-length': 'the volume float is interaction-mounted; not open at measurement',
|
|
|
'volume-float-padding-block':
|
|
|
'the volume float is interaction-mounted; not open at measurement',
|
|
|
'volume-float-padding-inline':
|
|
|
'the volume float is interaction-mounted; not open at measurement',
|
|
|
'rate-button-min-width': 'the rate control is conditional and the demo does not mount it',
|
|
|
'audio-title-padding-block': 'audio variant — the video demo does not mount the audio title',
|
|
|
'audio-title-padding-inline': 'audio variant — the video demo does not mount the audio title'
|
|
|
},
|
|
|
'tree-grid': {
|
|
|
'header-z':
|
|
|
'consumed only under [data-sticky-header], which the demo does not set; forced → reaches (2 → 4321)',
|
|
|
'disabled-row-fg': 'no disabled row in the demo; forced data-disabled → reaches (rgb(1,2,3))'
|
|
|
},
|
|
|
'gradient-picker': {
|
|
|
'chip-radius':
|
|
|
'the swatch is a CLASS hook (.gradient-picker-trigger-swatch) outside the attribute-filtered node set; direct read → reaches (§13 class-hook debt)',
|
|
|
'chip-border':
|
|
|
'the swatch is a CLASS hook outside the attribute-filtered node set; direct read → reaches (§13 class-hook debt)',
|
|
|
'content-width-sm':
|
|
|
"sized by data-picker-size (picker-shell's axis, not data-size); forced → reaches (288 → 512)",
|
|
|
'content-width-lg':
|
|
|
"sized by data-picker-size (picker-shell's axis, not data-size); forced → reaches (352 → 512)",
|
|
|
'stoplist-max-block':
|
|
|
"consumer node lives in the composed ScrollArea's attribute space (--scroll-area-auto-max-block on the viewport); direct read → reaches (132 → 1234)",
|
|
|
'stoplist-scrollbar-size':
|
|
|
'the design scrollbar only mounts on overflow; the demo list is short',
|
|
|
'stoplist-scrollbar-inset':
|
|
|
"consumer node lives in the composed ScrollArea's attribute space; direct read → reaches (12 → 1234)",
|
|
|
'presets-panel-width':
|
|
|
"the presets popover needs a second click the guard's open step does not perform; forced → reaches (240 → 1234)",
|
|
|
'presets-gap': 'presets popover not mounted by the open step; forced → reaches (8 → 1234)',
|
|
|
'presets-min-column':
|
|
|
'presets popover not mounted by the open step; forced → reaches (66px columns → 1234)',
|
|
|
'preset-height': 'presets popover not mounted by the open step; forced → reaches (32 → 1234)',
|
|
|
'preset-radius': 'presets popover not mounted by the open step; forced → reaches (6 → 1234)',
|
|
|
'preset-border': 'presets popover not mounted by the open step; forced → reaches (rgb(1,2,3))',
|
|
|
'hover-preset-border':
|
|
|
'presets popover not mounted by the open step; forced + real hover → reaches (rgb(1,2,3))',
|
|
|
'selected-preset-border':
|
|
|
'presets popover not mounted by the open step; forced data-selected → reaches (rgb(1,2,3))',
|
|
|
'selected-preset-outline-offset':
|
|
|
'presets popover not mounted by the open step; forced data-selected → reaches (2 → 1234)'
|
|
|
},
|
|
|
listbox: {
|
|
|
'invalid-border': 'no invalid listbox in the demo; forced data-invalid → reaches (rgb(1,2,3))'
|
|
|
},
|
|
|
carousel: {
|
|
|
'vertical-block-size':
|
|
|
'vertical orientation not mounted in the demo; forced → reaches (256 → 1234)'
|
|
|
},
|
|
|
feed: {
|
|
|
'sentinel-spinner-size':
|
|
|
'the spinner only exists while [data-busy], and the guard WAITS for the busy window to close (the settle fix); measured live during the busy window (::before, 12 → 1234)',
|
|
|
'sentinel-spinner-width':
|
|
|
'the spinner only exists while [data-busy]; measured live during the busy window (::before border)',
|
|
|
'sentinel-spinner-gap':
|
|
|
'the spinner only exists while [data-busy]; measured live during the busy window (::before margin)',
|
|
|
'sentinel-spinner-duration':
|
|
|
'the spinner only exists while [data-busy]; measured live during the busy window (animation-duration 0.7s → 11.5s)',
|
|
|
'article-title-font-size-5':
|
|
|
'the demo mounts heading levels 3–4 only; forced aria-level=5 → reaches (14 → 1234)',
|
|
|
'article-title-font-size-6':
|
|
|
'the demo mounts heading levels 3–4 only; forced aria-level=6 → reaches (12 → 1234)',
|
|
|
'sentinel-spinner-reduced-duration':
|
|
|
'behind prefers-reduced-motion; emulated reduce context → reaches (4s → 11.5s)'
|
|
|
},
|
|
|
'drag-drop': {
|
|
|
'preview-padding-block':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (8 → 1234px)',
|
|
|
'preview-padding-inline':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (12 → 1235px)',
|
|
|
'preview-radius':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (6 → 1236px)',
|
|
|
'preview-border-width':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (1 → 9px)',
|
|
|
'preview-bg':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (rgb(1,2,3))',
|
|
|
'preview-fg':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (rgb(4,5,6))',
|
|
|
'preview-font-family':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (Zapfino)',
|
|
|
'preview-font-size':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (14 → 99px)',
|
|
|
'preview-shadow':
|
|
|
'the drag PREVIEW is created only while a drag is in flight (0 nodes at rest, counted); forced → reaches (0 0 0 7px rgb(7,8,9))'
|
|
|
},
|
|
|
link: {
|
|
|
'external-icon-size':
|
|
|
'the demo mounts no EXTERNAL link, so the glyph never renders (0 nodes, counted); forced → reaches (11.89 → 77px on both axes)',
|
|
|
'underline-width':
|
|
|
"the demo link sits at data-underline='hover' with no pointer on it, so there IS no underline to move (text-decoration-line: none, thickness: auto). Forced 'always' → reaches (1 → 9px). textDecorationThickness was missing from the guard prop list and was added in the same pass",
|
|
|
'underline-offset':
|
|
|
"same: no underline at rest under data-underline='hover'; forced → reaches (2 → 11px). textUnderlineOffset added to the guard prop list too"
|
|
|
},
|
|
|
knob: {
|
|
|
'label-font-size-sm':
|
|
|
'the demo mounts no [data-knob-label] (0 nodes, counted); forced → reaches (per-size coordinate; the resolved name moves with it)',
|
|
|
'label-font-size-md':
|
|
|
'the demo mounts no [data-knob-label] (0 nodes, counted); forced → reaches (16 → 99px)',
|
|
|
'label-font-size-lg':
|
|
|
'the demo mounts no [data-knob-label] (0 nodes, counted); forced → reaches (per-size coordinate)',
|
|
|
'label-font-size-xl':
|
|
|
'the demo mounts no [data-knob-label] (0 nodes, counted); forced → reaches (per-size coordinate)',
|
|
|
'label-font-size':
|
|
|
'the demo mounts no [data-knob-label] (0 nodes, counted); forced → reaches (16 → 99px)',
|
|
|
'label-fg':
|
|
|
'the demo mounts no [data-knob-label] (0 nodes, counted); forced → reaches (rgb(1,2,3))',
|
|
|
'label-line-height':
|
|
|
'the demo mounts no [data-knob-label] (0 nodes, counted); forced → reaches (20px → 3.77em)',
|
|
|
'dragging-shadow':
|
|
|
'consumed only under [data-dragging], and the demo knob is at rest (0 nodes, counted); forced → reaches (0 0 0 7px rgb(4,5,6))'
|
|
|
},
|
|
|
label: {
|
|
|
'disabled-fg':
|
|
|
'the demo mounts no disabled label (0 [data-disabled] nodes, counted); forced → reaches (rgb(1,2,3))'
|
|
|
},
|
|
|
'link-preview': {
|
|
|
'font-family':
|
|
|
"declared but out-ranked: the panel stamps `data-depth='overlay'`, whose plane declares font-family too at an EQUAL (0,1,0) — the generated sheet loads later, so it wins. Kept on purpose: retiring it would fix that tie in the plane's favour, a pixel decision pending signature (next-features §12.9). Measured with CDP matched-rules 2026-08-21, stable across 5 dev reloads",
|
|
|
'line-height':
|
|
|
'same load-order tie with the `overlay` plane, which imposes 1.25 where this recipe asks for --leading-normal (1.5). Declared and kept until the signature resolves who owns overlay typography',
|
|
|
'trigger-fg':
|
|
|
'the panel opens on HOVER, so measuring it REQUIRES the pointer on the trigger — and hover is exactly the state whose rule overrides the resting ink. Structural, not a demo gap: the hover token (`hover-trigger-fg`) reads live in the same run',
|
|
|
'trigger-underline':
|
|
|
'same structural conflict: the hover rule sets `text-decoration-color: currentColor` over the resting mix, and the panel cannot be opened without hovering'
|
|
|
},
|
|
|
'menu-dial': {
|
|
|
'backdrop-bg':
|
|
|
'the backdrop only exists while the dial is open (0 nodes at rest, counted); forced → reaches (rgb(1,2,3))',
|
|
|
'stagger-step':
|
|
|
"consumed only by `[data-stagger] [data-state='open'] .menu-dial-action`, and the demo dial is closed and un-staggered; forced both attrs with index 3 → reaches (0.084s → 2.331s = 3 × 777ms)"
|
|
|
},
|
|
|
'picker-shell': {
|
|
|
'section-gap-xs':
|
|
|
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',
|
|
|
'section-gap-sm':
|
|
|
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',
|
|
|
'section-gap-lg':
|
|
|
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',
|
|
|
'row-spacing-xs':
|
|
|
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',
|
|
|
'row-spacing-lg':
|
|
|
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',
|
|
|
'footer-gap-xs':
|
|
|
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',
|
|
|
'footer-gap-lg':
|
|
|
'its size axis is `data-picker-size` on the PORTALED [data-popover-content], not `data-size` on itself, and the guard only cycles `data-size`; the demo popover sits at md. Cycled by hand across the four picker-sizes 2026-08-21 → reaches (section/row/footer spacing follows the picker size)',
|
|
|
'header-font-size-xs':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
|
|
|
'header-font-size-sm':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
|
|
|
'header-font-size-md':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
|
|
|
'header-font-size-lg':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (12/14/16/16px → 102px, each in its own picker-size)',
|
|
|
'header-gap':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
|
|
|
'header-fg':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
|
|
|
'header-font-weight':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
|
|
|
'header-line-height':
|
|
|
'the date-picker demo renders no [data-picker-header] (0 nodes, counted) — picker-shell has no demo route of its own (404) and is measured inside a host picker; forced → reaches (gap 8→1234px, fg → rgb(4,5,6), weight 500→123, line-height 20→60.32px)',
|
|
|
'time-row-gap':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-row-padding-block-start':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-row-padding-inline':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-row-border-width':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-row-border':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-label-fg':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-label-font-size-xs':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-label-font-size-sm':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-label-font-size-md':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)',
|
|
|
'time-label-font-size-lg':
|
|
|
'the time row is CLASS-hooked (.picker-time-row) and the date-picker demo does not render it; the guard filters nodes by attribute, so it cannot see it either way (§13 class-hook debt). Forced → reaches (gap/padding → 1234px, border 1→9px + rgb(7,8,9), label → rgb(10,11,12) / 99px)'
|
|
|
},
|
|
|
'scroll-frames': {
|
|
|
'loader-inset-block-end':
|
|
|
'the loading caption only exists while frames are decoding (0 nodes once loaded, counted); forced → reaches (24 → 1234px)',
|
|
|
'loader-font-size':
|
|
|
'the loading caption only exists while frames are decoding (0 nodes once loaded, counted); forced → reaches (12 → 99px)',
|
|
|
'loader-letter-spacing':
|
|
|
'the loading caption only exists while frames are decoding (0 nodes once loaded, counted); forced → reaches (1.2 → 9px)',
|
|
|
'loader-fg':
|
|
|
'the loading caption only exists while frames are decoding (0 nodes once loaded, counted); forced → reaches (rgb(1,2,3))'
|
|
|
},
|
|
|
skeleton: {
|
|
|
'line-radius':
|
|
|
"only the `text` shape renders [data-skeleton-line]; the demo mounts `rect` (0 lines, counted). Forced shape='text' → reaches (4 → 1234px)"
|
|
|
},
|
|
|
'skin-media-player': {
|
|
|
'record-base-width':
|
|
|
'the width is `min(100%, base * scale)`, so the LARGE sentinel of the guard is capped by the container and proves nothing. Re-measured with SMALL values 2026-08-22, with data-skin=record forced (the demo mounts turntable) -> reaches (111px exactly)',
|
|
|
'cassette-base-width':
|
|
|
'the width is `min(100%, base * scale)`, so the LARGE sentinel of the guard is capped by the container and proves nothing. Re-measured with SMALL values 2026-08-22, data-skin=cassette forced -> reaches (122px exactly)',
|
|
|
'deck-base-width':
|
|
|
'the width is `min(100%, base * scale)`, so the LARGE sentinel of the guard is capped by the container and proves nothing. Re-measured with SMALL values 2026-08-22, data-skin=deck forced -> reaches (133px exactly)',
|
|
|
'scale-sm':
|
|
|
'the width is `min(100%, base * scale)`, so the LARGE sentinel of the guard is capped by the container and proves nothing. Re-measured with SMALL values 2026-08-22, with the data-size=sm of the PLAYER forced -> reaches (133 x 0.11 = 14.625px)',
|
|
|
'scale-lg':
|
|
|
'the width is `min(100%, base * scale)`, so the LARGE sentinel of the guard is capped by the container and proves nothing. Re-measured with SMALL values 2026-08-22, data-size=lg forced -> reaches (133 x 0.22 = 29.25px)',
|
|
|
'scale-xl':
|
|
|
'the width is `min(100%, base * scale)`, so the LARGE sentinel of the guard is capped by the container and proves nothing. Re-measured with SMALL values 2026-08-22, data-size=xl forced -> reaches (133 x 0.33 = 43.875px)',
|
|
|
'lamp-opacity':
|
|
|
'the lamp only dims while the player is paused and the deck still; forced -> reaches (0.18 -> 0.123)'
|
|
|
},
|
|
|
spinner: {
|
|
|
'bar-radius':
|
|
|
"the demo mounts the RING variant only (0 bars, 0 dots, counted); forced variant='bars' → reaches (1234px)",
|
|
|
'label-font-size':
|
|
|
'the label is optional and the demo mounts none (0 nodes, counted); forced → reaches (1234px)',
|
|
|
'label-fg': 'the label is optional and the demo mounts none; forced → reaches (rgb(1,2,3))',
|
|
|
'track-opacity':
|
|
|
'its only declaration lives inside `prefers-reduced-motion`; emulated reduce context → reaches (0.6 → 0.123)'
|
|
|
},
|
|
|
'card-group': {
|
|
|
'title-fg':
|
|
|
'the STATIC heading form is not mounted in the demo (0 [data-static] nodes vs 1 [data-button], counted); forced → reaches (rgb(1,2,3))',
|
|
|
'title-font-family':
|
|
|
'static heading not mounted in the demo; forced → reaches (Instrument Sans → Zapfino)',
|
|
|
'title-font-size': 'static heading not mounted in the demo; forced → reaches (16 → 1234px)',
|
|
|
'title-line-height': 'static heading not mounted in the demo; forced → reaches (20px → 3.77em)',
|
|
|
'title-padding-block-end':
|
|
|
'static heading not mounted in the demo; forced → reaches (8 → 1234px)'
|
|
|
},
|
|
|
clipboard: {
|
|
|
'indicator-gap':
|
|
|
'the “Copied!” indicator only exists for a moment after a copy (0 nodes at rest, counted); forced → reaches (4 → 1234px)',
|
|
|
'indicator-padding-block':
|
|
|
'the “Copied!” indicator only exists for a moment after a copy (0 nodes at rest, counted); forced → reaches (2 → 1235px)',
|
|
|
'indicator-padding-inline':
|
|
|
'the “Copied!” indicator only exists for a moment after a copy (0 nodes at rest, counted); forced → reaches (8 → 1236px)',
|
|
|
'indicator-radius':
|
|
|
'the “Copied!” indicator only exists for a moment after a copy (0 nodes at rest, counted); forced → reaches (9999 → 1237px)',
|
|
|
'indicator-font-size':
|
|
|
'the “Copied!” indicator only exists for a moment after a copy (0 nodes at rest, counted); forced → reaches (12 → 99px)',
|
|
|
'indicator-font-weight':
|
|
|
'the “Copied!” indicator only exists for a moment after a copy (0 nodes at rest, counted); forced → reaches (500 → 123)'
|
|
|
},
|
|
|
code: {
|
|
|
radius:
|
|
|
'the DEMO HARNESS wins: `[data-uix-docs] code` (uix.css, 0-1-1) beats the recipe base rule `[data-code]` (0-1-0) and sets border-radius/padding/font on the inline code — confirmed with CDP matched-rules. The variant rules (0,2,0) DO win, which is why only this base-rule token reads dead. Same harness rule that hides the <pre> of code-block (next-features §13)',
|
|
|
'soft-bg':
|
|
|
"the demo mounts the `plain` variant only (no chrome); forced variant='soft' → reaches (rgb(1,2,3))",
|
|
|
'border-width': "the demo mounts `plain` only; forced variant='outline' → reaches (1 → 9px)",
|
|
|
border: "the demo mounts `plain` only; forced variant='outline' → reaches (rgb(4,5,6))",
|
|
|
'padding-inline':
|
|
|
'only the chromed variants pad; forced → reaches (4.12 → 77px), and the variant rule (0,2,0) beats the harness',
|
|
|
'padding-block': 'only the chromed variants pad; forced → reaches (1.29 → 66px)'
|
|
|
},
|
|
|
'code-block': {
|
|
|
'pre-padding':
|
|
|
'the DEMO HARNESS outweighs the component: `[data-uix-docs] pre` (uix.css:801, 0-1-1) beats `[data-code-block-pre]` (0-1-0) and paints padding/border/radius/bg/font on the <pre>. Forced with !important → reaches (16px 20px → 12px). Seven properties measured, registered in next-features §13'
|
|
|
},
|
|
|
'tree-view': {
|
|
|
'disabled-row-fg':
|
|
|
'no disabled row in the demo (0 mounted, counted); forced data-disabled → reaches (oklch(0.61 0 0) → rgb(1,2,3))'
|
|
|
},
|
|
|
'password-field': {
|
|
|
// The demo boots with an empty field: data-strength=0, so no segment is
|
|
|
// active and none of the four steps paints. Forced level by level
|
|
|
// 2026-08-22, transitions frozen.
|
|
|
'strength-1-bg': 'the meter sits at strength 0 (empty field); forced -> reaches (oklch(0.5787 0.1877 322.11) -> rgb(1,2,3))',
|
|
|
'strength-2-bg': 'same; forced -> reaches (oklch(0.6908 0.1909 45.02) -> rgb(1,2,3))',
|
|
|
'strength-3-bg': 'same; forced -> reaches (oklch(0.6406 0.1329 157.68) -> rgb(1,2,3))',
|
|
|
'strength-4-bg': 'same; forced -> reaches (oklch(0.6491 0.1136 181.96) -> rgb(1,2,3))',
|
|
|
// The visibility trigger owns both: the guard freezes transitions, and the
|
|
|
// demo boots enabled. Measured on it 2026-08-22.
|
|
|
'transition-duration': 'the guard freezes transitions; measured without the freeze on the visibility trigger -> reaches (0.12s -> 11.5s)',
|
|
|
'transition-ease': 'same freeze, same node -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(4))',
|
|
|
'disabled-opacity': 'only under [data-disabled] on the visibility trigger; forced -> reaches (0.4 -> 0.123)'
|
|
|
},
|
|
|
'context-menu': {
|
|
|
// Third measurement of the same finding: the overlay DEPTH PLANE imposes the
|
|
|
// panel font and beats the recipe (section 12.9, pending signature, 20
|
|
|
// components). Writing the token moves nothing while the plane does.
|
|
|
'item-disabled-opacity': 'no disabled item in the demo; forced data-disabled -> reaches (0.55 -> 0.123)'
|
|
|
},
|
|
|
'float-panel': {
|
|
|
// The demo mounts the DEFAULT variant and no generic action button (only the
|
|
|
// close control). Forced in place 2026-08-22, transitions frozen.
|
|
|
'outline-bg': 'the demo runs the default variant; set data-variant=outline -> reaches (oklch(0.9911 0 0) -> rgb(1,2,3))',
|
|
|
'outline-border': 'same variant; forced -> reaches (oklch(0.7921 0 0) -> rgb(1,2,3))',
|
|
|
'ghost-bg': 'same, variant ghost; forced -> reaches (oklch(0.9911 0 0) -> rgb(1,2,3))',
|
|
|
'ghost-border': 'same, variant ghost; forced -> reaches (oklch(0.931 0 0) -> rgb(1,2,3))',
|
|
|
'ghost-shadow': 'same, variant ghost; forced -> reaches (rgba(15,23,42,0.1) 0 4px 12px -> rgb(1,2,3) 0 0 0 7px)',
|
|
|
'action-fg': 'no generic action button in the demo (only the close control); mounted in place -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))',
|
|
|
'action-radius': 'same button; mounted -> reaches (4px -> 1234px)',
|
|
|
'action-font-size': 'same button; mounted -> reaches (16px -> 1234px)',
|
|
|
'hover-action-bg': 'same button, and only under :hover; same mounting pass',
|
|
|
'hover-action-fg': 'same button, and only under :hover; same mounting pass',
|
|
|
'resize-grip-radius': 'only under :focus-visible; focused the grip -> reaches (4px -> 1234px)',
|
|
|
// 2026-08-23. The twelve the 2026-08-22 commit left written as "rojo que
|
|
|
// queda" and nobody measured. Measured now, one by one, on the real panel.
|
|
|
//
|
|
|
// SIX are the depth plane, exactly as that commit predicted: the content
|
|
|
// carries `data-depth='overlay'`, and `[data-depth='overlay']` declares
|
|
|
// background / border / box-shadow / font-family / line-height at the SAME
|
|
|
// specificity (0,1,0) and LATER in the cascade. Verified by moving the
|
|
|
// plane's own tokens on this node: surface, border, font and leading
|
|
|
// repaint it, and the plane's `box-shadow` rule is in its matched list.
|
|
|
// Same adjudication as `tooltip`, same open decision (PLAN 12.9).
|
|
|
bg: PLANE_SUPERSEDED,
|
|
|
border: PLANE_SUPERSEDED,
|
|
|
'border-width': PLANE_SUPERSEDED + ' (the plane writes the `border` shorthand)',
|
|
|
shadow:
|
|
|
PLANE_SUPERSEDED +
|
|
|
'; `shadow-topmost` DOES reach — its [data-topmost] rule is (0,2,0) and beats the plane (-> rgb(4,5,6))',
|
|
|
'font-family': PLANE_SUPERSEDED,
|
|
|
'line-height': PLANE_SUPERSEDED,
|
|
|
// Three states / parts the demo does not mount.
|
|
|
'active-shadow':
|
|
|
'only under [data-dragging] / [data-resizing]; forced -> reaches (rgba(15,23,42,0.22) 0 28px 72px -> rgb(1,2,3))',
|
|
|
'resize-size':
|
|
|
'the CORNER handles (se/ne/sw/nw) carry it and the demo mounts only the edge ones (e, s)',
|
|
|
'button-size':
|
|
|
'consumed by [data-float-panel-action] / [data-float-panel-stage-trigger], neither mounted in the demo — the only control is the close Button, which is a composed <Button> and sizes itself (same group as the action-* entries above)',
|
|
|
// The palette bridge (THM-2), not debt: with a colour stamped the forward
|
|
|
// redefines the private and this key is only the no-colour base.
|
|
|
accent:
|
|
|
'the content carries data-color, so --_float-panel-accent comes from the THM-2 forward (--_float-panel-palette-border -> rgb(7,8,9)); with data-color removed it reaches (-> rgb(1,2,3))',
|
|
|
// The known law: the guard freezes transitions to make everything else
|
|
|
// measurable. Unfrozen pass on the panel itself.
|
|
|
'transition-duration':
|
|
|
'frozen by the guard by design; unfrozen pass -> reaches (0.12s -> 11.5s)',
|
|
|
'transition-ease':
|
|
|
'frozen by the guard by design; unfrozen pass -> reaches (cubic-bezier(0.4,0,0.2,1) -> steps(7))'
|
|
|
},
|
|
|
drawer: {
|
|
|
// The guard OPENS the drawer before testing, and the overlay then covers the
|
|
|
// trigger: the hover pass cannot reach it (Playwright refuses to hover a
|
|
|
// node that does not receive the pointer). Hovered by hand with the drawer
|
|
|
// closed 2026-08-22 -> reaches.
|
|
|
'hover-trigger-bg': 'the open drawer overlay covers the trigger, so the hover pass cannot land on it; hovered by hand with the drawer closed -> reaches (oklch(0.931 0 0) -> rgb(1,2,3))',
|
|
|
// ── The 24 the component carried from before the axis, measured one by one
|
|
|
// 2026-08-22 (transitions frozen, drawer open, each state forced).
|
|
|
// The GRABBER is not mounted in this demo and its size swaps with the side:
|
|
|
// length is the inline axis for top/bottom and the block axis for left/right.
|
|
|
'handle-bg': 'no grabber in this demo; mounted in place -> reaches (oklch(0.9911 0 0) -> rgb(1,2,3))',
|
|
|
'active-handle-bg': 'same grabber, and only while dragging; forced -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))',
|
|
|
'handle-radius': 'same grabber; mounted -> reaches (9999px -> 1234px)',
|
|
|
'handle-length': 'same grabber, and only under side=top/bottom; forced -> reaches (36px -> 1234px)',
|
|
|
'handle-thickness': 'same grabber, same side; forced -> reaches (4px -> 1234px)',
|
|
|
'handle-active-scale': 'same grabber, only while dragging (transform, which the guard does not read)',
|
|
|
'handle-transition-duration': 'the guard freezes transitions; the grabber transitions background and transform on it',
|
|
|
'handle-transition-ease': 'same freeze, same pair',
|
|
|
// The dragging ring only exists while a pointer drags the panel.
|
|
|
'content-ring-dragging-fg': 'only under [data-dragging]; forced -> reaches (srgb 0.392 / 0.32 -> rgb(1,2,3))',
|
|
|
'content-ring-dragging-width': 'same state; forced -> reaches (2px -> 1234px)',
|
|
|
'content-ring-dragging-offset': 'same state, outline-offset (read set)',
|
|
|
'content-shadow-dragging': 'same state; forced -> the box-shadow changes',
|
|
|
// Sizes: the demo runs md on the right side, and soma writes the resolved
|
|
|
// axis INLINE (width for left/right, height for top/bottom), so the token of
|
|
|
// the axis in play cannot win from :root — a value channel, like carousel
|
|
|
// item-gap. The cross axis does reach: height-sm forced 260px -> 976px.
|
|
|
'content-width-sm': 'soma writes width INLINE on the content for a left/right drawer, so no cascade can beat it - value channel, registered in next-features 13',
|
|
|
'content-width-md': 'same inline width channel',
|
|
|
'content-width-lg': 'same inline width channel',
|
|
|
'content-height-sm': 'the demo runs a right drawer, where the height is not the axis in play; forced side=bottom -> reaches (260px -> 976px)',
|
|
|
'content-height-md': 'same cross axis, same pass',
|
|
|
'content-height-lg': 'same cross axis, same pass',
|
|
|
'content-padding-full': 'only under [data-size=full]; forced -> reaches (24px -> 1234px)',
|
|
|
// Header and footer are optional parts the demo does not render.
|
|
|
'header-gap': 'the demo mounts no header; mounted in place -> reaches (12px -> 1234px)',
|
|
|
'footer-gap': 'the demo mounts no footer; mounted in place -> reaches (12px -> 1234px)',
|
|
|
'footer-margin-top': 'same footer; mounted -> reaches (8px -> 1234px)',
|
|
|
'inline-z': 'only under [data-variant=inline]; forced -> reaches (50 -> 4321)',
|
|
|
// AND A REAL DEFECT, not an instrument limit: the veil paints NOTHING.
|
|
|
'overlay-bg': 'the veil paints nothing today: soma writes --drawer-overlay-opacity INLINE as the unitless 1 (its drag progress) and the CSS consumes it as a color-mix PERCENTAGE, so the mix is invalid and the background falls to transparent. Writing 80% on the node paints it. Registered in next-features 13 - it moves pixel, so it is not fixed here'
|
|
|
},
|
|
|
aura: {
|
|
|
// The six state tints and the halo paint the INDICATOR background, and the
|
|
|
// demo runs with the WebGL canvas mounted — the rule
|
|
|
// [data-aura-indicator]:has(canvas) sets background:none ON PURPOSE (the orb
|
|
|
// IS the presence; a disc behind it muddies the silhouette). Removed the
|
|
|
// canvas in place 2026-08-22: the gradient returns and all six follow.
|
|
|
'orb-color-idle': 'the canvas covers the disc by design; removed in place -> reaches (oklch(0.6434 0 0) -> rgb(1,2,3) inside the radial-gradient)',
|
|
|
'orb-color-offered': 'same disc, same pass',
|
|
|
'orb-color-planned': 'same disc, same pass',
|
|
|
'orb-color-reviewing': 'same disc, same pass',
|
|
|
'orb-color-acting': 'same disc, same pass',
|
|
|
'orb-color-escalated': 'same disc, same pass',
|
|
|
'orb-halo-alpha': 'the outer stop of the same gradient; removed the canvas -> the gradient changes',
|
|
|
// The arc and the halo are ring SLOTS that only exist in the working
|
|
|
// states; the demo sits on the track. Mounted as <circle data-aura-ring-slot>
|
|
|
// inside the real SVG 2026-08-22 — which is exactly what the CSS selects.
|
|
|
'ring-arc-width': 'the arc slot is not mounted in the idle demo; mounted in place -> reaches (2px -> 1234px)',
|
|
|
'ring-halo-width': 'the halo slot is not mounted; mounted in place -> reaches (1px -> 1234px)',
|
|
|
'ring-halo-opacity': 'same slot; mounted in place -> reaches (0.5 -> 0.123)',
|
|
|
'transition-duration': 'the guard freezes transitions on purpose; the orb tint and the arc stroke transition on it',
|
|
|
'transition-ease': 'same freeze, same pair of properties'
|
|
|
},
|
|
|
popover: {
|
|
|
// The demo renders neither Title nor Description. Mounted inside the OPEN
|
|
|
// panel with the attrs the CSS selects (2026-08-22): all nine reach; the two
|
|
|
// with a number are the sample that was read.
|
|
|
'title-fg': 'the demo mounts no Popover.Title; mounted in place -> reaches (oklch(0.2435 0 0) -> rgb(1,2,3))',
|
|
|
'title-font-size': 'same part; mounted in place -> reaches (16px -> 1234px)',
|
|
|
'title-font-family': 'same part, same measurement pass',
|
|
|
'title-font-weight': 'same part, same measurement pass',
|
|
|
'title-line-height': 'same part, same measurement pass',
|
|
|
'description-fg': 'the demo mounts no Popover.Description; mounted in place -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))',
|
|
|
'description-font-family': 'same part, same measurement pass',
|
|
|
'description-font-size': 'same part, same measurement pass',
|
|
|
'description-line-height': 'same part, same measurement pass',
|
|
|
// The overlay is opt-in (modal popovers) and the demo boots with it off.
|
|
|
'overlay-bg': 'the demo boots with showOverlay off; mounted in place -> reaches (rgba(28,25,23,0.42) -> rgb(1,2,3))',
|
|
|
'overlay-z': 'same; mounted in place -> reaches (60 -> 4321)',
|
|
|
// The two that are NOT about mounting: the depth plane wins over both.
|
|
|
'font-family':
|
|
|
'the overlay DEPTH PLANE imposes font-family on the panel and beats the recipe: the section 12.9 finding, pending signature (20 components). Measured 2026-08-22, writing the token moves nothing while the plane does.',
|
|
|
'line-height':
|
|
|
'same plane, same finding: the token cannot win until the plane stops owning typography'
|
|
|
},
|
|
|
'radio-cards': {
|
|
|
// All five forced by hand 2026-08-22, WITH transitions frozen: the card
|
|
|
// transitions `border-color`, and reading right after the write returns the
|
|
|
// starting value (the same trap the guard's own freeze exists for — the
|
|
|
// first measurement of invalid-border read "no effect" for that reason).
|
|
|
'invalid-border':
|
|
|
'only paints under [data-invalid] and the demo boots valid; forced -> reaches (oklch(0.9555 0.0207 13.86) -> rgb(1,2,3))',
|
|
|
'horizontal-basis':
|
|
|
'only under [data-orientation=horizontal]; forced -> reaches (192px -> 1234px of flex-basis)',
|
|
|
'disabled-opacity':
|
|
|
'only under [data-disabled] on the item; forced -> reaches (0.4 -> 0.123)',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions on purpose; measured without the freeze -> reaches (0.12s -> 11.5s)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured without it -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(4))'
|
|
|
},
|
|
|
prose: {
|
|
|
// Prose styles bare HTML at ZERO specificity on purpose (`:where([data-prose] el)`,
|
|
|
// so an embedded component keeps its own look). That is exactly what the docs
|
|
|
// harness beats: `[data-uix-docs] code|pre` (0,2,0) wins over (0,0,0). Removing
|
|
|
// the harness attribute in place 2026-08-22 -> the five reach. Same §13 entry as
|
|
|
// code-block's, one floor down.
|
|
|
'code-bg': 'the harness rule [data-uix-docs] code (0,2,0) beats :where(…) (0,0,0); without it -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))',
|
|
|
'code-radius': 'same harness rule; without it -> reaches (4px -> 1234px)',
|
|
|
'code-font-family': 'same harness rule; without it -> reaches (Azeret Mono -> Zapfino)',
|
|
|
'pre-bg': 'the harness rule [data-uix-docs] pre (0,2,0) beats :where(…); without it -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))',
|
|
|
'pre-radius': 'same harness rule; without it -> reaches (6px -> 1234px)',
|
|
|
// Two elements the demo's sample HTML does not use. Mounted INSIDE the real
|
|
|
// [data-prose] container 2026-08-22 — and a bare <mark>/<dt> IS what this
|
|
|
// recipe styles (it selects raw HTML), so the node is faithful.
|
|
|
'mark-bg': 'the sample HTML has no <mark>; mounted in place -> reaches (oklch(0.9923 0.0025 48.72) -> rgb(1,2,3))',
|
|
|
'mark-radius': 'same; mounted in place -> reaches (4px -> 1234px)',
|
|
|
'dt-font-weight': 'the sample HTML has no <dl>; mounted in place -> reaches (600 -> 123)'
|
|
|
},
|
|
|
result: {
|
|
|
// All five measured on the demo's REAL nodes (its own controls drive them),
|
|
|
// not on a node built by hand — the drag-drop lesson: a "forced -> reaches"
|
|
|
// adjudication is worth what the forced node is worth.
|
|
|
'extra-margin-block-start':
|
|
|
'the extra block is optional and the demo boots without it; ticked its checkbox 2026-08-22 -> reaches (16px -> 1234px)',
|
|
|
'extra-measure':
|
|
|
'same optional block; ticked 2026-08-22 -> reaches (559.44px -> 1234px)',
|
|
|
'error-fg':
|
|
|
'only paints under [data-status=error] and the demo boots on success; clicked its chip 2026-08-22 -> reaches (oklch(0.6256 0.1933 23.03) -> rgb(1,2,3))',
|
|
|
'warning-fg':
|
|
|
'only paints under [data-status=warning]; clicked its chip 2026-08-22 -> reaches (oklch(0.6908 0.1909 45.02) -> rgb(1,2,3))',
|
|
|
'info-fg':
|
|
|
'only paints under [data-status=info]; clicked its chip 2026-08-22 -> reaches (oklch(0.5556 0.1829 305.86) -> rgb(1,2,3))',
|
|
|
'extra-font-size':
|
|
|
'same optional block; ticked its checkbox 2026-08-22 -> reaches (14px -> 1234px)',
|
|
|
'extra-fg': 'same optional block; ticked 2026-08-22 -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))',
|
|
|
'media-fg':
|
|
|
'the DEFAULT glyph ink: with a [data-status] the private wins by design (each status re-points --_result-media-fg to its accent) and the demo always sets one. Removed the attribute 2026-08-22 -> reaches (oklch(0.61 0 0) -> rgb(1,2,3))',
|
|
|
'code-font-family':
|
|
|
'the big HTTP code only renders for the http statuses and the demo boots on success; clicked the not-found chip 2026-08-22 -> reaches (Azeret Mono -> Zapfino)',
|
|
|
'code-font-weight': 'same http-only node; clicked not-found 2026-08-22 -> reaches (600 -> 123)',
|
|
|
'code-letter-spacing':
|
|
|
'same http-only node; clicked not-found 2026-08-22 -> reaches (3.36px -> 4.5px)',
|
|
|
'code-fg':
|
|
|
'same http-only node; clicked not-found 2026-08-22 -> reaches (oklch(0.61 0 0) -> rgb(1,2,3))'
|
|
|
},
|
|
|
chart: {
|
|
|
'transition-duration':
|
|
|
"the guard FREEZES transitions on purpose (a transitioned property returns its starting value), which is exactly what hides a token whose whole job IS the transition — same class as text-circular.char-transition. Measured without the freeze 2026-08-22 on [data-chart-line]: 0.12s -> 11.5s",
|
|
|
'transition-ease':
|
|
|
"same freeze, same measurement 2026-08-22 on [data-chart-line]: cubic-bezier(0, 0, 0.2, 1) -> steps(4)"
|
|
|
},
|
|
|
'text-circular': {
|
|
|
'char-transition':
|
|
|
"the guard FREEZES transitions on purpose (a transitioned property returns its starting value), which is exactly what hides a token whose whole job is the transition. Measured without the freeze 2026-08-22 -> reaches (0.5s cubic-bezier -> 9.5s linear)"
|
|
|
},
|
|
|
textarea: {
|
|
|
'focus-input-border':
|
|
|
'consumed only under [data-focused], which the guard blurs away ON PURPOSE (the click-focus poisoning fix); focused by hand with the pointer parked → reaches (rgb(4,5,6))',
|
|
|
'invalid-input-border':
|
|
|
'consumed only under [data-invalid]; the demo boots valid. Forced by hand 2026-08-21 → reaches (rgb(7,8,9))',
|
|
|
'overflow-count-fg':
|
|
|
'the count only carries [data-overflow] past maxLength (280 chars); forced → reaches (rgb(13,14,15))',
|
|
|
'overflow-count-font-weight':
|
|
|
'the count only carries [data-overflow] past maxLength (280 chars); forced → reaches (font-weight 333)'
|
|
|
},
|
|
|
'grid-list': {
|
|
|
'invalid-border':
|
|
|
'consumed only under [data-invalid]; the demo boots with its invalid control off, so the rule never matches at measurement. Toggled by hand 2026-08-21 → reaches (oklch(0.9555 0.0207 13.86) → rgb(1,2,3))'
|
|
|
},
|
|
|
metrics: {
|
|
|
// Measured one by one 2026-08-23 (transitions frozen, each condition forced).
|
|
|
// Every one of the 16 is ALIVE: the demo mounts neither the xxl step, nor the
|
|
|
// icon without a data-color, nor the actions row, nor an end-placed chart.
|
|
|
'gap-xxl': 'the size sweep of the guard does not reach the xxl step; forced data-size=xxl -> reaches (12px -> 1234px)',
|
|
|
'icon-gap-xxl': 'same xxl step, read on the padding-inline-start of an icon-start root; forced -> reaches (92px -> 1306px)',
|
|
|
'icon-box-xxl': 'same xxl step; forced -> reaches (72px -> 1234px)',
|
|
|
'label-font-size-xxl': 'same xxl step; forced -> reaches (28px -> 1234px)',
|
|
|
'value-font-size-xxl': 'same xxl step; forced -> reaches (80px -> 1234px)',
|
|
|
'description-font-size-xxl': 'same xxl step; forced -> reaches (28px -> 1234px)',
|
|
|
'icon-border-width': 'only the outline variant draws a stroke and the demo runs soft; forced data-variant=outline -> reaches (1px -> 1234px)',
|
|
|
'icon-bg': 'the no-data-color default, and every demo icon carries a colour that overrides the hook; removed data-color -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))',
|
|
|
'icon-fg': 'same no-data-color default; removed data-color -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))',
|
|
|
'actions-gap': 'the actions row is conditional and the demo mounts none; mounted in place -> reaches (12px -> 1234px)',
|
|
|
'actions-margin-block-start': 'same row; mounted -> reaches (12px -> 1234px)',
|
|
|
'actions-padding-block-start': 'same row; mounted -> reaches (12px -> 1234px)',
|
|
|
'actions-border-fg': 'same row; mounted -> reaches (oklch(0.931 0 0) -> rgb(1,2,3))',
|
|
|
'chart-right-width': 'only an end-placed chart reserves the gutter and the demo places them full; forced data-placement=end -> reaches (112px -> 1234px)',
|
|
|
'flash-duration': 'the flash rule needs the sema stamp of the hold (data-event-family=signal + phase=active), which is gone by the time the guard reads; stamped by hand -> reaches (0.32s -> 11.5s)',
|
|
|
'flash-ease': 'same stamp; forced -> reaches (cubic-bezier(0, 0, 0.2, 1) -> steps(7))',
|
|
|
'icon-circle-radius': 'the demo mounts only the rounded shape; forced data-shape=circle -> reaches (9999px -> 1234px)',
|
|
|
'icon-square-radius': 'same, the demo mounts only rounded; forced data-shape=square -> reaches (6px -> 1234px)'
|
|
|
},
|
|
|
'color-picker': {
|
|
|
// Measured one by one 2026-08-23 (picker open, transitions frozen unless the
|
|
|
// token IS the transition). 21 of the 23 are alive; the two width steps are a
|
|
|
// real reach defect, registered in next-features 13.
|
|
|
'content-width-sm': 'the private --_color-picker-content-width is declared three times and consumed ZERO times: the portalled panel hardwires calc(content-width-md + 2rem), so no sm step can reach it. Registered in next-features 13 - wiring or retiring it moves pixel',
|
|
|
'content-width-lg': 'same orphan private; the panel width has no size axis today. Registered in next-features 13',
|
|
|
'swatch-size-sm': 'inside the portalled content the private is pinned to swatch-size-md with no size variants; on a swatch outside the portal the step reaches (20px -> 1234px). Same entry in next-features 13',
|
|
|
'swatch-size-md': 'the demo mounts no swatch of any kind; mounted in place -> reaches (24px -> 1234px)',
|
|
|
'swatch-size-lg': 'same pinning inside the portal; outside it reaches (28px -> 1234px)',
|
|
|
'swatch-radius': 'presets and saved swatches are conditional and the demo mounts none; mounted -> reaches (4px -> 1234px)',
|
|
|
'swatch-border': 'same swatches; mounted -> reaches (oklch(0.931 0 0) -> rgb(1,2,3))',
|
|
|
'swatch-checked-ring': 'same swatches, and only under [data-checked]; mounted and checked -> reaches (the 3px ring turns rgb(1,2,3))',
|
|
|
'swatch-gap': 'the swatch group is conditional and the demo mounts none; mounted -> reaches (6px -> 1234px)',
|
|
|
'swatch-border-width': 'same swatches; mounted -> reaches (1px -> 1234px)',
|
|
|
'swatch-ring-gap': 'same swatches, and only under [data-checked] or :focus-visible; mounted and checked -> reaches (the 1px inner ring turns rgb(1,2,3))',
|
|
|
'transparency-cell-alt': 'the standalone transparency grid utility is not mounted in the demo; mounted -> reaches (oklch(0.9911 0 0) -> rgb(1,2,3))',
|
|
|
'eye-dropper-size': 'the DEMO route overrides the eye dropper with its own scoped rule (.cp-tools-row sets inline-size:auto + aspect-ratio); with the token consumption restored -> reaches (30px -> 1234px)',
|
|
|
'eye-dropper-bg': 'same demo override (background:transparent); restored and frozen -> reaches (transparent -> rgb(1,2,3))',
|
|
|
'hue-0': 'the rainbow ramp needs data-x-channel=hue and the demo area runs saturation/brightness; forced -> reaches (rgb(255,0,0) -> rgb(1,2,3))',
|
|
|
'hue-60': 'same hue ramp; forced data-x-channel=hue -> reaches',
|
|
|
'hue-120': 'same hue ramp; forced data-x-channel=hue -> reaches',
|
|
|
'hue-180': 'same hue ramp; forced data-x-channel=hue -> reaches',
|
|
|
'hue-240': 'same hue ramp; forced data-x-channel=hue -> reaches',
|
|
|
'hue-300': 'same hue ramp; forced data-x-channel=hue -> reaches',
|
|
|
'hue-360': 'same hue ramp; forced data-x-channel=hue -> reaches',
|
|
|
'saturation-floor': 'the ::before ramp needs x-channel=hue AND y-channel=saturation; forced -> reaches (rgb(128,128,128) -> rgb(1,2,3))',
|
|
|
'transition-duration': 'the guard freezes transitions before measuring (the forced-measurement fix) and these two tokens ARE the transition; measured unfrozen on the eye dropper -> reaches (0.12s -> 11.5s)',
|
|
|
'transition-ease': 'same instrument limit; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(7))',
|
|
|
'disabled-opacity': 'no disabled trigger or eye dropper in the demo; forced data-disabled -> reaches (0.4 -> 0.137)'
|
|
|
},
|
|
|
'dropdown-menu': {
|
|
|
'item-disabled-opacity':
|
|
|
'no disabled item in the demo menu; forced data-disabled 2026-08-23 -> reaches (0.55 -> 0.137)'
|
|
|
},
|
|
|
image: {
|
|
|
'fade-duration': 'the guard freezes transitions and this token IS the fade; measured unfrozen 2026-08-23 -> reaches (0.18s -> 11.5s)',
|
|
|
'fade-ease': 'same instrument limit; measured unfrozen -> reaches (cubic-bezier(0, 0, 0.2, 1) -> steps(7))',
|
|
|
'placeholder-base': 'consumed ONLY inside @media (prefers-reduced-motion: reduce), where the shimmer is replaced by a flat base; the guard runs with motion on. Measured with reducedMotion emulated -> reaches (oklch(0.9821 0 0) -> rgb(1,2,3))'
|
|
|
},
|
|
|
checkbox: {
|
|
|
// Measured one by one 2026-08-23. Two scoping traps of the instrument: the
|
|
|
// glyph only exists inside a CHECKED box, and the group label carries its
|
|
|
// own [data-disabled] (it is not inherited from the group).
|
|
|
'size-xs-indicator-stroke': 'the glyph exists only inside a CHECKED box and the first checkbox of the demo is empty; measured on a checked one with data-size forced -> reaches (2px -> 7px)',
|
|
|
'size-sm-indicator-stroke': 'same, forced -> reaches (2px -> 7px)',
|
|
|
'size-md-indicator-stroke': 'same, forced -> reaches (2px -> 7px)',
|
|
|
'size-lg-indicator-stroke': 'same, forced -> reaches (2px -> 7px)',
|
|
|
'size-xl-indicator-stroke': 'same, forced -> reaches (2px -> 7px)',
|
|
|
'invalid-border': 'only under [data-invalid]; forced -> reaches (oklch(0.9555 0.0207 13.86) -> rgb(1,2,3))',
|
|
|
'invalid-ring': 'the invalid focus ring: needs [data-invalid] AND :focus-visible, which the guard blurs on purpose; focused by hand -> reaches (the 4px ring turns rgb(1,2,3))',
|
|
|
'invalid-ring-gap': 'same double ring; forced -> reaches (the 2px gap turns rgb(4,5,6))',
|
|
|
'disabled-bg': 'only under [data-disabled]; forced -> reaches',
|
|
|
'disabled-border': 'only under [data-disabled]; forced -> reaches',
|
|
|
'disabled-opacity': 'only under [data-disabled]; forced -> reaches (0.4 -> 0.137)',
|
|
|
'disabled-indicator-fg': 'needs [data-disabled] AND a checked box (the glyph); forced on a checked one -> reaches',
|
|
|
'disabled-group-label-fg': 'the label carries its OWN [data-disabled] - it is not inherited from the group, so forcing the group does nothing; set on the label -> reaches (oklch(0.5032 0 0) -> rgb(1,2,3))',
|
|
|
'transition-duration': 'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s)',
|
|
|
'transition-ease': 'same instrument limit; measured unfrozen -> reaches',
|
|
|
'stroke-duration': 'same, on the check path dash animation; measured unfrozen -> reaches (0.12s -> 11.5s)',
|
|
|
'stroke-ease': 'same, measured unfrozen -> reaches'
|
|
|
},
|
|
|
'anchor-nav': {
|
|
|
'link-radius':
|
|
|
'the link only rounds under :focus-visible (the ring corner), and the guard blurs on purpose; focused from the keyboard 2026-08-23 -> reaches (4px -> 1234px)'
|
|
|
},
|
|
|
tooltip: {
|
|
|
// Measured 2026-08-23 with the demo's `open` switch (a hover-opened panel is
|
|
|
// not reliable enough to measure: it needs the pointer parked on the trigger
|
|
|
// for the whole pass).
|
|
|
'content-z': 'it lands on the floating WRAPPER, which carries no data-tooltip-* attr and so is outside the measured set; read on the wrapper -> reaches (90 -> 4321)',
|
|
|
'bg': 'SUPERSEDED by the overlay plane: [data-depth=overlay] declares background at the same specificity and later in the cascade, so the recipe loses. Measured: --tooltip-bg moves nothing, --depth-overlay-surface repaints the panel. Registered in next-features 13 with the variant fallout',
|
|
|
'border': 'same supersession: --depth-overlay-border repaints it, --tooltip-border does not',
|
|
|
'shadow': 'same supersession: --depth-overlay-shadow repaints it, --tooltip-shadow does not',
|
|
|
'font-family': 'same plane, the typography half of it (PLAN 12.9, unsigned)',
|
|
|
'line-height': 'same plane, same open decision',
|
|
|
'outline-arrow-bg': 'the arrow is painted through a SIBLING selector that needs the content carrying data-variant=outline AND the panel open; measured with the demo switch -> reaches (oklch(0.9911 0 0) -> rgb(1,2,3))',
|
|
|
'outline-arrow-border': 'same sibling rule; measured -> reaches (oklch(0.7921 0 0) -> rgb(1,2,3))'
|
|
|
},
|
|
|
section: {
|
|
|
// ONE STEP PER INSTANCE: the wrapper maps the `padding` prop to
|
|
|
// var(--section-padding-block-{k}) and writes it INLINE, so only the active
|
|
|
// step is reachable at a time. Forcing another step reaches (2026-08-23).
|
|
|
'padding-block-sm': 'one step per instance; the demo runs md. Forced the step inline -> reaches',
|
|
|
'padding-block-lg': 'same, one step per instance',
|
|
|
'padding-block-xl': 'same; forced -> reaches (96px -> 123px)'
|
|
|
},
|
|
|
'qr-code': {
|
|
|
'size-xs': 'one step per instance: the wrapper writes var(--qr-code-size-{k}) inline from the size prop and the demo runs lg. Forced -> reaches (96px -> 123px)',
|
|
|
'size-sm': 'same, one step per instance',
|
|
|
'size-md': 'same, one step per instance',
|
|
|
'size-xl': 'same, one step per instance',
|
|
|
'fg': 'painted as the SVG fill of the module path, not as color on the root; read on the path -> reaches (rgb(24,24,27) -> rgb(1,2,3))',
|
|
|
'bg': 'painted as the fill of the backing rect, not as background on the root; read on the rect -> reaches (white -> rgb(1,2,3))'
|
|
|
},
|
|
|
'toggle-group': {
|
|
|
gap: 'the demo group is ATTACHED (segmented), where the gap collapses to 0; removed data-attached -> reaches (4px -> 123px)'
|
|
|
},
|
|
|
'button-group': {
|
|
|
'divider-strength':
|
|
|
'the seam only draws between ATTACHED buttons of a filled variant (solid/soft/ghost/plain) and the demo runs surface; forced -> reaches (0.18 -> 0.9 alpha)'
|
|
|
},
|
|
|
'audio-player': {
|
|
|
'live-font-weight': 'the LIVE badge only exists while the provider reflects a live stream (display:none otherwise) and the demo plays a file; mounted with [data-live] 2026-08-23 -> reaches (600 -> 333)',
|
|
|
'live-letter-spacing': 'same badge; mounted -> reaches (1.28px -> 7px)'
|
|
|
},
|
|
|
button: {
|
|
|
// Measured one by one 2026-08-23. The icon scale needs a real <svg> child,
|
|
|
// the radius scale is an OPT-IN prop the demo does not set, and the four
|
|
|
// palette-* slots are RESOLVED names: a :root write loses by design.
|
|
|
'icon-size-xs': 'the demo button carries no icon; a real <svg> child mounted and the size forced -> reaches (14px -> 123px)',
|
|
|
'icon-size-sm': 'same, no icon in the demo; mounted -> reaches (16px -> 123px)',
|
|
|
'icon-size-md': 'same, no icon in the demo; mounted -> reaches (18px -> 123px)',
|
|
|
'icon-size-lg': 'same, no icon in the demo; mounted -> reaches (20px -> 123px)',
|
|
|
'icon-size-xl': 'same, no icon in the demo; mounted -> reaches (32px -> 123px)',
|
|
|
'radius-sm': 'the radius scale is an opt-in prop ([data-rounded]) and the demo does not set it; forced -> reaches (4px -> 1234px)',
|
|
|
'radius-lg': 'same opt-in attribute; forced -> reaches (10px -> 1234px)',
|
|
|
'radius-xl': 'same opt-in attribute; forced -> reaches (16px -> 1234px)',
|
|
|
'radius-full': 'same opt-in attribute; forced data-rounded=full -> reaches (9999px -> 1234px)',
|
|
|
'transition-duration': 'the guard freezes transitions to measure and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s, five properties)',
|
|
|
'transition-ease': 'same instrument limit; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(7))',
|
|
|
'disabled-opacity': 'no disabled button in the demo; forced data-disabled -> reaches (0.4 -> 0.137)',
|
|
|
'primary-track': 'the FALLBACK of the palette forward: it acts only on an instance with NO data-color, and the demo stamps data-color=primary. Removed the attribute -> reaches. See the pattern entry below for the other seven tones',
|
|
|
'primary-element': 'same fallback, same condition',
|
|
|
'primary-border': 'same fallback, same condition',
|
|
|
'primary-solid': 'same fallback: without data-color it repaints (oklch(0.5556 0.1829 305.86) -> rgb(1,2,3)); with data-color=primary the generic forward rule supersedes it',
|
|
|
'primary-solid-hover': 'same fallback, same condition',
|
|
|
'primary-text': 'same fallback, same condition',
|
|
|
'primary-contrast': 'same fallback, same condition',
|
|
|
'palette-track': 'a RESOLVED name, declared on [data-button] by the forward, so a :root write loses BY DESIGN - a theme moves the tone or the scale. Measured on a soft button: from :root nothing, from the node oklch(0.9932 0.0034 325.6) -> rgb(4,5,6)',
|
|
|
'palette-element': 'same resolved name; the theme moves its coordinate',
|
|
|
'palette-border': 'same resolved name; the theme moves its coordinate',
|
|
|
'palette-text': 'same resolved name; the theme moves its coordinate'
|
|
|
},
|
|
|
menubar: {
|
|
|
'disabled-trigger-fg': 'no disabled entry in the demo bar; forced data-disabled 2026-08-23 -> reaches (oklch(0.61 0 0) -> rgb(1,2,3))'
|
|
|
},
|
|
|
field: {
|
|
|
// Measured one by one 2026-08-23 on /field, /date-field, /date-picker and
|
|
|
// /time-picker. The 20 are alive; every one needed a state, a variant, a
|
|
|
// route or an instrument the guard does not reach on its own.
|
|
|
'invalid-label-fg': 'only under [data-invalid] and the demo boots valid; forced -> reaches (oklch(0.5855 0.1743 42.74) -> rgb(1,2,3))',
|
|
|
'disabled-label-fg': 'only under [data-disabled]; forced -> reaches (oklch(0.7921 0 0) -> rgb(1,2,3))',
|
|
|
'invalid-control-border': 'only under [data-invalid]; forced -> reaches (oklch(0.8059 0.1123 59.96) -> rgb(1,2,3))',
|
|
|
'disabled-control-bg': 'only under [data-disabled]; forced -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))',
|
|
|
'control-bg-readonly': 'only under [data-readonly]; forced -> reaches (oklch(0.9551 0 0) -> rgb(1,2,3))',
|
|
|
'disabled-opacity': 'it lives on the CONTROL, not on the field root, so the guard read the wrong node; forced data-disabled and read on the control -> reaches (0.4 -> 0.137)',
|
|
|
'control-ghost-border': 'the ghost variant and the demo boots surface; forced data-variant=ghost -> reaches (transparent -> rgb(1,2,3))',
|
|
|
'control-ghost-bg': 'same ghost variant; forced -> reaches (transparent -> rgb(1,2,3))',
|
|
|
'layout-column-gap': 'only in the horizontal orientation; forced data-orientation=horizontal -> reaches (16px -> 1234px)',
|
|
|
'focus-control-border': 'only under :focus-within, and the guard blurs ON PURPOSE (the click-focus poisoning fix); focused from the keyboard -> reaches (the focus ring colour -> rgb(1,2,3))',
|
|
|
'focus-control-trigger-fg': 'only under :focus-visible on the picker trigger, which the guard blurs; focused from the keyboard on /date-picker -> reaches (oklch(0.2435 0 0) -> rgb(1,2,3))',
|
|
|
'floating-label-top': 'floatingLabel is a demo switch that boots OFF, and the label must also be floated (focused or filled); switch on + focus -> reaches (0px -> 1234px)',
|
|
|
'floating-label-padding-inline': 'same switch; turned on -> reaches (4px -> 1234px)',
|
|
|
'error-fg': 'the error text only RENDERS when the field is invalid, and it is the demo switch that mounts it - forcing the data-invalid attribute does not make Svelte render the node; switch on -> reaches (oklch(0.5855 0.1743 42.74) -> rgb(1,2,3))',
|
|
|
'control-placeholder-fg': 'the ::placeholder box exists only when the input HAS a placeholder attribute, and the demo input has none: getComputedStyle then returns the ELEMENT style and the token reads dead. With a placeholder set -> reaches (oklch(0.61 0 0) -> rgb(1,2,3))',
|
|
|
'segment-active-bg': 'the ACTIVE segment being edited: no segment on the field route and none focused; measured on /date-field with the segment clicked -> reaches (the 40% mix -> rgb(1,2,3))',
|
|
|
'segment-active-text': 'same segment; measured on /date-field -> reaches (oklch(0.5168 0.1733 305.88) -> rgb(1,2,3))',
|
|
|
'segment-height': 'a FAMILY token that field OWNS and the pickers CONSUME - nothing on the field routes reads it - and it is RESOLVED (declared per [data-field][data-size]), so a :root write loses BY DESIGN: a theme moves the coordinate. Measured on /time-picker: from :root nothing, from the field host 28px -> 1234px, and moving --field-control-height-md 28px -> 992px',
|
|
|
'transition-duration': 'the guard freezes transitions to measure and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s)',
|
|
|
'transition-ease': 'same instrument limit; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(7))'
|
|
|
},
|
|
|
'onion-menu': {
|
|
|
// Measured 2026-08-23. The lift needs the transitions frozen; the four rim
|
|
|
// tokens belong to the `luminous` look, which the component demo does not
|
|
|
// render at all (it is a Svelte prop that emits DIFFERENT markup, so no
|
|
|
// attribute can be forced): measured on /active/docs/agnt, which uses it.
|
|
|
'trigger-lift': 'the lift TRANSITIONS, so an immediate read after the write returns the old value; hovered with transitions frozen -> reaches (translate 0 -2px -> 0 -123px)',
|
|
|
'rim-width': 'the luminous look is not rendered by the component demo; measured on /active/docs/agnt -> reaches (2px -> 17px)',
|
|
|
'rim-glow-width': 'same look, same route -> reaches (9px -> 17px)',
|
|
|
'rim-glow-blur': 'same look, same route -> reaches (blur(4px) -> blur(13px))',
|
|
|
'rim-glow-opacity': 'same look, and the rim is under an ANIMATION: with only the transitions frozen the read returns the animated value (0.482956) and the token looks dead. With animation frozen too it computes 0.45 and follows -> reaches (0.45 -> 0.137)'
|
|
|
},
|
|
|
'file-upload': {
|
|
|
// Measured one by one 2026-08-23 (`__fu-adj`). The demo mounts the WHOLE
|
|
|
// surface (two seeded files, so list, preview, name, size, progress, remove
|
|
|
// and clear all render): what is left is state the route cannot reach and
|
|
|
// the freeze the guard does on purpose.
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen on the dropzone -> reaches (0.12s -> 11.5s on its three properties)',
|
|
|
'transition-ease':
|
|
|
'same instrument limit; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(7))',
|
|
|
'disabled-opacity':
|
|
|
'only under [data-disabled]; forced on the provider -> reaches (0.4 -> 0.123)',
|
|
|
'invalid-dropzone-border':
|
|
|
'only under [data-invalid], and this component takes invalid from an enclosing Field, which the demo route does not mount; forced -> reaches (oklch(0.8059 0.1123 59.96) -> rgb(1,2,3))'
|
|
|
},
|
|
|
switch: {
|
|
|
// Measured one by one 2026-08-23 (`__sw-adj`). The demo stage boots
|
|
|
// UNCHECKED, VALID and ENABLED and the switch has no trigger to open, so
|
|
|
// every state rule of the recipe is off-stage for the guard; the travel adds
|
|
|
// an instrument gap of its own.
|
|
|
'size-xs-thumb-translate':
|
|
|
'the travel lands on `transform`, which the guard does not snapshot, and it is 0px until the thumb is checked; forced data-state=checked + data-size=xs -> reaches (translateX 12px -> 1234px)',
|
|
|
'size-sm-thumb-translate': 'same pair of reasons; forced -> reaches (14px -> 1234px)',
|
|
|
'size-md-thumb-translate': 'same pair of reasons; forced -> reaches (16px -> 1234px)',
|
|
|
'size-lg-thumb-translate': 'same pair of reasons; forced -> reaches (18px -> 1234px)',
|
|
|
'size-xl-thumb-translate': 'same pair of reasons; forced -> reaches (20px -> 1234px)',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s on all four properties)',
|
|
|
'transition-ease':
|
|
|
'same instrument limit; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(7))',
|
|
|
'disabled-track-bg':
|
|
|
'only under [data-disabled]; forced -> reaches (color(srgb 0.9332 0.9334 0.9334) -> rgb(1,2,3))',
|
|
|
'disabled-track-border': 'same state; forced -> reaches (color(srgb 0.8685 …) -> rgb(1,2,3))',
|
|
|
'disabled-thumb-bg':
|
|
|
'same state, on the thumb; forced -> reaches (color(srgb 0.9867 …) -> rgb(1,2,3))',
|
|
|
'disabled-thumb-border':
|
|
|
'same state, on the thumb; forced -> reaches (color(srgb 0.9097 …) -> rgb(1,2,3))',
|
|
|
'disabled-opacity': 'same state; forced -> reaches (0.4 -> 0.123)',
|
|
|
'invalid-track-border':
|
|
|
'only under [data-invalid], which no trigger reaches and the guard does not force; forced -> reaches (oklch(0.9555 0.0207 13.86) -> rgb(1,2,3))'
|
|
|
}
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* The one architectural fact behind every tone key that reads dead. Measured
|
|
|
* 2026-08-23 on button, badge and callout: the palette forward emits a block per
|
|
|
* tone AND, LAST, a generic `[data-{c}][data-color]` that resolves the palette
|
|
|
* slots from the GLOBAL `--palette-*` (which `[data-color={tone}]` fills from
|
|
|
* `--color-{tone}-*`). Equal specificity (0,2,0), so the last one wins: with any
|
|
|
* tone attribute present the per-tone keys are bypassed. On data-color=risk,
|
|
|
* `--{c}-risk-solid` moves nothing at :root NOR on the node, while
|
|
|
* `--palette-solid` and `--color-risk-solid` on the node repaint. The primary-*
|
|
|
* keys are that rule's FALLBACK and do act on an instance with no data-color.
|
|
|
* 49 recipes emit the generic block and the contract holds 419 tone keys —
|
|
|
* registered in next-features §13, where the fix (emission order) is proposed.
|
|
|
*/
|
|
|
const PALETTE_SUPERSEDED =
|
|
|
'SUPERSEDED, not missing: the generic [data-{c}][data-color] block of the palette forward is emitted LAST and wins by source order at equal specificity, so the tone keys are bypassed and the paint comes from the global --palette-* (see next-features 13, measured on button, badge and callout 2026-08-23)';
|
|
|
|
|
|
/**
|
|
|
* The same adjudication, written ONCE for a whole family of keys. Born with
|
|
|
* button: 56 of its 106 keys are the eight tones x seven slots, and the reason
|
|
|
* they move nothing is ONE architectural fact, not 56 accidents. Copying the
|
|
|
* sentence 56 times would bury it.
|
|
|
*
|
|
|
* Checked only AFTER the exact lookup above fails, so a single key can always
|
|
|
* carry its own, more precise reason.
|
|
|
*/
|
|
|
export const SENTINEL_PATTERN_EXCEPTIONS: Array<{
|
|
|
component: string;
|
|
|
pattern: RegExp;
|
|
|
reason: string;
|
|
|
}> = [
|
|
|
{
|
|
|
component: 'button',
|
|
|
pattern:
|
|
|
/^(secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|element|border|solid|solid-hover|text|contrast)$/,
|
|
|
reason: PALETTE_SUPERSEDED + ' — the seven primary-* keys carry their own entry above'
|
|
|
},
|
|
|
{
|
|
|
component: 'callout',
|
|
|
pattern: /^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|text|solid)$/,
|
|
|
reason: PALETTE_SUPERSEDED
|
|
|
},
|
|
|
{
|
|
|
component: 'checkbox',
|
|
|
pattern: /^(primary|secondary|neutral|affirm)-(solid|solid-hover)$/,
|
|
|
reason: PALETTE_SUPERSEDED
|
|
|
},
|
|
|
{
|
|
|
component: 'badge',
|
|
|
pattern:
|
|
|
/^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|border|solid|text|contrast)$/,
|
|
|
reason: PALETTE_SUPERSEDED
|
|
|
},
|
|
|
{
|
|
|
component: 'form',
|
|
|
pattern: /^(action-|transition-|disabled-opacity$)/,
|
|
|
reason:
|
|
|
'SUPERSEDED by the composed Button: [data-form-submit] & co. also carry `data-button`, and `[data-button]` matches at the SAME specificity (0,1,0) but loads LATER, so button.css paints the whole action chrome. Measured 2026-08-23, eight properties one by one — height, padding-inline, font-size, font-weight, line-height, gap, radius, border-width — plus the transition pair and the disabled opacity: NOT ONE of them moves. Same defect gradient-picker documented (36 of its 45 keys) and emoji-picker found in its trigger; the answer there was to retire the dead declarations, and here that is 24 public keys — a decision for the author, so they are adjudicated meanwhile (next-features 13)'
|
|
|
},
|
|
|
{
|
|
|
component: 'surface',
|
|
|
pattern: /^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|solid|contrast)$/,
|
|
|
reason: PALETTE_SUPERSEDED
|
|
|
},
|
|
|
{
|
|
|
component: 'file-upload',
|
|
|
pattern:
|
|
|
/^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(solid|solid-hover|track|border|text)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — the eight tones x five slots, 40 of its 97 keys. Measured 2026-08-23 on the trigger (solid) and on the hovered dropzone (track) for the eight tones: not one moves, while --palette-solid on the node repaints (oklch(0.6908 …) -> rgb(4,5,6)). Its host default is `primary`, and the demo passes color explicitly, so the fallback never applies either'
|
|
|
},
|
|
|
{
|
|
|
component: 'switch',
|
|
|
pattern: /^(primary|secondary|neutral|affirm|risk|threat)-(solid|solid-hover)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — and here NEUTRAL falls with the rest: soma always stamps data-color (it is the resolved colour, neutral by default), so the generic block matches on every instance and the fallback the other recipes keep alive never applies. Measured checked 2026-08-23: --switch-neutral-solid moves nothing, --palette-solid on the node repaints (oklch(0.5556 …) -> rgb(4,5,6))'
|
|
|
}
|
|
|
];
|