|
|
---
|
|
|
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.
|
|
|
|
|
|
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,7,0) 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,5,0) contra los (0,3,0) de
|
|
|
la receta: 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.
|
|
|
|
|
|
### 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.**
|
|
|
`command.input-border` salió «no effect» y el token SÍ alcanza — verificado
|
|
|
tres veces a mano con la misma congelación de transición que el propio
|
|
|
centinela aplica. No se encontró la causa; queda sin explicar.
|
|
|
- **El centinela no ve pseudo-elementos.** `::placeholder`, `::after` (el scrim
|
|
|
de media-player) y demás quedan fuera de `getComputedStyle` sobre el nodo, así
|
|
|
que sus tokens siempre salen «muertos».
|
|
|
- **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.
|
|
|
- **La sonda no pasa el ratón por un `<tr>`.** En `table` el conjunto de hover
|
|
|
no incluyó ninguna fila, así que el diff no cubría el estado donde estaba el
|
|
|
hallazgo. Hubo que medirlo con un script aparte.
|
|
|
- **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. **Merece un guard**: una declaración de receta que
|
|
|
ninguna cascada puede ganar es deuda muerta, y hoy sólo aparece si alguien
|
|
|
pasa el centinela por ese componente. El instrumento para detectarlo ya
|
|
|
existe (`__theming-sentinel.ts`); falta convertirlo en regla.
|
|
|
- **`--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.
|
|
|
- **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.
|
|
|
|
|
|
**Deps**: ninguna. Son mejoras del instrumental del eje, ejecutables cuando
|
|
|
estorben.
|