|
|
---
|
|
|
title: Next features — initiative registry
|
|
|
type: registry
|
|
|
audience: human + agent
|
|
|
authority: backlog of user-decided future initiatives — records scope, sequencing and dependencies; it does NOT record verdicts (those live in the RFCs / changelog / book-deviations when executed)
|
|
|
status: living — items enter by explicit user decision during audits and sessions; remove an item when it ships (and link where it landed)
|
|
|
created: 2026-07-06
|
|
|
---
|
|
|
|
|
|
# Next features — initiative registry
|
|
|
|
|
|
Rules of this file: one entry per initiative, dated, with its origin (which
|
|
|
audit/session raised it), its scope in one paragraph, and its dependencies.
|
|
|
Entries are **registered, not started** — starting one is a session decision.
|
|
|
Detail lives in the linked doc, never copied here.
|
|
|
|
|
|
## 1. Tonal-ramp contrast parity (measure, then construct) — 2026-07-06 · Stage 1 DONE 2026-07-19 · Stage 2 CLOSED 2026-07-20
|
|
|
|
|
|
> **Stage 1 SHIPPED (2026-07-19).** Measured with `scripts/contrast-audit.ts`
|
|
|
> (33 scales × 2 modes); drift + user verdicts ratified as doctrine in
|
|
|
> [`theming/reference.md §40`](./theming/reference.md) + [`changelog.md §44`](./theming/changelog.md).
|
|
|
> Outcome: `text·11` = secondary tier, `text-strong` = AA-guaranteed; the border
|
|
|
> vocabulary is Radix-subtle by design (only `solid·9` clears 3:1 by
|
|
|
> construction) with a decorative-vs-load-bearing split; the focus ring is a
|
|
|
> config axis (`primitives.focusRing` + `color.focus`, single `outline` per §32)
|
|
|
> whose shipped default is soft — hardening is a config-value choice, kept as-is.
|
|
|
> **Stage 2 CLOSED (2026-07-20) — no solver needed.** The 6 decisions were
|
|
|
> locked (D1 band · D2 measured-pass · D3 flip-only · D4 opt-in · D5 runtime-first
|
|
|
> · D6 now), then **execution disproved the premise**: the template morph does not
|
|
|
> _compute_ text contrast, it **inherits** it (steps 11/12 copy the donor's L-curve
|
|
|
> verbatim; L-driven contrast is ~chroma-invariant under gamut-mapping), so any
|
|
|
> scale generated from a §40-compliant donor library clears the ratified text
|
|
|
> floors **by construction**. Verified on three banks — authored base, leave-one-out
|
|
|
> regeneration, and 45 out-of-distribution seeds — **0 hard-gate failures**
|
|
|
> (min WCAG 9.7:1). The luminance solver would have had nothing to resolve for
|
|
|
> realistic inputs → dropped as speculative. **Delivered instead:** the ratified
|
|
|
> pair table as shared data (`$color` → `CONTRAST_PAIRS`), the audit refactored to
|
|
|
> consume it + a morph-generated regression bank (`scripts/contrast-audit.ts`), and
|
|
|
> a CI guard that locks the inheritance (`eidos/lib/contrast-invariant.test.ts` —
|
|
|
> fails if a future donor/generator breaks it). With D2, the base stays verbatim
|
|
|
> ground-truth; **no base→seeds migration**.
|
|
|
> **Plan + decision + outcome record:**
|
|
|
> [`process/contrast-stage2-plan-2026-07.md`](./process/contrast-stage2-plan-2026-07.md);
|
|
|
> chronicle [`changelog.md §45`](./theming/changelog.md).
|
|
|
|
|
|
**Origin**: A.7 follow-up (theming audit F). The on-solid pair is now computed
|
|
|
at generation with a single criterion ([`rfcs/rfc-color-engine.md`](./rfcs/rfc-color-engine.md)
|
|
|
§8, `eidos/lib/on-solid.ts`). M3 still guarantees more: contrast **by
|
|
|
construction across its whole tonal ramp** (container/on-container pairs at
|
|
|
fixed tone deltas — ΔT 40 ≈ 3:1, ΔT 50 ≈ 4.5:1). Our equivalent promises
|
|
|
(text-11 on bg-2/element-3, border-7 vs bg-2 at the 3:1 non-text floor, text
|
|
|
over composited soft surfaces a2/a3) rest on Radix-style curation, unmeasured.
|
|
|
|
|
|
**Stage 1 — pair-contract verification** (the A.7 pattern generalized): a
|
|
|
canonical table of slot pairs + floors (a user-decided doctrine session),
|
|
|
a measurement pass over pairs × 33 scales × 2 modes at generation
|
|
|
(`on-solid.ts` generalizes; the compositing math exists in `$color`), and the
|
|
|
resulting drift list goes to user verdicts (retune vs annotated exception).
|
|
|
Infrastructure is small; the fallout is the unknown.
|
|
|
|
|
|
**Stage 2 — by-construction generator (CLOSED 2026-07-20, solver dropped).** The
|
|
|
plan proposed a luminance solver that resolves each step to satisfy Stage 1's
|
|
|
pair table for a seed. Execution disproved its premise: the morph **inherits**
|
|
|
text contrast from the donor (steps 11/12 copy the donor's L-curve verbatim;
|
|
|
L-driven contrast is ~chroma-invariant under gamut-mapping), so every scale from
|
|
|
a §40-compliant donor library clears the ratified floors by construction. Measured
|
|
|
across three banks (authored base, leave-one-out regeneration, 45 out-of-distribution
|
|
|
seeds): **0 hard-gate failures, min WCAG 9.7:1.** The solver had nothing to
|
|
|
resolve for realistic inputs → dropped (speculative). What shipped: the pair
|
|
|
table as shared data (`$color` `CONTRAST_PAIRS`), the audit consuming it + a
|
|
|
morph-generated regression bank, and a CI guard (`eidos/lib/contrast-invariant.test.ts`)
|
|
|
that locks the inheritance. The BASE stays verbatim ground-truth (D2); no
|
|
|
base→seeds migration.
|
|
|
|
|
|
**Sequence**: Stage 1 first (it is the spec for Stage 2). No M3-style `on-*`
|
|
|
vocabulary expansion — our slots already encode the pairs; the table
|
|
|
formalizes them without inflating the namespace.
|
|
|
|
|
|
## 2. Per-theme palette-contrast cascade emission — 2026-07-06
|
|
|
|
|
|
**Origin**: A.7 execution. The per-instance `palette-contrast` cascade lives
|
|
|
in the STATIC foundation — one polarity for all themes. Today polarity is
|
|
|
computed per configured theme and unanimous (33/33 scales agree across
|
|
|
base-light/base-dark); on disagreement the `*-light` theme wins
|
|
|
(`computeLightSolidScales`, documented in
|
|
|
[`rfcs/rfc-color-engine.md`](./rfcs/rfc-color-engine.md) §8). **Trigger**: the
|
|
|
day a real configured theme disagrees on a scale's polarity, emit the cascade
|
|
|
per-theme instead of statically. Not before — no speculative machinery.
|
|
|
|
|
|
## 3. Component audit (post-system) — 2026-07-06
|
|
|
|
|
|
**Origin**: user decision during the theming audit — "de momento no tocamos
|
|
|
los componentes, merecen una auditoría adicional una vez que se haya resuelto
|
|
|
estas". Gate: the system-level audit (F/G) closes first. Registered items so
|
|
|
far, to seed its scope:
|
|
|
|
|
|
- **Named-styles adoption by controls** (from A.3): controls/recipes that
|
|
|
hand-declare typography instead of consuming `--style-*` /
|
|
|
size-bundle typography — likely by error, to be confirmed per component.
|
|
|
- **Letter-spacing bundle adoption**: ~34 recipes consume `font-size` /
|
|
|
`line-height` from the size bundle but not `letter-spacing`; plus ~7 raw
|
|
|
letter-spacing literals in component CSS.
|
|
|
- **Tier-A `data-depth` stamping coverage**: overlays that should stamp their
|
|
|
plane (dialog / drawer / tooltip / menubar / combobox) per
|
|
|
[`rfcs/rfc-depth.md`](./rfcs/rfc-depth.md) §5.
|
|
|
- **Calendar family → shared layer/archetype** (user design verdict,
|
|
|
2026-07-07): the calendar-grid vocabulary (accent set, day geometry, the
|
|
|
holiday/event marks fixed in audit B.2) becomes a FIRST-CLASS shared layer
|
|
|
— today the family borrows it informally (measured: range-calendar
|
|
|
consumes 79 `--calendar-*` tokens, month-grid 65, year-grid 65,
|
|
|
date-range-picker 38, date-picker 4 — ~250 cross-component borrows), and
|
|
|
**chronos will draw from it too** when its track lands. Materialization to
|
|
|
decide at execution: a morfo part-archetype for the day cell (the 26-set
|
|
|
grows) and/or a `calendar-surface` shared recipe à la list-surface
|
|
|
(`--list-item-*` precedent). This supersedes "per-component composition
|
|
|
repairs" for the B.2 borrowers: the marks reach the whole family through
|
|
|
the shared layer instead.
|
|
|
- **Field family composition — segments under `[data-field]`** (user design
|
|
|
verdict, 2026-07-07, audit B.1): segment height derives from the SIZE axis
|
|
|
at the FIELD family level — `field` recipe declares `segment-height` with
|
|
|
size-scoped declarations (`calc(var(--field-control-height-{k}) -
|
|
|
var(--space-2))`; the TSC `size:` scope axis exists, unused so far), and
|
|
|
the per-component copies die (each x-field today re-declares the whole
|
|
|
`height-{xs..xl}` family the field recipe already has). **MANDATED by the
|
|
|
B.1-(i) verdict**: date/time/color-field MUST incorporate the Field
|
|
|
wrapper (segments under `[data-field][data-size]` — probed live: today
|
|
|
they don't) as part of the component audit; the three broken
|
|
|
per-component `segment-height` tokens and their consumers were removed
|
|
|
(they had computed `auto` since birth — zero visual change).
|
|
|
- Whatever F/G registers as "component-level adoption gap" in
|
|
|
`docs/audit/theming-audit.md` when that report lands (deliverable G of the
|
|
|
2026-07 theming audit).
|
|
|
|
|
|
## 4. `architecture/sema.md` hold-sync — 2026-07-06
|
|
|
|
|
|
**Origin**: A.4/A.5 execution (holds single-source). `SEMA_HOLDS_BY_INTENT`
|
|
|
(`src/uix/sema/holds.ts`) is now THE only hold table (resolver + visual
|
|
|
channel consume it; `sema-map.ts`'s per-family hold column died). The chapter
|
|
|
[`architecture/sema.md`](./architecture/sema.md) still narrates per-family
|
|
|
holds in ~34 places. **Blocked**: the file is the author's WIP (uncommitted
|
|
|
edits) — sync it when the author's pass lands, not before.
|
|
|
|
|
|
## 6. THM-2 rollout — per-instance palette to all surfaces — SHIPPED 2026-07-12; scope boundaries REVERTED by the open-color-cage initiative (2026-07-18/19)
|
|
|
|
|
|
Shipped in `05f0031c` (media-player Batch-4 + 14 surfaces + vocab + 4 props)
|
|
|
and `cc7e9a16` (avatar root). Every per-component palette cascade now collapses
|
|
|
into the shared `--palette-*` layer (size win, behavior-preserving for roles);
|
|
|
the vocabulary gained the alpha `surface`/`surface-hover` + `hover` slots.
|
|
|
|
|
|
> **REVERSED (2026-07-18/19) — "abrir la jaula del color".** THM-2 originally
|
|
|
> kept semantic controls (checkbox/radio/stepper/editable/select/file-upload)
|
|
|
> and `Avatar.Badge` role-restricted "by design". A later user decision reverses
|
|
|
> that: `color` accepts the FULL system (role / intent / 33 scales / raw CSS =
|
|
|
> `ComponentColorProp`) on **every** component, no exceptions — content-ink
|
|
|
> primitives keep their axis as an additive union. Enforced by two guards in
|
|
|
> `recipe-css-contract.test.ts`. Full record + the closed queue (incl. the
|
|
|
> chart SVG resolver and the Card/Avatar custom-color fix):
|
|
|
> [`process/open-color-cage-2026-07.md`](./process/open-color-cage-2026-07.md).
|
|
|
> Only `qr-code` stays out (its `color` is the QR module ink, not a scale).
|
|
|
|
|
|
## 5. Book editorial note — "el hold es suelo, no tijera" — 2026-07-06
|
|
|
|
|
|
**Origin**: A.4/A.5 verdict. The mechanism (hold = floor for the expression,
|
|
|
never a scissor; expression waits animation-finish after the hold, capped by
|
|
|
an absolute `MAX_EXPRESSION_WAIT_MS`, never derived from the hold) is recorded
|
|
|
in [`decisions/book-deviations.md`](./decisions/book-deviations.md) D.12 as a
|
|
|
candidate **editorial appendix** for the book (_Diseñando lo que ocurre_,
|
|
|
HOMOGENEIZADO edition) — the book gives qualitative regions and this is the
|
|
|
materialization doctrine worth feeding back to the source.
|
|
|
|
|
|
## 7. Chat block v2 — 2026-07-18
|
|
|
|
|
|
**Origin**: the `chat-*` v1 initiative (comparative study + plan, session
|
|
|
2026-07-18; user scoped v1 to the room core). v1 shipped `chat-log`
|
|
|
(Feed + end-anchored VirtualList), `chat-message`, `chat-composer`
|
|
|
(Textarea + FileUpload composition) and `chat-typing`, plus the
|
|
|
`$libs/chat` helpers and the VirtualList `anchor: 'end'` extension. The
|
|
|
deferred surface, registered with disposition (each item also lives in its
|
|
|
component README §Gaps):
|
|
|
|
|
|
- ~~**`chat-list`** — the conversation-list pane (rooms, unread badges,
|
|
|
typing-in-list).~~ **✅ SHIPPED 2026-07-19** — composes `Listbox`
|
|
|
(single-select + keyboard) with an avatar/title/preview/meta row; unread
|
|
|
bold + `Badge`, typing-in-list composes `ChatTyping`. Virtualization for
|
|
|
very long lists deferred (pairs with the VirtualList bidirectional item).
|
|
|
- ~~**`emoji-picker`** — a real picker component (color-picker scale:
|
|
|
categories, search, skin tones).~~ **✅ SHIPPED 2026-07-21** — composes
|
|
|
`Popover` + `Command` (search + grid keyboard + combobox/listbox a11y, its
|
|
|
documented "grid mode") + two `ToggleGroup`s (categories + skin tone). Data is
|
|
|
the vendored `$libs/emoji` (baked from emojibase, MIT — `scripts/generate-emoji-data.ts`):
|
|
|
~1,900 emoji, 316 tone-capable. The grid double-registers Command's
|
|
|
List/Item so it keeps the a11y but wears its own grid-cell styling. Delegated
|
|
|
(no own events). Full-grid virtualization + per-emoji tone + image emoji
|
|
|
(Twemoji/Noto) deferred. The quick-set Popover in `ChatMessage.ReactionAdd`
|
|
|
stays as the inline tapback bar (different UX).
|
|
|
- **Mention autocomplete (`@`)** — combobox-in-textarea for
|
|
|
`chat-composer`; pairs with the future `words`/`palabras` integration.
|
|
|
- **Threads UI** — the side-panel composition over the already-shipped
|
|
|
`Feed.Thread` semantics.
|
|
|
- ~~**Jump-to-message highlight** — `signal-emphasize-target` on
|
|
|
`chat-message` (the morfo event was deliberately left out of v1).~~
|
|
|
**✅ SHIPPED 2026-07-19** — `emphasized` prop → `data-emphasized` row flash
|
|
|
(visual-only, no sema event: reference apps flash silently; the app scrolls
|
|
|
via a reply's `onJump` then toggles `emphasized` for ~1.4s).
|
|
|
- ~~**Read-by list** — "seen by N" avatar row (Teams/Messenger pattern) on
|
|
|
`chat-message`.~~ **✅ SHIPPED 2026-07-19** — `ChatMessage.ReadBy` part
|
|
|
(the app composes `AvatarGroup`; the sender's `onMessageSeen` feeds it).
|
|
|
- **VirtualList chat-mode refinements** — bidirectional sparse ranges for
|
|
|
jump-into-history far from the loaded window.
|
|
|
- ~~**Typing avatars** — faces next to the `chat-typing` dots (Messenger).~~
|
|
|
**✅ SHIPPED 2026-07-19** — `ChatTyping.Avatars` slot (compose `AvatarGroup`).
|
|
|
- **Delegate-family ↔ agent chat** — the Aura reservation (§F6 of the
|
|
|
scene/pack initiative) once agent conversations land.
|
|
|
|
|
|
**Deps**: none to start `chat-list`/`emoji-picker`; mentions depend on the
|
|
|
`words` integration decision. The transport stays app-land (`$connection`).
|
|
|
|
|
|
## 8. `blocks` tier — reusable page-function compositions — 2026-07-21
|
|
|
|
|
|
**Origin**: ecosystem analysis session 2026-07-21; user decision: a new tier
|
|
|
for **blocks** — named compositions of canon components performing a page
|
|
|
function (sticky site header, hero, footer, app shell, docs shell…). Scope:
|
|
|
tier infrastructure (`src/uix/blocks/` + `$blocks`, doctrine at
|
|
|
`architecture/blocks.md`, B contract, `blocks-check` guard), **7 canon base
|
|
|
components first** (`sticky` · `empty-state` · `result` · `callout` ·
|
|
|
`prose` · `anchor-nav` · `sidebar` — full 9-phase route each), then the
|
|
|
block catalog (10 site + 10 app + 3 docs). Admission rule mirrors packs:
|
|
|
any behavior with contract surface is promoted to canon BEFORE the block
|
|
|
composes it. Detail + per-item fiches: the execution plan at
|
|
|
[`process/PLAN-blocks.md`](./process/PLAN-blocks.md).
|
|
|
|
|
|
**Status**: D-BLK decisions SIGNED 2026-07-21 (D-BLK.1 amended by the user:
|
|
|
the tier lives at `src/uix/blocks/`, not `src/blocks/`). **F0 SHIPPED
|
|
|
2026-07-21** (`069759604`: doctrine + `$blocks` + `blocks:check` + gallery).
|
|
|
**Reference study SHIPPED 2026-07-21** (`aff42fee0`:
|
|
|
[`process/RESEARCH-blocks-references.md`](./process/RESEARCH-blocks-references.md),
|
|
|
6 research tracks; every phase-0 now checks against it). Amendments E-1…E-5
|
|
|
SIGNED 2026-07-21: F1 = **8** canon components (+`nav-tree`, data-driven docs
|
|
|
navigation tree); parity-floor = v1 scope policy; F2 = **14** site blocks
|
|
|
(+banner · team · contact · content-section); distribution registered as §9;
|
|
|
⌘K stays app-land.
|
|
|
|
|
|
**F3 STARTED 2026-08-19** — `app-shell` shipped with a signed phase 0 (four
|
|
|
decisions: the block wires no services, two scroll models by prop, the top bar
|
|
|
inside the inset, generated bypass links) and a new canon component in front of
|
|
|
it, `SkipLink` (WCAG 2.4.1). It closes ledger row **A-95**, the one thing the
|
|
|
tier could not fix from inside any block: with the shell owning the page's
|
|
|
heights, an in-flow notice and a pinned header overlap by **0px** (measured;
|
|
|
before: 49px, with the hit test landing on the strip).
|
|
|
|
|
|
**Deps**: `scheduler` block gated on `chronos` landing; chat-room block
|
|
|
explicitly NOT here (canon `chat-*` family, §7 above).
|
|
|
|
|
|
## 9. Blocks distribution — registry + llms.txt + MCP — 2026-07-21
|
|
|
|
|
|
**Origin**: the blocks reference study (E-4 signed 2026-07-21; dossier
|
|
|
[`process/RESEARCH-blocks-references.md`](./process/RESEARCH-blocks-references.md)
|
|
|
§P6). The ecosystem's 2026 table stakes for component/block distribution are
|
|
|
agent-enumerable catalogs: a registry protocol (shadcn `registry.json` /
|
|
|
`registry-item.json`), `llms.txt` (+ `llms-full.txt` / `.md` content
|
|
|
negotiation) over the docs corpus, and an MCP server so agents can list and
|
|
|
install blocks (sv-blocks precedent). Scope when started: (a) `llms.txt`
|
|
|
generation from the docs corpus (cheap — the corpus is already Markdown,
|
|
|
pairs with F4 docs blocks); (b) a blocks/components registry manifest; (c)
|
|
|
an MCP server exposing catalog + morfo contracts (pairs with the
|
|
|
contract-introspection superation angle and the palabras F-B agentive
|
|
|
track). Registered, not started.
|
|
|
|
|
|
**Deps**: F2 catalog existing (something to distribute); F4.3 props-table
|
|
|
work shares the morfo-introspection substrate.
|
|
|
|
|
|
## 10. Barcode — deferred symbologies + GS1 profiles — 2026-07-29
|
|
|
|
|
|
**Origin**: the `Barcode` component (shipped 2026-07-29; design record +
|
|
|
dispositions in
|
|
|
[`eidos/components/barcode/README.md`](../src/uix/eidos/components/barcode/README.md)
|
|
|
§Gaps). v1 covers the Retail + Logistics set — Code 128 (A/B/C auto), EAN-13/8,
|
|
|
UPC-A/E, Code 39, ITF/ITF-14 — from their published ISO standards, in the
|
|
|
zero-dependency `$libs/barcode` encoder.
|
|
|
|
|
|
**Shipped the same day, out of this registry: the ISBN input profile.** An ISBN
|
|
|
barcode IS an EAN-13 (ISO 2108, Bookland 978/979), so it entered as a profile
|
|
|
over `ean13` — separators tolerated in the numeric symbologies, ISBN-10 read and
|
|
|
converted after validating its own base-11 check digit — and explicitly NOT as a
|
|
|
ninth `symbology` value, which would make `data-symbology` claim a standard that
|
|
|
does not exist. The doctrine is recorded in the component README §ISBN.
|
|
|
|
|
|
Four dispositions stay **diferir**, each a self-contained follow-up over the
|
|
|
same encoder:
|
|
|
|
|
|
- **Codabar · MSI · Pharmacode** — the niche tail JsBarcode still ships. Pure
|
|
|
table work: each is a symbology module + its vectors.
|
|
|
- **GS1-128 (FNC1 + Application Identifier parsing)** — a _profile_ of Code 128,
|
|
|
not a new encoder: the FNC1 code value plus an AI grammar so the HRI can print
|
|
|
the bracketed form. The natural pair for a logistics-facing app, and the same
|
|
|
shape the ISBN profile already established.
|
|
|
- **EAN-2 / EAN-5 add-ons + the upper `ISBN 978-…` line** — the periodical /
|
|
|
book supplements and the book-cover convention; they extend the EAN geometry
|
|
|
(a second text band, a composed add-on symbol) rather than replacing it. Hard
|
|
|
constraint recorded with them: **we do not hyphenate**. The hyphen positions
|
|
|
are not computable from the number — they need the International ISBN
|
|
|
Agency's prefix-range tables, versioned data with an expiry date, which is a
|
|
|
maintenance clock a zero-dependency library must not swallow. The hyphenated
|
|
|
string comes from the consumer's catalogue.
|
|
|
- **2D — DataMatrix · PDF417 · Aztec** — explicitly NOT this component: each is a
|
|
|
whole different encoder (own Reed–Solomon, matrix layout). They belong beside
|
|
|
`QrCode` as their own components, sharing the display-primitive shape.
|
|
|
|
|
|
**Deps**: none — the encoder's `BarcodeResult` (module lattice + guards + HRI
|
|
|
groups in module space) already generalises; the 2D entries would instead reuse
|
|
|
the `$libs/qr` shape.
|
|
|
|
|
|
## 11. Background — parallax, the scrim scale, and the pack seam — 2026-08-18
|
|
|
|
|
|
**Origin**: the `Background` component (design record, measurements and
|
|
|
dispositions in
|
|
|
[`eidos/components/background/README.md`](../src/uix/eidos/components/background/README.md);
|
|
|
plan and signed decisions in
|
|
|
[`process/PLAN-background.md`](./process/PLAN-background.md)). It ships as the
|
|
|
HOST of a surface's background layers — a child that pins itself behind its
|
|
|
parent's content, with the parent adopted by a foundation rule rather than
|
|
|
wrapped. Patterns, gradients, scrims, images and video are in; so is the WCAG
|
|
|
2.2.2 pause control.
|
|
|
|
|
|
**Scope of this registry entry** — what the component deliberately does NOT own
|
|
|
yet, each a self-contained follow-up:
|
|
|
|
|
|
- **Parallax and the scroll seam** — `animation-timeline: view()` with a
|
|
|
`ScrollProgress` fallback, `attach='fixed'`, the pointer `depth` axis. The
|
|
|
infrastructure landed with the component (`$adom`'s `ScrollProgress` and
|
|
|
`prefersReducedData`); the layers that consume it did not. It is the one
|
|
|
follow-up with a mechanism already chosen and measured against browser
|
|
|
support, so it is also the one most likely to move first.
|
|
|
- **The scrim `strength` scale is not ordered by weight** — measured: `subtle`
|
|
|
veils MORE than `overlay`, and `overlay` and `muted` resolve to the same
|
|
|
number. The names come from the semantic opacity scale, which says how opaque
|
|
|
an ELEMENT is; borrowing them for a VEIL imports an ordering that means
|
|
|
something else. Renaming or renumbering is an API change, not a fix in
|
|
|
passing.
|
|
|
- **A bright photograph is illegible at every scrim weight** — white copy over
|
|
|
the heaviest veil measures below AA. Either the scale gains a step above
|
|
|
`--opacity-subtle`, or the doctrine becomes "the answer is a graded scrim",
|
|
|
stated once and demonstrated.
|
|
|
- **A shared `<video>` state port** — the framework has a load-state contract
|
|
|
for `<img>` (`ImageProvider` in `soma/layers`) and none for `<video>`:
|
|
|
`ScrollFrames` listens by hand and so does `Background.Video`. Two consumers
|
|
|
is the threshold this codebase uses to extract a layer.
|
|
|
- **The `Ambient` pack honouring the pause** — the canon publishes the context
|
|
|
(`paused`, `reduced`, `seen`); the pack reads it. A task OF the pack, because
|
|
|
the dependency runs pack → framework and never back.
|
|
|
|
|
|
**Deps**: the parallax entry needs no new service — the two `$adom` ports it
|
|
|
consumes already shipped. The scrim entries are the author's call on the token
|
|
|
vocabulary, so they block on a decision, not on code.
|
|
|
|
|
|
## 12. El contrato de cascada del velo de estado — 2026-08-20
|
|
|
|
|
|
**Origen**: el eje `theme-reach` (F2-A, sesión del 2026-08-20). Al tokenizar
|
|
|
componente a componente aparecieron, medidas una a una, seis incidencias que
|
|
|
son **la misma**: nadie fijó nunca cómo compone el velo de `archetypes.css` con
|
|
|
las reglas de una receta. No se tocan dentro del eje de theming — mueven píxel
|
|
|
y son decisión aparte — pero cada una está medida y localizada.
|
|
|
|
|
|
**La raíz**: `archetypes.css` pinta el velo neutro como
|
|
|
`background-image: linear-gradient(var(--state-hover), var(--state-hover))`, y
|
|
|
lo hace con DOS pesos distintos según el arquetipo — `:where(...)` (0,0,0) para
|
|
|
`trigger`, y especificidad PLENA (0,5,0) para `item` / `option`, «INTENTIONAL
|
|
|
cross-component accent», dice su comentario. Una receta que declare fondo sobre
|
|
|
el mismo nodo entra en una relación que nunca se escribió.
|
|
|
|
|
|
### Las seis, con su medición
|
|
|
|
|
|
1. **El atajo `background:` cancela el velo — 131 declaraciones en 45
|
|
|
componentes.** El atajo fija `background-image: none`; sobre un arquetipo
|
|
|
velado por `:where()` eso lo mata para siempre, no sólo en hover. Medido con
|
|
|
`scripts/__statelayer-analysis.ts`, que comprueba contra el morfo compilado
|
|
|
que el atajo cae sobre el nodo VELADO y no sobre cualquiera. Por clase de
|
|
|
regla: **66 BASE** (mecánicas: pasar a `background-color` no mueve el
|
|
|
reposo), **32 ACENTO** (selected/checked/open: §38 pide `background-color`
|
|
|
para que el velo COMPONGA encima), **23 HOVER** (la migración de la firma 3)
|
|
|
y **10 OTRA** (disabled, dragging, variantes). **Cero valores con
|
|
|
gradiente**, así que el único riesgo técnico del paso a longhand no existe
|
|
|
en este catálogo. El píxel que se mueve: **32 nodos velados que hoy no
|
|
|
reaccionan al ratón** pasarían a hacerlo. Esto explica la adopción 21/135
|
|
|
que midió la auditoría del 2026-07-01: el velo estaba escrito y
|
|
|
estructuralmente derrotado.
|
|
|
|
|
|
2. **La prop `hoverable` de `table` no suprime nada.** El velo de `item` es
|
|
|
incondicional y a (0,5,0), así que una fila se ilumina al pasar el ratón
|
|
|
aunque `hoverable` esté apagada — verificado con hover real, y ocurre desde
|
|
|
antes de tocar la receta. O el arquetipo decide y la prop sobra, o la fila
|
|
|
no es un `item` para el sistema. Decisión de MORFO. **Precisión medida
|
|
|
2026-08-21** (revisión adversarial, `elementsFromPoint`): en `table` el
|
|
|
arquetipo `item` está declarado en la fila Y EN LA CELDA
|
|
|
(`morfo/components/table.ts:132` y `:155`), y el nodo que recibe el velo
|
|
|
bajo el puntero es la CELDA — la decisión de morfo debe contemplar los dos
|
|
|
portadores, no sólo la fila.
|
|
|
|
|
|
3. **En `tree-grid` el empate lo decide el ORDEN DE CARGA.** El velo de `item`
|
|
|
y el hover de la receta pesan EXACTAMENTE lo mismo (0,5,0), así que gana la
|
|
|
hoja que cargue después — y en el dev server varía entre recargas: seis
|
|
|
corridas de la misma configuración dieron cuatro «sin velo» y dos «con
|
|
|
velo». Un empate resuelto por orden es la misma fragilidad que el canon ya
|
|
|
documenta para las bandas de `z-index`.
|
|
|
|
|
|
4. **En `tree-grid` la banda mata el hover.** La regla de `striped` pesa
|
|
|
(0,8,0) — siete atributos más el `:nth-of-type` (la cifra (0,7,0) que se
|
|
|
anotó primero contaba uno de menos; recontada 2026-08-21) — contra los
|
|
|
(0,5,0) del hover y usa el atajo: en las filas pares no ocurre NADA al
|
|
|
pasar el ratón. El hover del componente es inconsistente entre filas.
|
|
|
|
|
|
5. **Un token de receta no puede ganar al arquetipo.** La regla de `item` fija
|
|
|
también `color` en el estado seleccionado, a (0,4,0) contra los (0,3,0) de
|
|
|
la receta (la cifra (0,5,0) anotada primero era la de la variante `:hover`
|
|
|
de la misma lista; recontada 2026-08-21): un `table.selected-row-fg` no
|
|
|
podía mover nada nunca. Se retiró
|
|
|
con su declaración muerta (un token que no mueve nada es un token que
|
|
|
miente). Mientras el arquetipo pinte tinta, ningún componente puede
|
|
|
tematizar la suya en ese estado.
|
|
|
|
|
|
6. **`scroll-area.thumb-bg-hover` no tiene velo al que migrar.** Su thumb lleva
|
|
|
`archetype: 'thumb'`, que `archetypes.css` no vela, así que la firma 3 no
|
|
|
puede ejecutarse ahí sin cambiar el arquetipo — decisión de MORFO. Es el
|
|
|
único knob de los seis de la cola que sigue sin resolver.
|
|
|
|
|
|
7. **En `grid-list` el hover pinta DOS VECES sobre el MISMO nodo** (medido
|
|
|
2026-08-21 al tokenizarlo). A diferencia de `table` / `tree-grid` —donde el
|
|
|
velo cae en la celda y el plano en la fila, nodos distintos— aquí el plano
|
|
|
de la receta (`background:` atajo, (0,3,0) → `background-color`) y el velo
|
|
|
del arquetipo ((0,5,0) → `background-image`) caen ambos en
|
|
|
`[data-grid-list-row]`: el atajo fija `background-image: none` pero pierde,
|
|
|
así que se ven los dos. Por eso el commit de `grid-list` **no acuñó**
|
|
|
`hover-row-bg`: sería un token que no puede ganar (mismo criterio que
|
|
|
`listbox.highlighted`). Y como en `table`, **fila Y celda llevan
|
|
|
`archetype: 'item'`** (`morfo/components/grid-list.ts:118` y `:141`), así
|
|
|
que bajo el puntero se apilan velo de celda + velo de fila + plano — tres
|
|
|
capas. El par de portadores del punto 2 aplica igual aquí.
|
|
|
|
|
|
8. **En `tree-view` el velo DERRAMA sobre el subárbol entero** (medido y
|
|
|
capturado 2026-08-21 al tokenizarlo). `archetype: 'item'` está declarado en
|
|
|
el **`<li>` branch** (`morfo/components/tree-view.ts:103`), que contiene el
|
|
|
`branch-control` Y el `branch-content` con todos los descendientes — así que
|
|
|
al pasar el ratón por la fila de una carpeta abierta se tiñe **la carpeta
|
|
|
entera**: el nodo velado mide **336 px** (la rama completa) frente a los
|
|
|
36 px de la fila que el usuario señala. Un branch anidado acumula además el
|
|
|
velo de sus ancestros, así que sus filas se ven más oscuras que las
|
|
|
hermanas. Es la tercera variante de la misma familia: `table` lo tiene en
|
|
|
fila Y celda (punto 2), `grid-list` en fila Y celda sobre el MISMO nodo
|
|
|
(punto 7), y aquí en un **ANCESTRO** del nodo señalado. Mover el arquetipo
|
|
|
del `<li>` al control es morfo, y mueve píxel en toda la demo.
|
|
|
|
|
|
Ojo al tokenizar la familia: en `tree-view` el `branch-control` **no lleva
|
|
|
archetype**, así que ahí el plano de la receta es la única pintura y su
|
|
|
`hover-row-bg` SÍ es un token legítimo — al revés que en `grid-list`. La
|
|
|
misma regla CSS cubre los dos nodos.
|
|
|
|
|
|
9. **El plano de profundidad `overlay` impone TIPOGRAFÍA, no sólo elevación**
|
|
|
— medido con CDP el 2026-08-21 al tokenizar `link-preview`.
|
|
|
`[data-depth='overlay']` (`generated/base.css:7088`) declara
|
|
|
`font-family: var(--style-label-font-family)` y
|
|
|
`line-height: var(--leading-ui)` junto al fondo, el borde y la sombra. Un
|
|
|
componente que estampe el plano para su elevación —**veinte lo hacen**—
|
|
|
hereda además esa tipografía, y su propia declaración empata a **(0,1,0)**
|
|
|
y **pierde por orden de carga** (la hoja generada va después del chunk de la
|
|
|
receta). En `link-preview` el par `font-family` / `line-height` de la receta
|
|
|
NO pinta: el plano impone 1.25 donde ella pide `--leading-normal` (1.5).
|
|
|
Cuarta ocurrencia de la clase «empate resuelto por orden de carga» (§12.3, y
|
|
|
las dos del calendario y de `tree-grid`).
|
|
|
|
|
|
⚠ **El par se deja DECLARADO a propósito.** Se retiró primero —daba 0 diffs,
|
|
|
porque no pintaba— y se RESTAURÓ el 2026-08-22 al revisar la sesión:
|
|
|
retirarlo no es neutral, **fija el empate a favor del plano** y con él el
|
|
|
1.25, que es justo la decisión que este punto tiene pendiente. Un empate por
|
|
|
orden de carga se registra, no se resuelve dentro de un commit de
|
|
|
tokenización. Los dos tokens quedan adjudicados en el ledger de R-5.4 con esa
|
|
|
razón, y la medición del orden se hizo en DEV (estable en cinco recargas);
|
|
|
en un bundle de producción está sin verificar.
|
|
|
|
|
|
Lo que hay que decidir: si un PLANO DE PROFUNDIDAD tiene competencia sobre
|
|
|
la tipografía o sólo sobre la superficie. Si sólo sobre la superficie, esas
|
|
|
dos líneas salen del plano y cada componente recupera las suyas; si el plano
|
|
|
manda, entonces ningún overlay del catálogo debe declarar familia ni
|
|
|
interlineado y hay que barrer los que lo intentan. Mueve píxel en los veinte
|
|
|
en cuanto se toque.
|
|
|
|
|
|
### Qué habría que decidir
|
|
|
|
|
|
En una frase: **quién manda cuando el arquetipo y la receta pintan el mismo
|
|
|
eje**. Las tres piezas concretas: si el velo de `item` baja a `:where()` como
|
|
|
el de `trigger` (y entonces la receta manda, y `hoverable` vuelve a significar
|
|
|
algo); si las recetas pasan a longhand en bloque (66 BASE + 32 ACENTO, mecánico
|
|
|
y sin gradientes de por medio); y si un arquetipo puede fijar `color` sobre un
|
|
|
componente que quiere tematizarlo.
|
|
|
|
|
|
**Deps**: ninguna de código. Es decisión del autor sobre píxel, y el
|
|
|
instrumental para medirla ya existe (`__statelayer-analysis.ts` +
|
|
|
`__theming-probe.ts` + `__tg-rowhover.ts`).
|
|
|
|
|
|
## 13. Huecos de instrumento y de demo del eje theme-reach — 2026-08-20
|
|
|
|
|
|
**Origen**: la misma sesión. Cosas que hicieron que una medición mintiera o no
|
|
|
existiera. Se anotan porque el eje entero se apoya en esas mediciones.
|
|
|
|
|
|
- ~~**El centinela da falsos negativos sobre una propiedad transicionada.**~~
|
|
|
**CAUSA ENCONTRADA Y ARREGLADA 2026-08-21** (revisión adversarial): no era la
|
|
|
transición. El paso «abrir lo que se pueda abrir» hacía CLIC en
|
|
|
`[data-command-input]`, y un clic de ratón sobre un input de texto SÍ casa
|
|
|
`:focus-visible` — la regla de foco (0,2,0) se quedaba con `border-color` y
|
|
|
el token de reposo leía muerto. El guard (`theming-sentinel.ts`) ahora hace
|
|
|
`blur()` tras abrir. La revisión midió la tasa completa del instrumento
|
|
|
viejo: **22 falsos negativos de 26 «no effect»** en F2-A, con tres causas
|
|
|
(el clic-foco, escribir sólo en `[data-{c}]` cuando las partes cuelgan de
|
|
|
`{c}-root`, y no leer `::before`/`::after`). Las tres, arregladas.
|
|
|
- ~~**El centinela no ve `::placeholder`.**~~ **ERA FALSO, y ARREGLADO
|
|
|
2026-08-21** (al tokenizar `textarea`): `getComputedStyle(el, '::placeholder')`
|
|
|
SÍ devuelve el valor — medido, el color centinela volvió tal cual. La
|
|
|
afirmación anterior nunca se comprobó y le costó a
|
|
|
`command.input-placeholder-fg` una entrada de ledger «verificada a mano» que
|
|
|
ha salido STALE y se ha borrado. El pseudo entra ahora en el snapshot del
|
|
|
guard, junto a `::before`/`::after`.
|
|
|
- **El centinela no ve lo que pinta un componente COMPUESTO.** Los tokens que
|
|
|
media-player reenvía al Slider (`--media-player-track` →
|
|
|
`--slider-track-bg`) pintan en nodos `[data-slider-*]`, fuera del espacio de
|
|
|
atributos del componente medido. Sigue vivo; adjudicado por token en el
|
|
|
ledger de R-5.4.
|
|
|
- ~~**La sonda no pasa el ratón por un `<tr>`.**~~ **ARREGLADO 2026-08-21**:
|
|
|
`tr` entra en el filtro de hover de la sonda, y el guard R-5.4 añade un pase
|
|
|
de hover propio para los tokens `hover-*` (así adjudicaron
|
|
|
`table.hover-row-bg` y `gradient-picker.hover-preset-border`).
|
|
|
- **La sonda medía una demo aún CARGANDO.** El auto load-more de `feed`
|
|
|
mantiene `[data-busy]` ~3 s y su firma `commit-settle` anima `box-shadow`
|
|
|
sobre el nodo medido — los «5 diffs entre dos corridas del mismo código» del
|
|
|
handoff eran esto, no una animación sin localizar. **ARREGLADO 2026-08-21**:
|
|
|
sonda y guard esperan a que `[data-busy]` caiga; dos corridas del mismo
|
|
|
código dan 0 diffs y 0 nodos ausentes (6.264 valores).
|
|
|
- **Una sonda sobre una demo que no monta la parte compara CERO valores y
|
|
|
pasa.** Ocurrió con el skin de audio: `/uix/components/audio-player` da 404
|
|
|
porque la demo es la de `media-player` con el chip `media`, y la sonda cargaba
|
|
|
en modo vídeo. Resuelto con `scripts/__probe-audio-skin.ts`, pero la clase de
|
|
|
fallo es general: **una parte condicional necesita que la sonda la monte**.
|
|
|
Afecta al menos a captions, buffering y paneles portalados de media-player, al
|
|
|
estado vacío y la barra de carga de `command`, y al overview de
|
|
|
`date-range-picker`.
|
|
|
- **`date-range-picker` no expone `kind='month'` / `'year'` en su demo**
|
|
|
(norma N-6), así que la mitad de su API no se ve ni se mide.
|
|
|
- **Un componente COMPUESTO puede tener su cromo entero muerto sin que nada
|
|
|
avise.** `gradient-picker` re-declaraba altura, padding, tipografía, color,
|
|
|
fondo, borde, radio, hover y foco de su trigger, y NADA de ello pintaba:
|
|
|
`popover.css` gana con `[data-popover-trigger]:not([data-archetype='field-trigger'])`
|
|
|
(0,2,0 contra 0,1,0). Lo cazó el centinela —36 de 45 tokens sin efecto— y
|
|
|
retirarlo dio 0 diffs. **EL GUARD EXISTE desde 2026-08-21: R-5.4**
|
|
|
(`npm run theming:sentinel -- <c> <url>`, `scripts/theming-sentinel.ts` +
|
|
|
ledger `theming-sentinel-exceptions.ts`, doctrina en recipe-contract §4).
|
|
|
Nació con la segunda ocurrencia de la clase: el `item-gap` de `carousel`
|
|
|
contra un estilo INLINE de soma, invisible para todo análisis estático.
|
|
|
Los ocho componentes de F2-A pasan con su adjudicación escrita.
|
|
|
- **`--gp-current-gradient` es un nombre ABREVIADO en SOMA.** Lo estampa
|
|
|
`gradient-picker-provider.svelte.ts` (no el wrapper de eidos, como decía la
|
|
|
ficha), y viola theming §6 r5. No es knob de tema —es un canal de valor— pero
|
|
|
el nombre es deuda; renombrarlo toca soma, otra capa.
|
|
|
- **Hooks por CLASE en una receta de eidos.** `gradient-picker` usa
|
|
|
`.gradient-picker-trigger-swatch` y `.gradient-picker-trigger-label` en vez
|
|
|
de `data-*`; `eidos-lint` los cuenta (`class-hooks: 2`) y el centinela no los
|
|
|
ve, porque filtra nodos por atributo.
|
|
|
- **El foco del trigger de `gradient-picker` es opaco** (`--color-primary-solid`)
|
|
|
mientras el resto del catálogo lleva la mezcla suave del sistema
|
|
|
(`--focus-ring-color`, 52 %). Quedó sin tocar porque alinearlo mueve píxel —
|
|
|
y de hecho quedó sin token, porque la regla entera está muerta bajo popover.
|
|
|
- ~~**El censo PENALIZA consumir una capa compartida correctamente.**~~
|
|
|
**RESUELTO PARA LA FAMILIA CALENDAR 2026-08-21** y abierto para el resto: el
|
|
|
censo tiene ahora `LAYER_VOCABULARY` (mismo precedente que D-TH.2-b), así que
|
|
|
el `--calendar-*` que leen `range-calendar` / `month-grid` / `year-grid`
|
|
|
cuenta como `system`. **`list-surface` NO está registrado a propósito**: sus
|
|
|
consumidores puentean por PRIVADOS (`--_listbox-item-*: var(--list-item-*)`),
|
|
|
otra forma, y activarlo mueve diez componentes de golpe — sigue siendo
|
|
|
decisión aparte. Y aparece un techo NUEVO debajo: con el vocabulario de la
|
|
|
capa fuera del denominador, a los tres sólo les quedan los **forwards de
|
|
|
paleta THM-2** (`--_x-palette-*`, que el censo cuenta como `private` en TODO
|
|
|
el catálogo) y literales de layout, así que siguen leyendo 0 %. Reclasificar
|
|
|
el forward de paleta es la misma pregunta, un piso más abajo, y afecta a
|
|
|
todos.
|
|
|
- **El censo PENALIZA consumir una capa compartida correctamente.** `listbox`
|
|
|
adopta los cinco ejes de ritmo que `list-surface` posee — que es exactamente
|
|
|
lo que la doctrina de capas manda — y el censo los cuenta como `private`, así
|
|
|
que su alcance queda en 68 % en vez de ~86 %. Es el mismo defecto de medición
|
|
|
que D-TH.2-b arregló para los primitivos tipográficos (`--style-*` pasó a
|
|
|
contar como `system`): la capa ES la superficie de tema de ese eje. Afecta a
|
|
|
los consumidores de `list-surface` (select · combobox · command · listbox ·
|
|
|
los menús) y de `viewport-placement` (affix · fab · menu-dial). Extenderlo es
|
|
|
una decisión, no una corrección al paso.
|
|
|
- **`listbox` pinta el `highlighted` DOS veces**: la receta pone un
|
|
|
`--color-surface-overlay` plano y encima el arquetipo `item` añade su velo del
|
|
|
8 % — medido. El plano es el duplicado que §38 deprecó; se dejó sin token para
|
|
|
no bendecirlo, pero retirarlo mueve píxel.
|
|
|
- **La demo del bloque F2-A ya enseña los tokens** (`TokensPanel`), pero los
|
|
|
~155 componentes sin contrato siguen sin superficie donde verlos hasta que se
|
|
|
tokenicen.
|
|
|
|
|
|
Lo que la capa `calendar-surface` añadió (2026-08-21):
|
|
|
|
|
|
- **⚠ El font-size de los selectores month/year del calendario es una MONEDA AL
|
|
|
AIRE.** `[data-calendar-month-select][data-button]` (0,2,0) empata con
|
|
|
`[data-popover-trigger]:not([data-archetype='field-trigger'])` (0,2,0) — la
|
|
|
MISMA regla de `popover.css` que dejó muerto el cromo entero del trigger de
|
|
|
`gradient-picker` — y gana la hoja que cargue después: dos corridas del mismo
|
|
|
código dan 16px o 14px en los mismos nodos (24 diffs de instrumento, medidos
|
|
|
con la sonda dos veces). El pin del calendario existe justamente para que
|
|
|
caja, etiqueta y chevron lean UNA talla, así que perderlo es visible.
|
|
|
Arreglarlo es subir el peso del pin de forma determinista — decisión, porque
|
|
|
fija el píxel en un lado. Segunda ocurrencia de la clase §12.3 (empate por
|
|
|
orden de carga) y segunda víctima de la misma regla de popover.
|
|
|
- **Los tres consumidores de la capa siguen leyendo 0 % de alcance**, ahora por
|
|
|
los forwards de paleta THM-2 — ver el punto del censo más arriba.
|
|
|
|
|
|
Lo que la revisión adversarial de F2-A añadió (2026-08-21):
|
|
|
|
|
|
- ~~**PENDIENTE DE FIRMA — el `striped` de `table` es inerte con RowDetail.**~~
|
|
|
**FIRMADO Y EJECUTADO 2026-08-21**: `:nth-child(even of [data-table-row])`
|
|
|
cuenta filas de DATOS y la paridad ya no cae en los `<tr>` de detalle (la
|
|
|
demo no bandeaba NADA; ahora las filas 2 y 4 tiñen — 12 diffs medidos, 2
|
|
|
filas × 6 estados, y nada más se movió en 13.195 valores). El selector
|
|
|
conserva el (0,6,0) del viejo para que el hover siga ganando su empate (no
|
|
|
se importa el defecto §12.4 de tree-grid) y un `:where(:not([data-selected]))`
|
|
|
de peso cero deja el acento seleccionado al mando — verificado los tres.
|
|
|
La excepción del ledger se retiró; `striped-row-bg` adjudica VIVO (42/45).
|
|
|
- **El gap entre diapositivas de `carousel` es un canal de valor de soma.**
|
|
|
Soma lo estampa INLINE desde la prop `gap` (necesita el número para el
|
|
|
`flex-basis`), así que ninguna declaración de receta puede ganarle — el trío
|
|
|
`item-gap*` se retiró con 0 diffs. Si algún día debe ser temable, es soma
|
|
|
quien tendría que leer un token; decisión de otra capa.
|
|
|
- **Dos `header-z` llevan el literal `'2'`** (`table`, `tree-grid`) mientras los
|
|
|
otros catorce `*-z` del catálogo leen la escalera `--z-index-*`. Alinearlos es
|
|
|
decidir qué peldaño significa «cabecera pegajosa dentro de un scroller».
|
|
|
- **El spinner de `media-player` conserva `720ms` a pelo** mientras el spinner
|
|
|
idéntico de `feed` ganó dos tokens públicos de duración
|
|
|
(`sentinel-spinner-duration` / `-reduced-duration`). El mismo trato o una
|
|
|
anotación `functional:`, pero no las dos varas.
|
|
|
|
|
|
Lo que `grid-list` añadió (2026-08-21):
|
|
|
|
|
|
- **⚠ PENDIENTE DE FIRMA — la selección de fila de `grid-list` nunca ha
|
|
|
pintado.** La receta seleccionaba `[data-grid-list-row][data-selected]`, un
|
|
|
atributo que ni el morfo declara ni soma estampa: soma estampa
|
|
|
`data-state='selected'` + `aria-selected='true'`. La regla no casaba jamás,
|
|
|
así que su acento (`--_grid-list-palette-element`) no llegó nunca al píxel y
|
|
|
una fila seleccionada muestra sólo el velo NEUTRO del arquetipo —
|
|
|
visualmente indistinguible de una fila con el ratón encima (capturado). La
|
|
|
declaración muerta y su forward huérfano se retiraron en el commit de
|
|
|
tokenización con **0 diffs de computed**, que es la prueba de que estaban
|
|
|
muertos. **Repararlo mueve píxel** (re-apuntar a `[data-state='selected']`
|
|
|
haría aparecer un acento que el componente nunca tuvo), y choca de frente con
|
|
|
§12.5: el arquetipo `item` fija también `color` a (0,4,0) sobre el estado
|
|
|
seleccionado. Es la misma decisión del velo, no una corrección al paso.
|
|
|
El acento del SelectionCheckbox (`_palette-solid`) sí vive y se conservó.
|
|
|
- **`SHARED_LAYERS` declara a `grid-list` consumidor de `menu-indicator`, y no
|
|
|
lo es.** `theming-census.ts:432` lo lista, pero `lib/menu-indicator.css` no
|
|
|
tiene un solo selector que apunte a grid-list (sus 134 líneas targetean
|
|
|
`[data-context-menu-*]` / `[data-dropdown-menu-*]`), la receta no importa la
|
|
|
capa y soma no renderiza indicator: el checkbox **espeja** ese visual
|
|
|
pintándolo por su cuenta. La entrada hace que el informe generado escriba
|
|
|
«Consume la capa compartida `menu-indicator`» en la §4 de la ficha, que es
|
|
|
falso y dirige mal a quien la lea. Revisar las seis entradas de esa lista
|
|
|
contra los selectores reales de cada capa (`listbox`, `menubar` y
|
|
|
`navigation-menu` están en la misma entrada y tampoco aparecen en el fichero).
|
|
|
|
|
|
Lo que `textarea` añadió (2026-08-21):
|
|
|
|
|
|
- **⚠ El borde de foco de `textarea` no se ve al hacer CLIC — sólo con Tab.**
|
|
|
Medido: la regla de hover `[data-textarea-input]:hover:not([data-disabled]):not([data-readonly])`
|
|
|
pesa **(0,4,0)** y la de foco `[data-textarea][data-focused] [data-textarea-input]`
|
|
|
**(0,3,0)**, así que con foco y puntero a la vez gana el hover — y hacer clic
|
|
|
deja el puntero encima por definición. Verificado con las dos reglas
|
|
|
apuntando a colores distintos: sale el del hover. Debajo, `[data-invalid]`
|
|
|
pesa **(0,2,0)**: un campo inválido pierde su borde rojo tanto al enfocarlo
|
|
|
como al pasar el ratón. El anillo de foco del SISTEMA (`outline`) sí se ve
|
|
|
siempre, así que no es un fallo de accesibilidad, pero el orden de las tres
|
|
|
reglas de `border-color` está invertido respecto a lo que significan.
|
|
|
Arreglarlo mueve píxel: no entra en el commit de tokenización.
|
|
|
- ~~**El guard mide con el PUNTERO encima del componente.**~~ **ARREGLADO
|
|
|
2026-08-21**: `reopen()` corre antes de CADA token y hace clic en
|
|
|
`[data-{c}-input]` cuando el componente no tiene parte `content` — el
|
|
|
`blur()` que añadió la revisión adversarial quita el foco pero **no el
|
|
|
puntero**, así que `:hover` seguía casando durante toda la corrida y toda
|
|
|
regla de hover pisaba a sus vecinas de reposo. Es la mitad que aquel arreglo
|
|
|
dejó atrás. En `textarea` costaba tres falsos negativos
|
|
|
(`input-border`, `focus-input-border`, `invalid-input-border`); con
|
|
|
`page.mouse.move(0, 0)` tras cada blur, `input-border` revive solo. Corridos
|
|
|
los diez componentes con ledger: ningún otro cambia, un único STALE
|
|
|
(`command.input-placeholder-fg`, por el arreglo del `::placeholder`).
|
|
|
|
|
|
- **Hueco de demo: `card-group` no monta su encabezado ESTÁTICO.** La página
|
|
|
renderiza el título sólo en su forma de disclosure (`[data-button]`), así que
|
|
|
la mitad `[data-static]` del cromo del título —tinta, familia, tamaño,
|
|
|
interlineado y el hueco inferior— no se ve ni se mide: cinco tokens
|
|
|
adjudicados por eso. Misma clase que `date-range-picker` con
|
|
|
`kind='month'|'year'` (norma N-6).
|
|
|
|
|
|
- ~~**El test del SUELO del censo que F0 pedía no existe.**~~ **ESCRITO
|
|
|
2026-08-22** — `src/uix/eidos/theming-reach-floor.test.ts`: alcance global
|
|
|
≥ 56 %, literales ≤ 564, globales ≤ 1.156 y ≥ 14 componentes al 100 %, con
|
|
|
las dos muta-pruebas que el plan exige (un literal inyectado y cinco públicos
|
|
|
cambiados por primitivos, cada uno con su rojo). Medido de paso: **los techos
|
|
|
son el detector FINO y el porcentaje el grueso** — cinco knobs de ~4.700 no
|
|
|
mueven un porcentaje redondeado, pero sí un techo. La entrada original decía: El plan (§5, F0.d)
|
|
|
encarga «un test que fije el suelo: alcance global ≥ el de hoy y literales ≤
|
|
|
614 — el número SUBE o el test falla», con su muta-prueba. No hay
|
|
|
`theming-census.test.ts` ni equivalente bajo `src/uix/eidos/`, así que hoy
|
|
|
**nada impide que el alcance BAJE** entre sesiones: sólo se detectaría
|
|
|
mirando la cifra a mano. Con el censo ya endurecido (ver arriba) es el
|
|
|
momento de escribirlo.
|
|
|
|
|
|
- **⚠ `knob.arc-width` significaba DOS cosas con DOS defaults distintos** —
|
|
|
medido 2026-08-22 al tokenizarlo: el inset de la cara del dial (`--space-3`)
|
|
|
y la separación superior del puntero (`--space-2`). Un tema que lo escribiera
|
|
|
movía las tres declaraciones a la vez, pero SIN tema cada una valía algo
|
|
|
distinto. Separado en `arc-width` + `pointer-inset` con 0 diffs. Queda como
|
|
|
aviso de clase: **un mismo nombre con defaults distintos por sitio es una
|
|
|
colisión aunque el override funcione** — es la definición que el propio
|
|
|
generador usa para marcar `⚠`, y conviene barrer el catálogo buscando más.
|
|
|
|
|
|
- **⚠ `mockup` no tiene ruta de demo** (`/uix/components/mockup` da 404), como
|
|
|
le pasaba a `picker-shell`. Con 29 knobs al 38 % es el mayor de los que
|
|
|
quedan sin tocar de su tramo, y sin demo no hay sonda ni guard: habría que
|
|
|
medirlo prestado o darle página.
|
|
|
|
|
|
- **⚠ R-5.1 y R-5.2 no están implementadas** en `component-audit` (0 disparos
|
|
|
hoy; sólo existe R-5.3 de gramática y R-5.4 de alcance). El plan las da por
|
|
|
hechas en F0 «en `warn`» y F3 las quiere en `error`, así que **F3 no puede
|
|
|
cerrarse sin escribirlas primero**. Cuando lleguen, los componentes con
|
|
|
alcance parcial necesitan su marca `R-5.x exception:` en el README — ya
|
|
|
puestas en los 16 de F2-B que no llegan al 100 %.
|
|
|
|
|
|
- **Cuatro componentes salen NEEDS-WORK en `component:audit`** y son
|
|
|
PREEXISTENTES (ninguno tiene un commit de F2-B en su código): `badge`,
|
|
|
`gradient-picker`, `mockup` y `motion`. Anotado para que nadie los atribuya
|
|
|
al eje de theming al leer el informe.
|
|
|
|
|
|
- **⚠ Un worktree de comparación NO hidrata si su `node_modules` es una
|
|
|
junction fuera del root** — Vite responde **403** a `@fs/…` y SvelteKit no
|
|
|
arranca, así que la página SSR se ve pero ningún componente monta. Mordió en
|
|
|
la revisión adversarial de F2-B, que comparó contra una base muerta sin que
|
|
|
nada lo delatara. Se arregla con `server: { fs: { allow: [...] } }` en el
|
|
|
`vite.config.ts` del worktree, y la regla que generaliza es **comprobar que
|
|
|
un componente monta ANTES de comparar**. (Memoria
|
|
|
`worktree-devserver-fsallow`; vuelve a morder cada vez.)
|
|
|
|
|
|
- **⚠ `--radius-xs` NO EXISTE, y una receta lo referenciaba.** La escala de
|
|
|
radios del sistema es `sm | md | lg | xl | xxl` (+ `none`, `full`,
|
|
|
`default`), sin `xs`. `text-focus` escribía
|
|
|
`border-radius: var(--radius-xs, 3px)`: **pintaba el fallback**, así que
|
|
|
nadie lo notó — hasta que tokenizarlo sin fallback dejó la variable vacía, la
|
|
|
declaración inválida y las cuatro esquinas cuadradas (**48 diffs**, medidos
|
|
|
2026-08-22). Corregido a `3px` verbatim, el valor que de verdad pintaba.
|
|
|
|
|
|
Lo que esto dice del catálogo: **un `var(--fantasma, fallback)` es una
|
|
|
referencia muerta que funciona por accidente**, y el guard G2 que el plan
|
|
|
menciona («referencias-fantasma contra el contrato derivado») o no existe o
|
|
|
no cubre los fallbacks. Barrer el catálogo buscando `var(--…, …)` cuyo primer
|
|
|
nombre no esté en el contrato derivado es un pase mecánico que se paga solo.
|
|
|
|
|
|
- **⚠ `filter` y `backdrop-filter` faltaban en la lista de propiedades del
|
|
|
guard** — arreglado 2026-08-22. `text-focus.glow-color` vive dentro de un
|
|
|
`drop-shadow()` y leía «no effect» estando vivo; `KNOB_PROPS` del censo sí
|
|
|
las tenía, así que el censo contaba el knob y el guard no podía verlo.
|
|
|
Verificados los 30 componentes con ledger tras el cambio: cero regresiones,
|
|
|
cero STALE.
|
|
|
|
|
|
- **⚠ El guard CONGELA las transiciones, y eso esconde los tokens cuyo trabajo
|
|
|
ES la transición.** La congelación existe por una razón buena y documentada
|
|
|
(una propiedad transicionada devuelve su valor INICIAL justo tras escribir el
|
|
|
token, y un token vivo parecía muerto), pero deja ciego al guard ante
|
|
|
`text-circular.char-transition`, que guarda una transición completa. Medido
|
|
|
sin congelar: alcanza. Si aparecen más tokens de este tipo —transición,
|
|
|
animación, easing— merecen un pase propio sin congelación, como el que ya
|
|
|
tiene el hover.
|
|
|
|
|
|
- **⚠ El valor centinela GRANDE no prueba nada contra una propiedad capada.**
|
|
|
`skin-media-player` calcula su ancho como `min(100%, base × scale)`: el
|
|
|
`1234px` del guard se capa al contenedor y los siete tokens de tamaño leen
|
|
|
«no effect» aunque estén vivos. Con valores PEQUEÑOS (111/122/133px y escalas
|
|
|
0.11/0.22/0.33) alcanzan exactamente. Es primo del caso ya registrado «un
|
|
|
valor centinela igual al real lee como no efecto» (`9999px` contra un
|
|
|
`--radius-full` que ya era 9999px): **el centinela tiene que ser imposible
|
|
|
EN LA DIRECCIÓN en que la propiedad puede moverse**. Un guard que probara
|
|
|
ambos extremos —uno muy grande y uno muy pequeño— cazaría las dos familias.
|
|
|
|
|
|
Lo que la REVISIÓN ADVERSARIAL de F2-B destapó (2026-08-22):
|
|
|
|
|
|
- **`drag-drop.preview-z` era un token que MENTÍA, y lo escondió una
|
|
|
adjudicación hecha sobre un nodo sintético.** El ledger decía «forzado →
|
|
|
alcanza (100 → 4321)», pero ese nodo lo había creado yo con JS. El preview
|
|
|
REAL —el que soma monta durante un arrastre de verdad— lleva
|
|
|
`z-index: 9999` **INLINE** (`drag-drop-provider.svelte.ts:663,671`), así que
|
|
|
ninguna cascada puede ganarle: el token resolvía a 4321 y el nodo computaba 9999. Retirado con 0 diffs, que es la prueba. Misma clase que el `item-gap*`
|
|
|
de `carousel` que retiró la revisión de F2-A: **canal de valor de soma, no
|
|
|
superficie de tema**.
|
|
|
|
|
|
La lección para el protocolo: **una adjudicación «forzado → alcanza» vale lo
|
|
|
que valga el nodo que se forzó**. Si lo creaste tú, no lleva lo que soma le
|
|
|
pone. Verificado el resto de la misma clase: `clipboard` (indicador real tras
|
|
|
un clic real) y `picker-shell` (pie real) coinciden con lo adjudicado y no
|
|
|
llevan inline; `skeleton-line` lleva inline pero de `inline-size`, no del
|
|
|
eje tokenizado.
|
|
|
|
|
|
- **El orden de carga NO es el mismo en dev que en producción.** Medido con
|
|
|
`npm run build` + preview y CDP: en dev la receta de `link-preview` va
|
|
|
ANTES del plano `overlay`, en producción queda EN MEDIO de sus dos reglas.
|
|
|
El ganador coincide por casualidad —hay dos reglas del plano y una queda
|
|
|
siempre al final—, pero la fragilidad de §12.9 es real y ahora está medida en
|
|
|
los dos entornos, no supuesta.
|
|
|
|
|
|
- **⚠ Un worktree de comparación NO hidrata si su `node_modules` es una
|
|
|
junction fuera del root**: Vite responde **403** a `@fs/…` y SvelteKit no
|
|
|
arranca, así que la página SSR se ve pero ningún componente monta. La primera
|
|
|
pasada de esta revisión comparó contra una base muerta y dio «77 casos, 1
|
|
|
diferencia» sin que eso significara nada. Se arregla con
|
|
|
`server: { fs: { allow: [...] } }` en el `vite.config.ts` del worktree. Es la
|
|
|
memoria `worktree-devserver-fsallow`, y vuelve a morder cada vez.
|
|
|
|
|
|
Lo que la cola pequeña añadió (2026-08-22):
|
|
|
|
|
|
- **⚠ El censo no distingue «0 % por deuda» de «0 % POR NATURALEZA», y ya son
|
|
|
cinco componentes.** Medidos uno a uno el 2026-08-22, `aspect-ratio`,
|
|
|
`text-blur`, `cascade`, `motion` y `date-picker` tienen alcance 0 % y
|
|
|
**ninguno tiene contrato que escribir**: el knob de `aspect-ratio` está
|
|
|
PRESTADO de `box` (la ficha ya lo marca ⤴) porque es una faceta suya, no un
|
|
|
componente; los de `text-blur` son el `1px` de la técnica sr-only; los de
|
|
|
`cascade` y `motion` son el `opacity` del gate antiparpadeo, mecánica del
|
|
|
canal de motion cuyo valor tematizable vive en `EidosConfig.motion`; y los de
|
|
|
`date-picker` son la corrección `max-content` que impide que el pie del
|
|
|
popover desborde, con un único valor correcto. Sus veredictos §5 quedan
|
|
|
escritos.
|
|
|
|
|
|
Mientras el censo los cuente igual que a un componente con deuda real, la
|
|
|
cifra global miente por abajo y el gate de F3 («censo 100 %») es inalcanzable
|
|
|
por construcción. Lo que hace falta es una clase más —`structural`, junto a
|
|
|
`system`— o una marca en la ficha que los saque del denominador, como
|
|
|
`LAYER_VOCABULARY` hizo con la familia calendar. Es la misma pregunta que ya
|
|
|
está abierta para las capas compartidas, un piso más abajo.
|
|
|
|
|
|
Lo que `picker-shell` añadió (2026-08-21):
|
|
|
|
|
|
- **⚠ `picker-shell` no tiene ruta de demo propia** (`/uix/components/picker-shell`
|
|
|
da 404) y sus partes se llaman `data-picker-header` / `-body` / `-footer` en
|
|
|
vez de `data-picker-shell-{parte}` — genéricas **a propósito**, dice su
|
|
|
receta, «so every picker gets the same visual contract for free». La suma de
|
|
|
las dos cosas deja al instrumental ciego: la sonda genérica devolvió **0
|
|
|
nodos**, que es el antipatrón de comparar cero valores y pasar. Se tokenizó
|
|
|
con sonda dirigida (abre el popover del `date-picker`, captura los seis nodos
|
|
|
reales en las cuatro `data-picker-size`: 576 valores, 0 diffs) y el guard
|
|
|
R-5.4 ganó un `COMPONENT_OVERRIDES` para poder abrirlo y filtrarlo — acotado
|
|
|
a este componente, con los otros catorce verificados sin cambio. Lo de fondo
|
|
|
sigue abierto: **una demo propia**, y decidir si el precio de los nombres
|
|
|
genéricos (un contrato visual común para todos los pickers) vale la ceguera
|
|
|
del instrumental.
|
|
|
- **El eje de talla de `picker-shell` es `data-picker-size` sobre un ancestro
|
|
|
PORTALADO**, así que el TSC no puede emitir su cascada (`scope: 'size:xs'`
|
|
|
generaría `[data-picker-shell][data-size='xs']`, que no casa nunca). Es el
|
|
|
único componente del eje cuyas coordenadas por talla quedan como públicos
|
|
|
planos que la receta consume desde sus propios selectores. Si algún día el
|
|
|
TSC gana un eje de scope configurable, éste es su caso de prueba.
|
|
|
|
|
|
Lo que `code-block` añadió (2026-08-21):
|
|
|
|
|
|
- **⚠ La demo de `code-block` NO ENSEÑA el `code-block`: el harness del sitio
|
|
|
de documentación le pinta el `<pre>` por encima.**
|
|
|
`[data-uix-docs] pre` (`web/routes/uix/uix.css:801`) pesa **(0,1,1)** y gana
|
|
|
a `[data-code-block-pre]` **(0,1,0)** — y `[data-uix-docs]` es el shell de
|
|
|
TODAS las páginas, así que la regla alcanza cualquier demo. Medidas **siete
|
|
|
propiedades pisadas**: `padding` (16px 20px del harness contra los 12px de la
|
|
|
receta), `border` (1px sólido contra ninguno), `border-radius` (8px contra
|
|
|
0), `background-color` (gris contra transparente), `font-family`,
|
|
|
`font-size` (13px contra 14px) y `line-height` (18.2px contra 20.3px). El
|
|
|
fondo, el borde y el radio que se ven en esa página son del sitio, no del
|
|
|
componente. Es la clase «la demo puede tapar el componente» (precedente
|
|
|
`proof-of-human`), pero aquí son siete propiedades y le toca justo al
|
|
|
componente cuya razón de ser es mostrar código. Por eso `pre-padding` lee
|
|
|
muerto en el guard y está adjudicado con esa medición.
|
|
|
|
|
|
Acotar la regla del harness es tocar el CSS del SITIO, que sirve a las ~162
|
|
|
páginas y a los bloques de código de las fichas: no entra en el commit de un
|
|
|
componente. Decisión aparte.
|
|
|
|
|
|
**No es un caso aislado — la misma familia de reglas pisa también a `code`**
|
|
|
(medido 2026-08-21 al tokenizarlo, con CDP): `[data-uix-docs] code` declara
|
|
|
`font-family`, `font-size`, `padding` y `border-radius`, y gana (0,1,1)
|
|
|
contra la regla BASE del componente (0,1,0). Sus reglas de VARIANTE (0,2,0)
|
|
|
sí ganan, así que el efecto es selectivo y por eso sólo su `radius` lee
|
|
|
muerto — 3px del sitio donde el componente pide 4px. Cualquier decisión sobre
|
|
|
la regla del harness cubre los dos componentes.
|
|
|
|
|
|
- **⚠ El guard de claves duplicadas del handoff INSPECCIONA EL VACÍO.** El
|
|
|
comando documentado en `CONTINUE-theming.md` («Trampas que costaron un commit
|
|
|
cada una») es
|
|
|
`grep -oE "^ '?[a-z0-9-]+'?: \{" src/uix/eidos/lib/recipes/base.ts | …`,
|
|
|
y **`grep -E` no interpreta ` ` como tabulador** — lo lee como una `t`
|
|
|
literal, así que el patrón no casa NADA: 0 coincidencias sobre 130 bloques
|
|
|
reales. El guard salía siempre «vacío = OK» sin haber mirado nada, que es la
|
|
|
definición del antipatrón que el propio proyecto tiene registrado. Con un tab
|
|
|
real en el patrón encuentra los 130 (comprobado: cero duplicados en el árbol,
|
|
|
así que no hay daño que reparar). El handoff queda corregido con la versión
|
|
|
que sí mira.
|
|
|
|
|
|
Lo que `text-gradient` destapó (2026-08-22) — **PENDIENTE DE FIRMA, apuntado
|
|
|
para resolver después** (autor, 2026-08-22):
|
|
|
|
|
|
- **⚠ La familia text-effects ACUÑA tipografía propia contra el canon.** Cinco
|
|
|
claves del contrato: `text-focus.word-size` (`var(--font-size-4xl, 3rem)` —
|
|
|
`4xl` es un paso que la escala canónica `xxs..xxxl` de
|
|
|
`lib/primitives/typography.ts` NO tiene: idioma Tailwind del import de julio
|
|
|
`63b371d5c`, pinta el fallback) y `word-weight` (900) ·
|
|
|
`text-circular.weight` (900) y `font-size` (1.5rem) ·
|
|
|
`text-gradient.font-weight` (500, subida el 22 en `97a6f03d7`). Son opiniones
|
|
|
del seed (react-bits) que el protocolo «valor verbatim» convirtió en contrato
|
|
|
sin la pregunta previa: ¿un tratamiento de pintura sobre texto real tiene
|
|
|
tipografía propia? La doctrina fijada en F2-B (regla 2, cinco precedentes:
|
|
|
`label`, `code`, `kbd`, `announce`, `code-block`) dice que una receta
|
|
|
CONSUME la capa tipográfica (`--style-*`) o HEREDA (`link` → `inherit`):
|
|
|
un TextGradient dentro de un `h1` ES un h1.
|
|
|
|
|
|
**Son SIETE claves en CUATRO de los cinco componentes** (medido 2026-08-22 al
|
|
|
adjudicar `text-scramble`): las cinco de arriba más `text-scramble.font` y
|
|
|
`.size`, éstas aún sin declarar en `base.ts`. Y la de `text-scramble` es la
|
|
|
peor de todas porque además apunta fuera del canon: `var(--font-mono, …)`,
|
|
|
nombre que **no existe** —el canónico es `--font-family-mono`—, así que pinta
|
|
|
`ui-monospace` y **el componente ignora la mono del tema** (`Azeret Mono`).
|
|
|
Medido en navegador. `text-blur` es el único limpio de la familia: hereda.
|
|
|
|
|
|
**Resolución propuesta**: retirar las cinco claves y heredar del contexto.
|
|
|
Mueve defaults (TextFocus deja de imponer 3rem/900, TextCircular 1.5rem/900,
|
|
|
TextGradient 500) → firma del autor. Con ello muere el fantasma `4xl` sin
|
|
|
tocar el módulo de tipografía, que es canon y está **prohibido modificar**
|
|
|
(autor, 2026-08-22). Refuerza además el barrido mecánico que `--radius-xs`
|
|
|
ya pedía: `var(--nombre, …)` cuyo nombre no emite ningún generador.
|
|
|
|
|
|
- **⚠ El barrido mecánico está HECHO: 9 referencias fantasma con fallback en el
|
|
|
catálogo de componentes** (2026-08-22). Es lo que pedían el hallazgo de
|
|
|
`--radius-xs` y el de `--font-size-4xl`, y ya no hay que suponer cuántas
|
|
|
quedan. Método: recoger las **5.793** custom properties declaradas en TODO
|
|
|
`src/uix/eidos` (fundación + capas + recetas + `generated/`) y buscar en las
|
|
|
recetas los `var(--x, fallback)` cuyo `--x` sea vocabulario de SISTEMA y no
|
|
|
esté declarado — el sistema nunca lo escribe en runtime, así que un nombre
|
|
|
ausente es una errata. (Los `var(--x)` SIN fallback ya los guarda
|
|
|
`recipe-css-contract`; el hueco eran justo los que llevan fallback, que
|
|
|
«funcionan por accidente».)
|
|
|
|
|
|
| referencia inexistente | dónde | qué pinta en su lugar |
|
|
|
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
|
| `--color-content-tertiary` ×3 | `float-panel.css:227` · `proof-of-human/clock.css:87` · `proof-of-human/rotate-align.css:114` | `--color-content-secondary`. **Es el caso histórico** que `recipe-contract` §1 cita como origen de la regla («shipped a month that way») y sigue vivo en tres recetas |
|
|
|
| `--style-{body,prose,label,caption}-font-variation-settings` ×4 | `text.css:32,55,65,75` | `normal` — los estilos con nombre emiten 6 claves y ésta no está, así que **el eje variable no llega nunca al primitivo `Text`** |
|
|
|
| `--font-family-sans` | `palabras.css:296` | `sans-serif` del sistema. El canónico es `--font-family-primary` (palabras es track WIP) |
|
|
|
| `--font-mono` | `text-scramble.css:11` | `ui-monospace`. El canónico es `--font-family-mono` |
|
|
|
|
|
|
Ninguna se arregla aquí: dos son tipografía y esperan la firma 1.bis, y las
|
|
|
otras dos familias son decisión de diseño (¿existe un slot `tertiary`?
|
|
|
¿el eje variable debe emitirse por estilo?). El barrido, en cambio, **debería
|
|
|
ser un guard**: la lógica es la misma que la del test de fantasmas sin
|
|
|
fallback que ya existe, con el filtro de vocabulario de sistema encima.
|
|
|
|
|
|
Lo que `chart` destapó (2026-08-22):
|
|
|
|
|
|
- **~~El censo penaliza consumir la capa tipográfica~~ — RESUELTO el mismo día,
|
|
|
y la nota estaba equivocada.** Escribí que las doce declaraciones de `chart`
|
|
|
que pasaban a `var(--style-caption|label-font-size)` seguían contando
|
|
|
`global` porque el censo sólo exime `--style-*` a los seis primitivos. El
|
|
|
censo medía BIEN: lo que faltaba era la **costura**. PLAN §2-A pide que todo
|
|
|
knob lea `--{c}-{slot}`, y leer el canon a pelo desde el CSS deja a un tema
|
|
|
sin poder retocar ESE componente sin mover el rol en toda la app. Con seis
|
|
|
claves `--chart-{caption,label}-*` cuyo VALOR es `var(--style-{rol}-*)`,
|
|
|
`global` cayó a cero y el alcance a 89 %. **La lección: cuando la métrica y
|
|
|
la doctrina discrepan, sospecha de la lectura antes que del instrumento.**
|
|
|
El molde canónico ya estaba escrito en la vertebración tipográfica
|
|
|
(`--accordion-trigger-font-family: var(--style-label-font-family)`), y D-TH.2
|
|
|
aclara que sólo los PRIMITIVOS quedan fuera. `listbox` (68 %, ritmo de fila
|
|
|
de `list-surface`) es la misma pregunta un piso más arriba: **una capa
|
|
|
compartida no se lee a pelo, se cose** — pendiente de revisar con este molde.
|
|
|
|
|
|
- **⚠ Un READOUT de dato no tiene estilo con nombre en el canon.** El número del
|
|
|
gauge (`--font-size-xl`), el total de un funnel, el valor de un `Stat`: no
|
|
|
son encabezados (`h2` está en ese tamaño pero significa otra cosa) ni texto de
|
|
|
cuerpo. Su costura toma el bundle de talla, que es correcto pero no dice nada
|
|
|
del ROL. Si aparecen más, la respuesta es un estilo con nombre nuevo en el
|
|
|
módulo de tipografía — firma del autor, y el módulo no se toca sin ella.
|
|
|
|
|
|
- **⚠ La clase `private` del censo ES, en su mayor parte, el PUENTE DE PALETA
|
|
|
THM-2** (medido 2026-08-22). De las 687 declaraciones que leen un privado en
|
|
|
el catálogo, **130 leen un `--_{c}-palette-*`**, y son las que dominan la
|
|
|
columna `private` de los componentes con paleta: `tag-group` 14/14,
|
|
|
`stepper` 11/11, `radio-group` 10, `checkbox` 8, `file-upload` 8,
|
|
|
`calendar`/`editable`/los tres grids 7 cada uno.
|
|
|
|
|
|
**No es deuda y no debe acuñarse**: el puente lo alimenta la capa compartida
|
|
|
por instancia desde `[data-color]`, y un público encima
|
|
|
(`--{c}-selected-bg: var(--_{c}-palette-surface)`) dejaría que un tema lo
|
|
|
fijara y **matara en silencio el `color=` de cada instancia** — la misma clase
|
|
|
de daño que «declarar un público de facto mata una escala» (`knob`). El
|
|
|
componente ya expone sus nueve roles × variante como claves públicas: ésa es
|
|
|
su superficie de color.
|
|
|
|
|
|
Consecuencia para la métrica: **el techo de un componente con paleta no es el
|
|
|
100 %**, y su cifra no compara con la de uno sin ella — el denominador incluye
|
|
|
un puente que por diseño no se acuña. Tres ya adjudicados con esta razón
|
|
|
(`tag-group` 65 %, `stepper` 79 %, `timeline` 73 %); los demás la heredan
|
|
|
cuando les toque. Si algún día se quiere una cifra comparable, la respuesta es
|
|
|
una clase `bridge` en el censo, no acuñar los tokens.
|
|
|
|
|
|
- **⚠ La capa `list-surface` no es alcanzable por un tema — MEDIDO
|
|
|
2026-08-22.** Ni por su propio nombre ni por el puente que acuñan sus
|
|
|
consumidores. Con el menú abierto y la fila a 36 px: un tema que escribe
|
|
|
`--list-item-height: 1234px` en `:root` no mueve nada, y uno que escribe
|
|
|
`--dropdown-menu-item-height` tampoco. Es cascada: la capa declara sobre
|
|
|
`[data-list-surface][data-size]` y el puente sobre `[data-{c}-content]`; las
|
|
|
dos ganan a `:root`.
|
|
|
|
|
|
Por eso `listbox` **no** se cosió como sus hermanos: copiar el puente habría
|
|
|
subido la cifra sin dar alcance (un token que miente). La salida es la de
|
|
|
`calendar-surface`: que el vocabulario de la capa viva en una entrada de
|
|
|
receta y el gancho sólo lo RESUELVA por talla. Toca los diez consumidores de
|
|
|
golpe → eje propio.
|
|
|
|
|
|
Y deja un aviso sobre el instrumento: **el centinela da por vivos los tokens
|
|
|
de esta forma** porque escribe inline en el nodo, que gana a la regla del
|
|
|
gancho. Para un token que un tema sólo puede escribir en `:root`, «mueve un
|
|
|
computed» no equivale a «un tema lo alcanza».
|
|
|
|
|
|
- **⚠ La FIRMA DE SALIDA está escrita a mano tres veces** (medido 2026-08-22).
|
|
|
`data-last-action` × 3 intents × 4 lados tiñe el borde durante la animación
|
|
|
de cierre, y la misma matriz vive en `popover` (22 reglas), `drawer` (22) y
|
|
|
`dialog` (9), leyendo `--color-{fulfill,threat,neutral}-element`. **Y ya ha
|
|
|
derivado**: dialog tiñe sólo `border-top-color` mientras popover y drawer usan
|
|
|
el shorthand con `--border-width-medium`, así que en dos de tres el GROSOR
|
|
|
también cambia al salir.
|
|
|
|
|
|
La tabla morfo→eidos la sitúa como mapeo TRANSVERSAL (« `events[].prewrite` →
|
|
|
tinting the exit anim by cause»), misma fila que los arquetipos y los
|
|
|
selectores `[data-event=…]`: es el canal visual de sema materializado por
|
|
|
eidos, como la capa de estado vive en `archetypes.css`. Coserlo por componente
|
|
|
sería triplicar un vocabulario que debería vivir UNA vez; **la salida es una
|
|
|
regla transversal sobre el arquetipo `content`**, con los colores de intent
|
|
|
(que son canon) — 53 knobs a `system` y un tema que retoca la firma una sola
|
|
|
vez. Lleva una firma dentro: ¿la salida tiñe sólo el color o también el
|
|
|
grosor?
|
|
|
|
|
|
- **⚠ El test de huérfanos cuenta un COMENTARIO como consumo.**
|
|
|
`--popover-bg` no lo leía ninguna regla; el único sitio del corpus que lo
|
|
|
nombraba era un comentario de `popover-arrow.svelte`. El test
|
|
|
`does not leave declared public recipe variables orphaned` busca el nombre
|
|
|
como TEXTO en los ficheros del componente, así que un comentario lo mantiene
|
|
|
«vivo» para siempre. Retirado el token y corregido el comentario
|
|
|
(2026-08-22); el test ganaría con despojar comentarios antes de buscar,
|
|
|
igual que hace el censo.
|
|
|
|
|
|
- **⚠ `color-picker` arrastra 23 tokens sin adjudicar, ANTERIORES al eje**
|
|
|
(2026-08-22). Su costura los dejó en 23 —eran 24 antes—, pero el bloque
|
|
|
viejo necesita su propio pase de medición: las muestras (7 claves), la rueda
|
|
|
de tono (`hue-0..360` + `saturation-floor`), el ancho del panel por talla,
|
|
|
el cuentagotas y las dos de transición. Dos causas ya descartadas: no es que
|
|
|
el panel no abra (abre y sobrevive al blur, al parqueo del ratón y al forzado
|
|
|
de `data-size`) ni que el guard no vea sus nodos (ya se le dieron las dos
|
|
|
familias que le faltaban).
|
|
|
|
|
|
- **~~Un falso negativo del centinela sin causa~~ — RESUELTO en la revisión
|
|
|
adversarial del bloque (2026-08-22).** `float-panel.resize-grip-fg` movía
|
|
|
aislado y leía muerto en una corrida completa. La causa: **la pasada de HOVER
|
|
|
deja el puntero sobre el último nodo que tocó**, y una regla `:hover` gana a
|
|
|
la de reposo con la que comparte nodo — el asa re-apunta su color al acento
|
|
|
al pasar el ratón. Así que CUALQUIER token de reposo probado después de un
|
|
|
token `hover-*` podía leer muerto. Es la misma clase que el envenenamiento
|
|
|
por clic-foco que F2-A arregló, una pasada más tarde. Arreglado aparcando el
|
|
|
puntero tras la pasada de hover; el propio guard delató entonces DOS
|
|
|
excepciones STALE (`float-panel.resize-grip-fg` y `media-player.track`),
|
|
|
retiradas. Confirma la regla de F2-A: **un falso negativo siempre tiene
|
|
|
causa.**
|
|
|
|
|
|
- **⚠⚠ EL VELO DEL `drawer` NO PINTA — defecto real, medido y visto**
|
|
|
(2026-08-22). Su regla es
|
|
|
`background: color-mix(in srgb, var(--drawer-overlay-bg) var(--drawer-overlay-opacity), transparent)`,
|
|
|
y **soma escribe `--drawer-overlay-opacity: 1` INLINE** en el nodo del velo (es
|
|
|
su progreso de arrastre, un número sin unidad). `color-mix` exige un
|
|
|
PORCENTAJE ahí: con `1` la función entera es inválida y el fondo cae a
|
|
|
`transparent`. Computado en el velo abierto: `rgba(0, 0, 0, 0)`; escribiendo
|
|
|
`80%` en el propio nodo pinta. **En la captura se ve**: el fondo detrás del
|
|
|
cajón está DESENFOCADO pero no atenuado — el `backdrop-filter` funciona y el
|
|
|
tinte no existe.
|
|
|
|
|
|
No se arregla aquí porque mueve píxel y toca soma (§7.8). El arreglo puede
|
|
|
ser de un lado o del otro: que soma escriba un porcentaje, o que la receta
|
|
|
no use ese slot como porcentaje. Y la lección general: **un valor que soma
|
|
|
escribe inline y una receta consume tiene que compartir UNIDAD**, o el fallo
|
|
|
es silencioso.
|
|
|
|
|
|
- **⚠ Las tallas del `drawer` son canal de valor en el eje que manda**: soma
|
|
|
escribe `width` (o `height`, según el lado) INLINE en el contenido, así que
|
|
|
`content-width-{sm,md,lg}` no puede ganar desde `:root` para un cajón
|
|
|
izquierda/derecha —el eje cruzado sí alcanza (`height-sm` forzado: 260px →
|
|
|
976px)—. Misma clase que el `item-gap` de carousel y el `preview-z` de
|
|
|
drag-drop, que se RETIRARON; aquí no, porque el token sigue siendo la fuente
|
|
|
del valor que soma resuelve. Decidir si se retira o si soma lo lee es firma.
|
|
|
|
|
|
- **⚠ El eje `size` del `color-picker` MUERE EN EL PORTAL — dos veces**
|
|
|
(2026-08-23). El panel viaja en un portal, fuera de `[data-color-picker]`,
|
|
|
así que ningún privado declarado en la raíz llega hasta él. Consecuencias
|
|
|
medidas:
|
|
|
1. **`--_color-picker-content-width` se declara TRES veces y no lo consume
|
|
|
NADIE.** La anchura del panel la fija
|
|
|
`--_popover-content-width-override: calc(var(--color-picker-content-width-md) + 2rem)`,
|
|
|
cableada al paso `md`. Un picker `sm` y uno `lg` abren el MISMO panel de
|
|
|
344 px, y `content-width-sm` / `content-width-lg` son dos públicos que no
|
|
|
mueven nada (el `md` sí alcanza, por el override).
|
|
|
2. **El tamaño de las muestras dentro del panel está clavado a `md`**: la
|
|
|
regla del contenido redeclara `--_color-picker-swatch-size: var(--color-picker-swatch-size-md)`
|
|
|
sin variantes por talla. Fuera del portal los tres pasos alcanzan
|
|
|
(20 / 24 / 28 px → 1234 px); dentro, sólo el `md`.
|
|
|
|
|
|
El arreglo honesto es una escala por talla EN EL ÁMBITO DEL CONTENIDO (que sí
|
|
|
lleva su `data-size`), no en la raíz. Mueve píxel para `sm` y `lg`, así que
|
|
|
es firma. Misma familia que la capa `list-surface` inalcanzable desde
|
|
|
`:root`: **el portal es una frontera de cascada, y un privado declarado en la
|
|
|
raíz no la cruza.**
|
|
|
|
|
|
- **Punto ciego genérico del centinela: los tokens que SON la transición.** El
|
|
|
guard congela las transiciones antes de medir —el arreglo que hizo medibles
|
|
|
`radio-cards` y compañía— y con ello `transition-duration` / `transition-ease`
|
|
|
no pueden moverse jamás. Medidos sin congelar en `color-picker` alcanzan
|
|
|
(0.12s → 11.5s). Cualquier componente con esos dos públicos los reportará
|
|
|
muertos: o el guard los mide en una pasada sin congelar, o cada uno paga su
|
|
|
adjudicación escrita.
|
|
|
|
|
|
- **⚠⚠ §12.9 tiene MECANISMO: el plano `overlay` no «impone» tipografía donde
|
|
|
el componente calla — GANA donde el componente habla** (medido en `menubar`,
|
|
|
2026-08-23). Acuñadas `panel-font-family` y `panel-line-height` sobre una
|
|
|
declaración que la receta YA tenía, el centinela las dio muertas:
|
|
|
`[data-depth='overlay']` declara las dos con la MISMA especificidad (0,1,0) y
|
|
|
más tarde en la cascada. Del mismo bloque, `color` y `font-size` —que el
|
|
|
plano no declara— sí alcanzan. Consecuencia para la cola: **mientras §12.9 no
|
|
|
se firme, un token de `font-family` o `line-height` en cualquier superficie
|
|
|
`overlay` MIENTE**, y son veinte componentes. Retiradas del contrato de
|
|
|
menubar y devueltas a su fuente literal, con la razón escrita en el CSS.
|
|
|
|
|
|
- **⚠ El hover de `menubar` mata la capa del sistema — y es del eje
|
|
|
navigation-menu, no de éste** (medido 2026-08-23). `[data-menubar-trigger]:hover`
|
|
|
pinta `background: var(--color-surface-overlay)`: en hover el
|
|
|
`background-image` computa `none` con `--state-hover` vivo. Dos razones, las
|
|
|
dos ya escritas en `navigation-menu.css` cuando retiró esta MISMA regla: el
|
|
|
shorthand resetea `background-image`, y la regla del arquetipo va en
|
|
|
`:where()`, especificidad CERO. El eje navigation-menu tiene `menubar` en su
|
|
|
cola por esto; aquí no se acuña un `hover-trigger-bg` porque fosilizaría la
|
|
|
invención (doctrina: el hover es del SISTEMA).
|
|
|
|
|
|
- **⚠ Un token de `::placeholder` lee MUERTO sobre un input sin `placeholder`**
|
|
|
(medido en `field`, 2026-08-23). Sin el atributo no existe la caja del
|
|
|
pseudo-elemento, y `getComputedStyle(nd, '::placeholder')` devuelve entonces el
|
|
|
estilo del ELEMENTO — que no sigue al token. Poniendo un placeholder, alcanza
|
|
|
(`oklch(0.61 0 0)` → `rgb(1,2,3)`). Es genérico: cualquier componente con un
|
|
|
token de placeholder lo sufrirá si su demo arranca con el input vacío. El guard
|
|
|
podría sembrar un placeholder antes de esa medida.
|
|
|
|
|
|
- **⚠ DOS recetas escriben `:global()` en un CSS PLANO, y el navegador descarta
|
|
|
la regla entera** (medido 2026-08-23): `onion-menu.css` (`.onion-menu-icon
|
|
|
:global(svg)`, que cree pintar el glifo al 70 % de su caja — computa 14px, el
|
|
|
tamaño propio del Icon) y `timeline.css:331` (`[data-timeline-marker]
|
|
|
:global(svg)`). `:global()` es un envoltorio de SVELTE: fuera de un
|
|
|
`<style>` de componente el selector es inválido. `image-picker.css` ya
|
|
|
documenta la trampa en su propio comentario, así que la lección estaba escrita
|
|
|
y se repitió. Arreglar el selector EMPIEZA A PINTAR lo que la regla dice, así
|
|
|
que mueve píxel en los dos y es firma. Mientras tanto, los literales se quedan
|
|
|
con el defecto escrito encima y sin token (uno acuñado se retiró porque
|
|
|
mentía).
|
|
|
|
|
|
- **⚠ Congelar las transiciones no basta cuando el nodo está ANIMADO** (medido
|
|
|
en `onion-menu`, 2026-08-23). `rim-glow-opacity` leía 0,482956 —el valor que
|
|
|
la animación tenía en ese instante— y el token parecía muerto; con la
|
|
|
animación congelada también, computa 0,45 y sigue al token. El guard no puede
|
|
|
congelar animaciones en general (Presence necesita la suya para montar), así
|
|
|
que es una medida A MANO cuando el nodo pulsa.
|
|
|
|
|
|
- **⚠⚠ LA CASCADA DE PALETA ANULA LOS TONOS DEL COMPONENTE — 419 claves
|
|
|
públicas del catálogo** (medido 2026-08-23 en `button`, `badge` y `callout`).
|
|
|
|
|
|
El forward emite, por componente, un bloque por tono
|
|
|
(`[data-button][data-color='risk'] { --button-palette-solid: var(--button-risk-solid) }`)
|
|
|
y, **al final**, uno genérico
|
|
|
(`[data-button][data-color] { --button-palette-solid: var(--palette-solid, var(--button-primary-solid)) }`).
|
|
|
Los dos tienen especificidad (0,2,0), así que **gana el último**: cualquier
|
|
|
instancia con `data-color` resuelve por el `--palette-*` GLOBAL, que
|
|
|
`[data-color='{tono}']` llena desde `--color-{tono}-*`. Consecuencia medida
|
|
|
sobre `data-color='risk'`: `--button-risk-solid` no mueve nada ni en `:root`
|
|
|
ni en el nodo, mientras `--palette-solid` y `--color-risk-solid` en el nodo
|
|
|
repintan. Las siete `{c}-primary-*` son el RESPALDO de la regla genérica y
|
|
|
sólo actúan en una instancia SIN `data-color`.
|
|
|
|
|
|
**Tamaño**: 49 recetas emiten el forward genérico y el contrato tiene **419
|
|
|
claves de tono** (`{tono}-{track|element|border|solid|solid-hover|text|contrast}`),
|
|
|
repartidas entre ~13 componentes que además declaran reglas por tono. Son
|
|
|
públicas y no alcanzan: R-5.4 las llamaría mentiras, y no son deuda de nadie
|
|
|
en particular — es el orden de emisión.
|
|
|
|
|
|
**Arreglo propuesto (firma)**: emitir el bloque genérico ANTES de los de tono.
|
|
|
Entonces una instancia con tono lee `--{c}-{tono}-*` —cuyo valor por defecto
|
|
|
ES `var(--color-{tono}-*)`, así que **no movería un píxel**— y las escalas y
|
|
|
el custom siguen cayendo por el genérico. **Contrapartida**: cambia el
|
|
|
contrato de la jaula del color — un ancestro que inyecte `--palette-*` dejaría
|
|
|
de pisar un tono SEMÁNTICO en esos componentes (seguiría pisando los
|
|
|
personalizados). Por eso no se toca aquí.
|
|
|
|
|
|
Mientras tanto el guard lo adjudica con un PATRÓN
|
|
|
(`SENTINEL_PATTERN_EXCEPTIONS`), para que la razón se escriba una vez.
|
|
|
|
|
|
- **⚠ `combobox.content-font-family` es el TERCER token de tipografía que el
|
|
|
plano `overlay` anula** (medido 2026-08-23). Su panel lleva
|
|
|
`data-depth='overlay'`, así que la clave no mueve nada, igual que las de
|
|
|
`menubar`, `dropdown-menu` y `context-menu` —las tres retiradas ya—. No se
|
|
|
toca aquí porque no es el componente en curso: cuando §12.9 se firme, o se
|
|
|
retira o empieza a pintar, y la decisión es la MISMA para las cuatro.
|
|
|
|
|
|
- **⚠ Ya son CUATRO los componentes canónicos sin ruta de demo**: `picker-shell`,
|
|
|
`mockup`, `surface` y **`audio-player`** (404 los cuatro; los dos últimos
|
|
|
medidos el 2026-08-23). El de audio se mide dentro de `/uix/components/media-player`,
|
|
|
tras el chip `media: audio`, porque es la piel de ese chasis. Lo de abajo vale
|
|
|
igual para él: sin ruta no hay sonda ni guard, y el guard **miente en verde-rojo** (`/uix/components/surface` = 404, medido
|
|
|
2026-08-23). Sin ruta no hay sonda ni guard, y el guard **miente en verde-rojo**:
|
|
|
reportaba 0/25 sobre una página vacía, incluido un `radius` que está vivo. El
|
|
|
parche es el mismo que se le puso a picker-shell —medirlo donde SÍ se renderiza
|
|
|
(`urls` en el override)—, pero la solución de fondo es que un componente del
|
|
|
canon tenga su demo.
|
|
|
|
|
|
- **⚠⚠ EL PLANO `overlay` ANULA LAS TRES VARIANTES DE `tooltip`** (medido
|
|
|
2026-08-23). No es sólo la tipografía de §12.9: `[data-depth='overlay']`
|
|
|
declara `background`, `border` y `box-shadow` con la misma especificidad que
|
|
|
`[data-tooltip-content]` y más tarde en la cascada, así que gana. Medido con el
|
|
|
panel abierto: `--tooltip-bg` / `-border` / `-shadow` no mueven nada,
|
|
|
`--depth-overlay-surface` / `-border` / `-shadow` repintan.
|
|
|
|
|
|
**Consecuencia visual**: `solid`, `outline` y `ghost` computan el MISMO fondo,
|
|
|
el MISMO borde y la MISMA sombra. `outline` es indistinguible de `solid`;
|
|
|
`ghost` sólo aporta un `backdrop-filter` invisible tras una superficie opaca.
|
|
|
La receta tiene una máquina de variantes completa (privados por variante) que
|
|
|
el plano neutraliza entera.
|
|
|
|
|
|
Las cinco claves afectadas quedan ADJUDICADAS, no retiradas: son la fuente de
|
|
|
esa máquina y borrarlas dejaría los privados inválidos sin arreglar el fondo.
|
|
|
La decisión es la de §12.9 —qué manda, el plano o la receta— y aquí ya no es
|
|
|
sólo tipografía: es la superficie.
|
|
|
|
|
|
- **El censo pierde el eje `size` DE PREMIO por aplicar bien el protocolo**
|
|
|
(medido 2026-08-23 en `navigation-menu`). `hasSize` se decide grepeando
|
|
|
`data-size` en el CSS de la receta (`theming-census.ts:287`), y el paso §7.4
|
|
|
del protocolo ORDENA retirar los bloques `[data-size]` del CSS cuando la
|
|
|
cascada la emite el TSC. Resultado: todo componente que haga lo correcto
|
|
|
aparece con «eje `size`: no» en su ficha y sin `y` en la tabla del censo.
|
|
|
Ya le pasa a `sidebar` y `nav-tree`, y a `navigation-menu` desde hoy — los
|
|
|
tres con su cascada por talla viva en `generated/base.css`. La columna es
|
|
|
informativa (no entra en el ratio), pero el conteo «con eje `size`: N» del
|
|
|
encabezado cuenta al revés de lo que quiere medir. El arreglo es leer el
|
|
|
eje del CONTRATO (una clave `-{sm,md,lg}` o un `scope: 'size:{k}'`), no del
|
|
|
CSS.
|
|
|
- **La sonda y el centinela no ven las partes que el CONSUMIDOR compone**
|
|
|
(medido 2026-08-23 en `navigation-menu`). Las filas de su mega-menú son `<a>`
|
|
|
desnudos que la receta estiliza por descendencia
|
|
|
(`[data-navigation-menu-content] :is(a, …)`): sin atributo del componente, el
|
|
|
filtro `data-{c}-*` veía 10 nodos y ninguno era el panel ni una fila — 9 de
|
|
|
19 knobs fuera del diff. Es la MISMA clase que `prose` y `onion-menu`, pero
|
|
|
por composición del consumidor, no por HTML crudo ni por hooks de clase.
|
|
|
Resuelto para este componente con `EXTRA_NODES` / `extraNodes` (y el paso
|
|
|
`open` de la sonda, que no los honraba). **Queda la pregunta general**: una
|
|
|
receta que estiliza nodos sin atributo propio no es medible por defecto, y
|
|
|
nada avisa — el gate sale verde.
|
|
|
- **Un panel de HOVER se cierra por el propio guard.** `navigation-menu` abre
|
|
|
con `pointerenter` y `pointerleave` programa el cierre; el guard abre con
|
|
|
clic y luego aparca el puntero en (0,0), que es exactamente el gesto que lo
|
|
|
cierra. Producía no-determinismo (dos corridas del mismo código discrepando
|
|
|
en un token) y **las dos caras del error a la vez**: falso negativo en los
|
|
|
tokens de la fila y falso positivo en `content-link-padding-inline`, que leía
|
|
|
«vivo» por ensanchar el panel que sí se medía. Resuelto con `openBy: 'hover'`.
|
|
|
La regla general: **antes de creer un «no effect», comprobar que la superficie
|
|
|
seguía abierta al medirlo**.
|
|
|
|
|
|
**Deps**: ninguna. Son mejoras del instrumental del eje, ejecutables cuando
|
|
|
estorben.
|