|
|
/**
|
|
|
* 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>> = {
|
|
|
slider: {
|
|
|
// 2026-08-24. Seven keys, seven measured reasons, zero retirements: an
|
|
|
// orientation, a pointer held DOWN, two demo states and the transition
|
|
|
// pair the guard freezes. Every one reaches once its state exists — the
|
|
|
// contract does not lie, the stage only wears one of its faces.
|
|
|
'min-block-size':
|
|
|
"only [data-slider][data-orientation='vertical'] reads it, and the stage boots horizontal. Measured 2026-08-24 through the orientation chip of the demo: vertical -> reaches (block-size and min-block-size 160px -> 1234px)",
|
|
|
'active-thumb-scale':
|
|
|
'the pickup lift lives in `[data-slider-thumb]:active`, which needs the pointer HELD DOWN, and the static pass of the guard parks the pointer. Measured with a real mouse.down() on the real thumb, transitions frozen -> reaches (scale 1.15 -> 4.56)',
|
|
|
'active-thumb-shadow':
|
|
|
'same :active pickup, measured the same way -> reaches (rgba(15, 23, 42, 0.12) 0px 8px 24px -> rgb(1, 2, 3) 0px 0px 0px 7px)',
|
|
|
'active-tick-bg':
|
|
|
'a tick carries data-active only while its value sits INSIDE [min(values), max(values)] (soma SliderTickProvider.isActive), so one thumb at 40 with ticks at 0/50/100 leaves the three inactive — the token is not dead, its state is unreachable with a single value. Measured with the `multiple` chip of the demo (values 20-80, tick 50 goes active) -> reaches (oklch(0.5556 0.1829 305.86) -> rgb(1, 2, 3))',
|
|
|
'disabled-opacity':
|
|
|
'`[data-slider][data-disabled]`, and the stage boots enabled. Measured with the disabled toggle of the demo -> reaches (opacity 0.4 -> 0.123)',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s on the four transitioned properties of the thumb)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(4))'
|
|
|
},
|
|
|
'scroll-area': {
|
|
|
// 2026-08-24. Three keys, three measured reasons, zero retirements: one
|
|
|
// MODE the demo has no control for, and the transition pair the guard
|
|
|
// freezes in order to make every other token measurable.
|
|
|
'auto-max-block':
|
|
|
'the rule is `[data-scroll-area][data-autosize] [data-scroll-area-viewport]` and the stage mounts the default fixed-fill mode. There is no control for it, and `data-autosize` is stamped by the EIDOS wrapper from a prop, not declared by the morfo, so the demo exception of PLAN 7.8 does not open it either. Measured 2026-08-24 on the real viewport: the token alone leaves `max-block-size: none`; forcing data-autosize on the real root makes the same write reach (none -> 1234px), and removing the token restores none',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s on both transitioned properties of the scrollbar and on the one of the thumb)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(4), on scrollbar and thumb)'
|
|
|
},
|
|
|
fab: {
|
|
|
// 2026-08-24. ONE key, and it is the guard's blind spot for `translate` —
|
|
|
// the same limit already adjudicated for `transform` on background,
|
|
|
// rating-group and card, on the sibling property. Three independent
|
|
|
// reasons, all measured: the property is not in the read set; the static
|
|
|
// pass parks the pointer and this rule is `:hover`-only; and a sentinel-
|
|
|
// sized lift moves the node OUT from under the cursor, so the state
|
|
|
// cancels itself (`:hover` true at t+50ms, false by t+300ms).
|
|
|
lift: 'the guard does not snapshot `translate` (the `transform` limit of background / rating-group / card on the sibling property) and its static pass never hovers, while this rule is `[data-fab]:hover:not([data-disabled]):not(:active)`. Measured by hand with a real pointer: set --fab-lift=1234px BEFORE hovering and the FAB computes `translate: 0px -1234px` at t+300ms -> reaches. Note the self-cancel that makes it unmeasurable even with a hover pass: a 1234px lift carries the node out from under the cursor, :hover drops and it settles back to none by t+900ms'
|
|
|
},
|
|
|
'tags-input': {
|
|
|
// 2026-08-24. Four reasons, all measured on the real nodes with a real
|
|
|
// pointer; the twenty-four tone keys are the palette cascade and ride the
|
|
|
// PATTERN entry below.
|
|
|
'item-fg':
|
|
|
"the ARCHETYPE ink always wins: the morfo stamps aria-selected='true' as a LITERAL on EVERY tag, so archetypes.css's `[data-archetype='item'][aria-selected='true']:not([data-state='checked']):not([data-state='on'])` (0,4,0) out-ranks the recipe's `[data-tags-input-item]` (0,1,0) on every item, at rest and always. Measured 2026-08-24: with the attribute as shipped the token moves nothing (the ink stays oklch(0.2435 0 0) = --color-content-primary, the archetype's); set aria-selected=false on the real tag and the same write reaches (rgb(1, 2, 3)). Same class as table.selected-row-fg and the listbox ink (next-features §12); the aria literal is a morfo decision and is registered, not touched from this axis",
|
|
|
'placeholder-fg':
|
|
|
"the input carries a `placeholder` attribute only while it is EMPTY (soma: `isEmpty ? opts.placeholder : undefined`) and the stage boots with two tags, so ::placeholder returns the ELEMENT's style — the trap already measured on field. Emptying the stage is not an option for this run: it deletes every item node, which is where a third of the contract paints. Measured both ways 2026-08-24: clearing the tags through the demo's own Clear trigger gives the input placeholder=\"Add tag\", and forcing the attribute on the real input makes the token reach (rgb(1, 2, 3); its default is oklch(0.61 0 0) = --color-content-muted)",
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 4.321s on the three transitioned properties of the control)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))'
|
|
|
},
|
|
|
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)'
|
|
|
},
|
|
|
editable: {
|
|
|
// 2026-08-23. Only two reasons, both measured: the transition pair IS the
|
|
|
// transition the guard freezes, and the twelve tone keys are the palette
|
|
|
// cascade (adjudicated by PATTERN below).
|
|
|
'submit-fg':
|
|
|
'OSCILLATES: read ALIVE in 3 of 4 runs on the same HEAD (42/56) and dead in the fourth (41/56). Leaving the edit mode is what a blur does, and the guard opens by focus + prepareWith, so the surface it measures is not deterministic. Measured alive with the editor open and transitions frozen -> reaches. Without this entry the fourth run fails R-5.4 with a token that does not lie (2026-08-23)',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 4.321s on the three transitioned properties of the preview)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))',
|
|
|
// NOT the palette pattern: this one key is ALSO read straight by the invalid
|
|
|
// rules, so its silence has a second, different cause — the demo is valid.
|
|
|
'risk-border':
|
|
|
'the palette path is superseded like its eleven siblings, but this key is ALSO the invalid tint, read directly by three rules; forced data-invalid on the real root -> reaches on the input AND on the control (oklch(0.8059 0.1123 59.96) -> rgb(1, 2, 3))'
|
|
|
},
|
|
|
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)'
|
|
|
},
|
|
|
'chat-composer': {
|
|
|
// 2026-08-23. Four, and three of them are the guard's own two habits.
|
|
|
'focus-border':
|
|
|
'only paints under :focus-within, and the guard blurs the active element after opening; focused the REAL textarea with transitions frozen -> reaches (color(srgb 0.745 0.577 0.894 / 0.48) -> rgb(1, 2, 3) on the shell border)',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 4.321s on the shell AND on the send)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))',
|
|
|
// NOT the guard: a cross-recipe collision, and the composer is on the losing
|
|
|
// side of it BY DESIGN — its documented composition puts it inside FileUpload.
|
|
|
'disabled-opacity':
|
|
|
'file-upload sweeps DESCENDANTS: `[data-file-upload] [data-disabled]` is (0,2,0), the same weight as `[data-chat-composer-send][data-disabled]`, and its recipe is emitted LATER, so it wins over every disabled node inside a FileUpload — which is where the demo (and the documented composition) puts the composer; measured on the REAL nodes with the ancestor attr removed -> reaches (0.4 -> 0.123 on the send AND on the shell). Both defaults are --opacity-disabled, so the collision is invisible until a theme moves one'
|
|
|
},
|
|
|
'chat-message': {
|
|
|
// 2026-08-24. Four, and every one of them is a limit of the INSTRUMENT —
|
|
|
// measured on the real nodes, one by one.
|
|
|
'bubble-tail-radius':
|
|
|
'the guard snapshots exactly ONE corner (`borderTopLeftRadius`) and the tail is the BOTTOM one — `border-end-start-radius` incoming, `border-end-end-radius` outgoing. Measured on the real bubble across the 8 direction x run combinations: 4px -> 1234px on solo and last in BOTH directions (10px/10px/10px/4px -> 10px/10px/10px/1234px in, 10px/10px/4px/10px -> 10px/10px/1234px/10px out), while the corner the guard reads never moves. Its sibling `bubble-radius-run` reads alive because `middle` flattens the START corner, which IS in the read set',
|
|
|
'emphasize-duration':
|
|
|
'the guard freezes transitions and this token IS the transition (the jump-to-message flash is a two-state toggle + a transition, R-4.5 — there are no keyframes); measured unfrozen on the real row -> reaches (0.24s -> 4.321s)',
|
|
|
'focus-radius':
|
|
|
'the row is tabbable ONLY when composed into a Feed — soma merges `Feed.Article`s identity (tabindex, keyboard) into the provider props when a Feed ancestor exists — and its own demo mounts it standalone, so `:focus-visible` has no node there. Measured on /uix/components/chat-log, where the rows ARE feed articles (tabindex=0), with transitions frozen: rest 6px (the emphasize corner), focused 4px -> 1234px, both by programmatic focus and by a real Tab walk',
|
|
|
'quick-react-hover-scale':
|
|
|
'two limits at once: the guard does not snapshot `transform` (the same blind spot adjudicated for background / rating-group / card) and the cell is a PORTALED tapback button whose rule is `:hover, :focus-visible`. Measured with a real pointer on the open bar, transitions frozen -> reaches (matrix(1.18, 0, 0, 1.18, 0, 0) -> matrix(7.77, 0, 0, 7.77, 0, 0))'
|
|
|
},
|
|
|
'chat-log': {
|
|
|
// 2026-08-23. ONE key, and only because the guard blurs after opening: the
|
|
|
// event row is a tabbable feed article whose corner exists only under the
|
|
|
// focus ring (it is square at rest).
|
|
|
'event-radius':
|
|
|
'only paints under :focus-visible, and the guard blurs the active element after opening; keyboard-focused the REAL row with transitions frozen -> reaches (rest 0px, focused 4px -> 1234px)'
|
|
|
},
|
|
|
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: 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': {
|
|
|
'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)'
|
|
|
},
|
|
|
collapsible: {
|
|
|
// 2026-08-24. Its ONLY two silences, and both are the freeze the guard
|
|
|
// needs to read everything else. Measured unfrozen on the real trigger,
|
|
|
// which transitions `background` and `color` (two entries in the computed
|
|
|
// list, so the write shows up twice).
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen on [data-collapsible-trigger] -> reaches (0.18s, 0.18s -> 4.321s, 4.321s)',
|
|
|
'transition-ease':
|
|
|
'same freeze, same node -> reaches (cubic-bezier(0.4, 0, 0.2, 1) x2 -> steps(3) x2)'
|
|
|
},
|
|
|
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'
|
|
|
},
|
|
|
toolbar: {
|
|
|
// 2026-08-24. Its ONLY two silences, and both are the freeze the guard
|
|
|
// needs to read everything else. Measured unfrozen on the real Link, which
|
|
|
// transitions FOUR properties (background, border-color, color, box-shadow)
|
|
|
// — hence four entries in the computed list.
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen on [data-toolbar-link] -> reaches (0.12s x4 -> 4.321s x4)',
|
|
|
'transition-ease':
|
|
|
'same freeze, same node -> reaches (cubic-bezier(0.4, 0, 0.2, 1) x4 -> steps(3) x4)'
|
|
|
},
|
|
|
'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).
|
|
|
shadow:
|
|
|
PLANE_SUPERSEDED +
|
|
|
'; `shadow-topmost` DOES reach — its [data-topmost] rule is (0,2,0) and beats the plane (-> rgb(4,5,6))',
|
|
|
// 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.
|
|
|
},
|
|
|
'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)',
|
|
|
'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))'
|
|
|
},
|
|
|
select: {
|
|
|
// 2026-08-24. Ten reasons, every one measured on the REAL nodes with
|
|
|
// transitions frozen (the 24 tone forwarders are the palette cascade and
|
|
|
// go by PATTERN below). Its panel is PORTALED, so the guard has to keep it
|
|
|
// OPEN for the ~30 tokens that live inside it — and the open state is what
|
|
|
// buries the two rest-state keys of the trigger.
|
|
|
'trigger-border':
|
|
|
'the guard must keep the panel OPEN (30 of its tokens live in it) and [data-select-trigger][data-state=open] re-points border-color to the palette accent at (0,2,0); measured with the panel CLOSED -> reaches (oklch(0.8853 0 0) -> rgb(1, 2, 3))',
|
|
|
'trigger-indicator-fg':
|
|
|
'same open-state shadowing, on the chevron: [data-state=open] [data-select-indicator] re-points its colour; measured with the panel CLOSED -> reaches (oklch(0.5032 0 0) -> rgb(1, 2, 3))',
|
|
|
'placeholder-fg':
|
|
|
'the demo boots WITH a value, so no node carries [data-placeholder]; forced on the real value span -> reaches (oklch(0.2435 0 0) -> rgb(1, 2, 3))',
|
|
|
'disabled-border':
|
|
|
'the demo mounts ONE select and switching `disabled` on closes the panel that carries 30 other tokens; forced [data-disabled] on the real trigger -> reaches (oklch(0.8853 0 0) -> rgb(1, 2, 3))',
|
|
|
'disabled-bg':
|
|
|
'same single instance; forced [data-disabled] on the real trigger -> reaches (oklch(0.9911 0 0) -> rgb(1, 2, 3))',
|
|
|
'invalid-border':
|
|
|
'minted 2026-08-24 (it was reading --color-risk-border raw); the demo boots VALID. Forced [data-invalid] on the real trigger -> reaches on BOTH halves: border-color oklch(0.8853 0 0) -> rgb(1, 2, 3), and with [data-state=open] the ring too (oklch(0.8059 0.1123 59.96) 0 0 0 1px -> rgb(1, 2, 3) 0 0 0 1px)',
|
|
|
'close-duration':
|
|
|
'it feeds --motion-duration-exit, a CUSTOM property the guard does not snapshot, and it only reaches animation-duration while the panel is LEAVING — measured while open, animation-duration is the ENTER pair (0.18s). Read on the real panel: --motion-duration-exit 120ms -> 11.5s',
|
|
|
'separator-size':
|
|
|
'ALIVE but crushed: the separator sits in the ScrollArea flex column with the default flex-shrink 1, and the panel overflows (scrollHeight 670 vs clientHeight 310), so its 1px block-size computes 0px and no value of the token changes it. With flex-shrink 0 on the same node the token follows (1px -> 9px). The separator therefore paints NOTHING in an overflowing panel — a pixel-moving defect registered in next-features §13, not a dead token',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen on the real trigger -> reaches (0.12s -> 11.5s on all four properties)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))'
|
|
|
},
|
|
|
calendar: {
|
|
|
// 2026-08-24. This entry is the FAMILY's vocabulary (calendar-surface.css),
|
|
|
// so the guard measures it across calendar + month-grid + range-calendar;
|
|
|
// with those three routes 68 of its 74 keys move. These six are what the
|
|
|
// family cannot show, each measured on the real node.
|
|
|
'invalid-border':
|
|
|
'no calendar surface of the three routes boots invalid; forced [data-invalid] on the real host -> reaches (oklch(0.8059 0.1123 59.96) -> rgb(1, 2, 3))',
|
|
|
'select-min-width':
|
|
|
'its ONLY consumer is [data-range-calendar-month-select] / -year-select, and NO demo in the repo mounts those parts (0 nodes on the range-calendar route, which renders a text heading instead; DateRangePicker imports the CALENDAR selectors, so its nodes carry data-calendar-month-select). A part no page mounts has no node to paint — demo gap, registered in next-features §13',
|
|
|
'range-start-solid-hover':
|
|
|
'the hover pass stops at the first 30 nodes and the range endpoints sit past them on the range-calendar route; hovered the real [data-range-start] day -> reaches (oklch(0.6193 0.1094 181.3) -> rgb(1, 2, 3))',
|
|
|
'range-end-solid-hover':
|
|
|
'same limit on the other endpoint; hovered the real [data-range-end] day -> reaches (oklch(0.5246 0.1755 305.39) -> rgb(1, 2, 3))',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen on a real day cell -> reaches (0.12s -> 11.5s on all five properties)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))'
|
|
|
},
|
|
|
'radio-group': {
|
|
|
// 2026-08-23. The guard already sweeps its four variants and both
|
|
|
// orientations and measures the disabled / invalid stages through the
|
|
|
// demo's URL switches; these are what is left, each measured on the REAL
|
|
|
// nodes. Three of them are DEFECTS, not instrument limits, and say so.
|
|
|
//
|
|
|
// (1) The dot's whole scale — a value channel written INLINE.
|
|
|
'dot-size':
|
|
|
'DEAD BY INLINE STYLE, not by the demo: <SvgDot> writes style="width: 1em; height: 1em" on the glyph, and an inline declaration beats any stylesheet rule. Measured 2026-08-23: the dot computes 13.3281px (1em of the button UA font) at EVERY step while the token says 5/6/7/9/11px, the sentinel value changes nothing, and removing the inline style makes the same token paint 1234px. The recipe scale has never painted — a pixel-moving fix (the `!important` shape recipe-contract §3 allows for exactly this, bypassing an inline style) that needs a signature; registered in next-features §13',
|
|
|
'dot-size-xs': 'same inline style; the step reads 5px and the dot paints 13.3281px',
|
|
|
'dot-size-sm': 'same inline style; the step reads 6px and the dot paints 13.3281px',
|
|
|
'dot-size-md': 'same inline style; the step reads 7px and the dot paints 13.3281px',
|
|
|
'dot-size-lg': 'same inline style; the step reads 9px and the dot paints 13.3281px',
|
|
|
'dot-size-xl': 'same inline style; the step reads 11px and the dot paints 13.3281px',
|
|
|
// (2) A rule whose selector no runtime ever matches.
|
|
|
'disabled-item-label-fg':
|
|
|
'DEAD SELECTOR: it is consumed by [data-radio-group-label][data-disabled], and soma never stamps data-disabled on the Label part (the morfo declares `data: []` for it). Measured 2026-08-23 with the group disabled through ?perm.disabled=true: the label carries [id, data-radio-group-label, data-archetype] and nothing else. Forced on the label by hand -> reaches (oklch(0.2435 0 0) -> rgb(1, 2, 3)), so the token is sound and the ATTRIBUTE is missing — a morfo decision (checkbox\'s twin label does carry its own), registered in next-features §13',
|
|
|
// (3) A declaration the variant rules out-rank in every variant.
|
|
|
'checked-disabled-indicator-bg':
|
|
|
'SUPERSEDED by the variant rules: [data-radio-group][data-variant=X] [data-radio-group-item][data-state=checked] is (0,4,0) and [data-radio-group-item][data-disabled][data-state=checked] only (0,3,0). Measured 2026-08-23 on a disabled+checked item with the sentinel value written: solid oklch(0.5556 0.1829 305.86) (the full accent), outline and segmented rgba(0,0,0,0), ghost the 18% mix — never rgb(1,2,3). And removing data-variant does not help: the `:not([data-variant])` twin is (0,4,0) too. So a disabled+checked radio shows the FULL accent today; raising the specificity moves pixels -> signature, registered in next-features §13',
|
|
|
// Instrument limits, measured.
|
|
|
'gap-horizontal':
|
|
|
'the row gap is a PAIR and only the half in force paints: the demo boots `vertical`, and the guard sweeps ONE axis (data-variant, where twelve of its keys live). Switched the real demo to horizontal -> reaches (column-gap 16px -> 1234px)',
|
|
|
'invalid-ring':
|
|
|
'needs [data-invalid] AND :focus-visible, and the guard blurs on purpose; on a button :focus-visible only latches with the keyboard modality, so measured 2026-08-23 with a Tab followed by focus() on the real item -> reaches (the 4px ring oklch(0.9555 0.0207 13.86) -> rgb(1, 2, 3))',
|
|
|
'invalid-ring-gap': 'same double ring; measured the same way -> reaches (the 2px gap oklch(0.9911 0 0) -> rgb(4, 5, 6))',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen on the real item -> reaches (0.12s -> 11.5s on all four transitioned properties)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))',
|
|
|
'segmented-transition-duration':
|
|
|
'same freeze, on the segmented pair; measured unfrozen with the real variant on -> reaches (segment 0.18s -> 11.5s, and the pill\'s --indicator-duration 180ms -> 11.5s)',
|
|
|
'segmented-transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.32, 0.72, 0, 1) -> steps(3))',
|
|
|
'segmented-pill-shadow':
|
|
|
'the sliding pill is a node the WRAPPER only renders under variant="segmented" (like Switch\'s default Thumb), so stamping data-variant while measuring cannot create it; measured with the demo\'s real segmented chip on -> reaches (rgba(15, 23, 42, 0.08) 0 1px 2px -> rgb(1, 2, 3) 0 0 0 7px)'
|
|
|
// `hover-segmented-segment-fg` had an entry here for one run and the guard
|
|
|
// reported it STALE on the next: it needs hover AND the segmented variant,
|
|
|
// which the hover pass does not sweep — yet the pointer's resting place is
|
|
|
// enough for the static pass to catch it. Verified alive twice before the
|
|
|
// entry was pruned (hovered segment: oklch(0.2435 0 0) -> rgb(1, 2, 3)).
|
|
|
},
|
|
|
'rating-group': {
|
|
|
// 2026-08-23. Six, each measured on the real nodes.
|
|
|
'partial-item-fg':
|
|
|
'only under [data-state=partial], which needs allowHalf AND a half value; forced on the real fifth item -> reaches (oklch(0.6406 0.1329 157.68) -> rgb(1, 2, 3))',
|
|
|
'invalid-item-fg':
|
|
|
'minted 2026-08-23 (the rule was reading --color-threat-element raw); the demo boots VALID, and switching `invalid` on would make its (0,3,0) rule out-rank item-fg / active-item-fg / partial-item-fg for the whole run. Forced [data-invalid] on the real root -> reaches (oklch(0.9555 0.0207 13.86) -> rgb(1, 2, 3))',
|
|
|
'item-hover-offset':
|
|
|
'the guard does not snapshot `transform`, and this is the only knob of the recipe that moves one (the 1px lift on hover); hovered the real item and read transform -> reaches (matrix(1, 0, 0, 1, 0, -1) -> matrix(1, 0, 0, 1, 0, -40)). Instrument gap, registered in next-features §13',
|
|
|
'focus-radius':
|
|
|
'the corner only rounds under :focus-visible on the provider and the guard blurs on purpose; focused from the keyboard -> reaches (6px -> 1234px)',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen on a real item -> reaches (0.12s -> 11.5s on colour and transform)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))'
|
|
|
},
|
|
|
toggle: {
|
|
|
// 2026-08-23. Eight, each forced on the REAL stage toggle. The forty-eight
|
|
|
// tone keys are the palette cascade, adjudicated BY PATTERN below; the
|
|
|
// twelve hover keys and the thirty variant keys stopped being dead when the
|
|
|
// guard learnt to sweep SEVERAL axes (variant x state x invalid) in both
|
|
|
// passes — they are not adjudicated, they are measured.
|
|
|
'radius-sm':
|
|
|
'only the step in force paints (the demo has no `rounded` control at all, so the resting corner is the only one mounted); forced data-rounded=sm -> reaches (4px -> 1234px)',
|
|
|
'radius-md': 'same scale; forced data-rounded=md -> reaches (6px -> 1234px)',
|
|
|
'radius-lg': 'same scale; forced data-rounded=lg -> reaches (10px -> 1234px)',
|
|
|
'radius-xl': 'same scale; forced data-rounded=xl -> reaches (16px -> 1234px)',
|
|
|
'radius-full': 'same scale; forced data-rounded=full -> reaches (9999px -> 1234px)',
|
|
|
'disabled-opacity':
|
|
|
'the demo boots enabled, and switching `disabled` on for the whole run would drop pointer-events and revert the hover pair, killing six other keys; forced data-disabled on the real toggle -> reaches (opacity 0.4 -> 0.123)',
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s on all four transitioned properties)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(4))'
|
|
|
},
|
|
|
card: {
|
|
|
// 2026-08-23. Nine, each forced on the REAL stage card (which already boots
|
|
|
// with data-interactive + data-lift), transitions frozen before reading.
|
|
|
// The forty tone keys are the palette cascade, adjudicated BY PATTERN below.
|
|
|
'radius-sm':
|
|
|
'only the step in force paints (the demo boots rounded=md); forced data-rounded=sm -> reaches (6px -> 1234px)',
|
|
|
'radius-lg': 'same scale; forced data-rounded=lg -> reaches (16px -> 1234px)',
|
|
|
// The three emerge keys share ONE cause, and it is not the demo being shy:
|
|
|
// the mount rule is `:not([data-animation-style])` on purpose (the motion
|
|
|
// system owns the single `animation` property when the card opts in), and
|
|
|
// the stage boots with motion=select-pop.
|
|
|
'emerge-duration':
|
|
|
'the mount rule yields to the motion system — [data-card]:not([data-animation-style]) — and the stage boots with data-animation-style=select-pop, so no node runs the emerge. Removed the attribute on the real card -> reaches (animation-duration 0.32s -> 11.5s on both keyframes)',
|
|
|
'emerge-ease':
|
|
|
'same yield; same removal -> reaches (cubic-bezier(0, 0, 0.2, 1) -> steps(4))',
|
|
|
'emerge-distance':
|
|
|
'same yield, PLUS a second cause the other two do not have: it feeds the channel hook --motion-distance-md, and the mount ends on translate 0 with fill-mode both, so `transform` at rest can never show it. Read the hook itself on the real card -> reaches (8px -> 1234px)',
|
|
|
// The guard does not snapshot `transform` (same instrument gap as
|
|
|
// rating-group.item-hover-offset, next-features §13).
|
|
|
'hover-lift':
|
|
|
'the guard does not snapshot `transform`; hovered the real card and read it -> reaches (matrix(1, 0, 0, 1, 0, -2) -> matrix(1, 0, 0, 1, 0, 1234)). Instrument gap, registered in next-features §13',
|
|
|
'press-scale':
|
|
|
'same `transform` gap AND :active, which no script can force; read the live rule instead — [data-card][data-interactive]:active…:not(:has(button:active, …)) { transform: scale(var(--card-press-scale)) } is in the sheet and consumes the token by name',
|
|
|
'press-duration':
|
|
|
'same :active rule, which also sets transition-duration: var(--card-press-duration); read in the live sheet next to press-scale',
|
|
|
'disabled-opacity':
|
|
|
'the card dims through `filter: opacity()` (not `opacity` — its own mount animation would outrank a plain one, the comment in card.css records the measurement), and the demo boots enabled; forced data-disabled on the real card -> reaches (filter opacity(0.4) -> opacity(0.123))'
|
|
|
},
|
|
|
'image-adjustments': {
|
|
|
// 2026-08-24. One, measured on the REAL stage reset, transitions frozen.
|
|
|
// Its sibling `hover-reset-fg` looked like the same class and was NOT: the
|
|
|
// reset boots disabled (nothing adjusted yet) so `:hover:not([disabled])`
|
|
|
// could not match, and the `disabled` sweep in COMPONENT_OVERRIDES makes it
|
|
|
// read live. Only the focus corner has no mechanical route.
|
|
|
'focus-reset-radius':
|
|
|
'the corner only rounds under :focus-visible and the guard blurs the active element on purpose (the F2-A fix for click-focus poisoning); focused the real reset from script and confirmed :focus-visible matched -> reaches (4px -> 1234px). Same cause and same shape as rating-group.focus-radius'
|
|
|
},
|
|
|
toast: {
|
|
|
// 2026-08-24. Thirty-four, and the biggest group is §12.9 with a face
|
|
|
// nobody had seen yet: on a PORTALED card the overlay plane does not only
|
|
|
// take the typography, it takes the whole CARD. Measured on the real item
|
|
|
// with transitions frozen (`scripts/__dbg-toast-plane.mjs`): writing the
|
|
|
// component's surface / border / accent / border-width / shadow moves
|
|
|
// NOTHING (bg stays oklch(0.931 0 0), border oklch(0.8853 0 0) 1px), while
|
|
|
// --depth-overlay-surface / -border / -shadow / --border-width on the same
|
|
|
// root repaint every one of them (rgb(4, 5, 6), 999px, the 9px ring). The
|
|
|
// tone keys ride the PATTERN entries below.
|
|
|
'transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s on the two transitioned properties of the card)',
|
|
|
'transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))',
|
|
|
'closed-offset':
|
|
|
'TWO reasons: the rule is `[data-toast-item][data-state=closed]` and a card only wears that state on its way OUT (the guard measures a card it has just fired), and it paints through `transform`, which the guard does not snapshot (the blind spot already adjudicated on background / rating-group / card / fab). Measured 2026-08-24 forcing data-state=closed on the real card with transitions frozen -> reaches (matrix(0.98, 0, 0, 0.98, 0, 10) -> matrix(0.123, 0, 0, 0.123, 0, 1234))',
|
|
|
'closed-scale': 'same rule, same property, same measurement -> reaches (0.98 -> 0.123)',
|
|
|
'action-focus-ring-offset':
|
|
|
'the ring only paints under `[data-toast-action]:focus-visible` and the guard blurs the active element on purpose; walked the focus onto the real Undo button with the keyboard, confirmed :focus-visible matched -> reaches (the inner spread goes 1px -> 999px)',
|
|
|
'action-focus-ring-width':
|
|
|
'same focus ring; measured focused -> reaches (the outer spread follows offset + width)',
|
|
|
'action-focus-ring-color':
|
|
|
'same focus ring; measured focused -> reaches (color(srgb 0.745076 …) -> rgb(1, 2, 3))',
|
|
|
'provider-max-width':
|
|
|
'the card reads `inline-size: min(100%, var(--toast-provider-max-width))`, so the guard`s 1234px is CAPPED by the other half of the min() and nothing moves — the known blind spot of a sentinel value that is too big. Measured 2026-08-24 from the other side, which is the only side that can move it: 120px -> the card narrows from 420px to 280px (its min-inline-size floor), so the token reaches'
|
|
|
},
|
|
|
tabs: {
|
|
|
// 2026-08-24. Nine, and NOT ONE of them is a token that lies: four are the
|
|
|
// transition the guard freezes, three the focus ring the guard blurs, one
|
|
|
// the composition the demo happens to mount, one a property the guard does
|
|
|
// not read. All nine measured by hand on the real nodes of the demo. The
|
|
|
// variant sweep + the two URL surfaces in COMPONENT_OVERRIDES took the run
|
|
|
// from 40/79 to 70/79 first, so what is left here is the residue, not the
|
|
|
// bulk.
|
|
|
'trigger-bg':
|
|
|
"the demo mounts `<Tabs.Indicator>`, and `[data-tabs-list]:has([data-tabs-indicator]) [data-tabs-trigger] { background: transparent }` (0,2,0) out-ranks the trigger's own `[data-tabs-trigger]` (0,1,0) — by design, the label has to read above the sliding pill. Measured 2026-08-24 with transitions frozen: with the indicator mounted the write moves nothing (rgba(0, 0, 0, 0)); switch the demo's `render Indicator part` OFF — the DEFAULT composition, the indicator is opt-in — and the same write reaches (rgba(0, 0, 0, 0) -> rgb(1, 2, 3)). Turning it off for the whole run only trades this key for `indicator-line-thickness`",
|
|
|
'trigger-transition-duration':
|
|
|
'the guard freezes transitions and this token IS the transition; measured unfrozen -> reaches (0.12s -> 11.5s on all four transitioned properties of the trigger)',
|
|
|
'trigger-transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3) on all four)',
|
|
|
'indicator-transition-duration':
|
|
|
'same freeze, on the sliding indicator; measured unfrozen -> reaches (0.18s -> 11.5s on translate / inline-size / block-size; the fourth slot is the opacity fade, which is system)',
|
|
|
'indicator-transition-ease':
|
|
|
'same freeze; measured unfrozen -> reaches (cubic-bezier(0.32, 0.72, 0, 1) -> steps(3) on the same three)',
|
|
|
'trigger-focus-ring-offset':
|
|
|
'the ring only paints under :focus-visible and the guard blurs the active element on purpose (the F2-A fix for click-focus poisoning); walked the focus onto a real trigger with the keyboard, confirmed :focus-visible matched (1 node) -> reaches (1px -> 1234px)',
|
|
|
'trigger-focus-ring-width': 'same focus ring; measured focused -> reaches (2px -> 1234px)',
|
|
|
'trigger-focus-ring-color':
|
|
|
'same focus ring; measured focused -> reaches (color(srgb 0.745076 0.576585 0.894185 / 0.48) -> rgb(1, 2, 3))',
|
|
|
'scrollable-fade':
|
|
|
'TWO reasons, and either alone would be enough: the edge fade only exists under `[data-scrollable]`, an opt-in switch the stage boots OFF, and it paints through `mask-image`, which is not in the guard read set. Measured 2026-08-24 with the demo switch on and transitions frozen -> reaches (the mask reads `linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)` and the write moves both stops to 1234px)'
|
|
|
}
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
* 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: 'radio-group',
|
|
|
pattern: /^(primary|secondary|neutral|affirm)-(solid|solid-hover)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — the same four tones x two slots as its twin checkbox, and PRIMARY falls with them: the eidos wrapper always resolves a colour and stamps data-color, so the generic block matches on every instance and the fallback that names the component key never applies. Measured 2026-08-23 on an affirm instance: --radio-group-affirm-solid moves nothing (oklch(0.6491 0.1136 181.96) stays), --palette-solid on the same node repaints (rgb(4, 5, 6))'
|
|
|
},
|
|
|
{
|
|
|
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: 'editable',
|
|
|
pattern: /^(primary|secondary|neutral|affirm|risk|threat)-(solid|border)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — measured here 2026-08-23 with the mechanism in the open: the node carries --palette-solid (oklch(0.5556 …)) from the shared per-instance layer, the generic block reads it, and the fallback that names the component key never applies; strip data-color from the same instance and --editable-primary-solid paints again (rgb(1, 2, 3)). soma always stamps data-color, so no instance escapes'
|
|
|
},
|
|
|
{
|
|
|
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))'
|
|
|
},
|
|
|
{
|
|
|
component: 'toggle',
|
|
|
pattern:
|
|
|
/^(primary|secondary|neutral|affirm|risk|threat)-(track|element|hover|border|solid|solid-hover|text|contrast)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — the six tones toggle forwards x eight slots, 48 of its 126 keys (it excludes fulfill/loss by doctrine: a toggle is neither completion nor irreversible loss). Measured 2026-08-23 on the real pressed toggle with transitions frozen: with data-color=primary, --toggle-primary-solid moves NOTHING (the fill stays oklch(0.5556 0.1829 305.86)) while --palette-solid on the same node repaints it (rgb(1, 2, 3)); strip data-color and --toggle-neutral-solid paints again (oklch(0.6434 0 0) -> rgb(1, 2, 3)). The neutral eight fall with the rest for a second reason worth writing down: the guard STAMPS data-color=neutral to test them, which is exactly what turns the generic block on'
|
|
|
},
|
|
|
{
|
|
|
component: 'card',
|
|
|
pattern:
|
|
|
/^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|border|solid|text|contrast)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — the eight tones x five slots, 40 of its 98 keys. Measured 2026-08-23 on the real stage card with transitions frozen: with data-color=primary, --card-primary-track moves NOTHING (the surface stays oklch(0.9932 0.0034 325.6)) while --palette-track on the same node repaints it (rgb(1, 2, 3)); strip data-color and --card-neutral-track paints again (oklch(0.9911 0 0) -> rgb(1, 2, 3)). The wrapper stamps data-color on every instance, so the named fallback never applies'
|
|
|
},
|
|
|
{
|
|
|
component: 'tags-input',
|
|
|
pattern: /^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|border|text)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — the eight tones x three slots, 24 of its 91 keys. Measured 2026-08-24 on the real tag forced into data-state=active with transitions frozen: with data-color=primary (the wrapper resolves `color` and stamps it on EVERY instance, default included) --tags-input-primary-border moves NOTHING (the tag border stays oklch(0.8043 0.0918 309.69)) while --palette-border on the same node repaints it (rgb(4, 5, 6)); strip data-color and --tags-input-primary-border paints again (rgb(1, 2, 3)). The generic `[data-tags-input][data-color], [data-tags-input][data-color-custom]` block is emitted LAST at the same (0,2,0) as the eight per-tone blocks. The three `*-text` slots carry a SECOND cause on top: even unsuperseded they cannot tint the item, because the archetype ink beats them (see the item-fg entry above)'
|
|
|
},
|
|
|
{
|
|
|
component: 'toast',
|
|
|
pattern: /^(default|affirm|fulfill|risk|threat|loss)-(surface|border)$/,
|
|
|
reason:
|
|
|
PLANE_SUPERSEDED +
|
|
|
' — the six intents x two slots, 12 of its 85 keys, and this is §12.9 on a PORTALED card: the item carries data-depth=overlay and the plane declares `background` and the `border` shorthand at the same (0,1,0) and later, so the whole per-intent CARD is painted by the plane. Measured 2026-08-24 on the real item with transitions frozen: --toast-risk-surface / -border move nothing (bg stays oklch(0.931 0 0)) while --depth-overlay-surface / -border on the same root repaint it (rgb(4, 5, 6)). Visible consequence, registered in next-features §12.9 and NOT fixed from this axis: a risk toast and a fulfill toast wear the same grey card; only the status disc and the action ink carry the intent'
|
|
|
},
|
|
|
{
|
|
|
component: 'toast',
|
|
|
pattern: /^(default|affirm|fulfill|risk|threat|loss)-accent$/,
|
|
|
reason:
|
|
|
'SUPERSEDED by the depth plane AT REST (the accent is a `border-inline-start` longhand and the plane`s later `border` shorthand overwrites all four sides — §12.9, same measurement as the surface/border pattern), but NOT dead: under `[data-toast-item][data-loading]` the pulse rule is (0,2,0) and its @keyframes outranks every normal declaration. Measured 2026-08-24 over 100 frames of the real pulse: without the write the interpolated border-inline-start-color runs oklab L 0.5032..0.8514; with --toast-default-accent=rgb(1, 2, 3) it runs 0.0823..0.5032 — the token reaches, in the one state where the plane cannot reach it'
|
|
|
},
|
|
|
{
|
|
|
component: 'toast',
|
|
|
pattern: /^(affirm|fulfill|risk|threat|loss)-action-hover-bg$/,
|
|
|
reason:
|
|
|
'the tone is what the guard cannot combine with the hover: its static pass stamps data-intent to test a tone key, and its hover pass does not — so only `default-action-hover-bg` (the untinted card, which needs no stamp) reads alive. Measured 2026-08-24 with the demo`s own `risk` chip and a real pointer on the Undo button, transitions frozen -> reaches (oklch(0.9923 0.0025 48.72) -> rgb(1, 2, 3))'
|
|
|
},
|
|
|
{
|
|
|
component: 'select',
|
|
|
pattern: /^(primary|secondary|neutral|affirm|fulfill|risk|threat|loss)-(track|border|text)$/,
|
|
|
reason:
|
|
|
PALETTE_SUPERSEDED +
|
|
|
' — the eight tones x three slots, 24 of its 103 keys, and here the cascade is the TSC v2.2 MULTI-PART one (parts trigger + content, because the panel portals out): the last block emitted is `[data-select-trigger][data-color], [data-select-content][data-color], …` reading `var(--palette-track, var(--select-primary-track))`. Measured 2026-08-24 on the open trigger, which carries data-color=primary: --select-primary-border moves NOTHING (border stays oklch(0.8043 0.0918 309.69)) while --palette-border on the same node repaints it (rgb(4, 5, 6)). soma always stamps data-color, so the named fallback never applies on any instance'
|
|
|
}
|
|
|
];
|