From 7fabee19721852216992973c6238a006372a8db8 Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 23 Aug 2026 03:30:53 +0200 Subject: [PATCH] =?UTF-8?q?uix(anchor-nav):=20temable=20=E2=80=94=2020=20%?= =?UTF-8?q?=20=E2=86=92=20100=20%,=20y=20el=20censo=20aprende=20los=20bord?= =?UTF-8?q?es=20POR=20LADO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 11 claves nuevas (2 → 13). Tenía dos knobs y todo lo demás a pelo: la escala del índice (que ahora lee el bundle, no `--font-size-sm` suelto), el relleno y el radio del enlace, sus tres tintas —reposo, hover y activo—, el peso del activo y LAS DOS TINTAS DEL RAÍL, que es lo que da nombre al componente. EL RAÍL ACTIVO ESTABA INVISIBLE PARA EL CENSO. Su color vive en un `border-inline-start-color`, y `KNOB_PROPS` sólo conocía `border-color` y los shorthands: los ocho longhands por lado (`border-{top,bottom,left,right, block-start,block-end,inline-start,inline-end}-{color,width}`) no eran knobs para la medida. El centinela los mira desde el 2026-08-22 —los añadió cuando prose y nav-tree leyeron muertos—; el censo, no. Corregido: +30 FILAS EN EL CATÁLOGO (5.101 → 5.131 knobs), y esta ficha baja de 20 % a 18 % antes de coserse, que es lo honesto: el censo promete sobre-reportar, nunca infra-reportar. La tinta de reposo del raíl estaba escondida de otra manera: dentro del shorthand `border-inline-start: var(--anchor-nav-rail-width) solid var(--color-border-subtle)`, que el censo clasifica por el PRIMER token que casa —el trazo, ya público— y da la fila por alcanzable. No hizo falta partir el shorthand: basta con nombrar el color dentro. Un susto útil: una coma que faltaba en el contrato rompió el build de tokens, y lo que lo delató fue la SONDA midiendo 0 nodos — la página no renderizaba. El recuento de nodos es el canario, tal como dice el handoff. Gates: sonda 0 diffs en 1.760 valores × 7 estados (8 nodos) · centinela 11/12 con el radio del foco adjudicado (el guard desenfoca a propósito) · censo 100 % · `component:audit` PASS · suite eidos con el rojo conocido ajeno · `rtl:check` 0 · `docs:check` 0 · captura del raíl. Co-Authored-By: Claude Opus 5 --- docs/audit/theming/README.md | 44 ++++++------ docs/audit/theming/anchor-nav.md | 69 ++++++++++--------- docs/audit/theming/card.md | 10 +-- docs/audit/theming/code-block.md | 4 +- docs/audit/theming/dialog.md | 12 ++-- docs/audit/theming/drawer.md | 21 ++++-- docs/audit/theming/feed.md | 13 ++-- docs/audit/theming/kbd.md | 4 +- docs/audit/theming/media-player.md | 4 +- docs/audit/theming/search-field.md | 4 +- docs/audit/theming/spinner.md | 15 ++-- docs/audit/theming/tabs.md | 4 +- docs/audit/theming/toast.md | 4 +- docs/process/PLAN-theming.md | 26 +++++++ scripts/theming-census.ts | 2 +- scripts/theming-sentinel-exceptions.ts | 4 ++ .../components/anchor-nav/anchor-nav.css | 20 +++--- src/uix/eidos/generated/base.css | 10 +++ src/uix/eidos/lib/recipes/base.ts | 15 +++- 19 files changed, 184 insertions(+), 101 deletions(-) diff --git a/docs/audit/theming/README.md b/docs/audit/theming/README.md index f57d2c86b..eca790e02 100644 --- a/docs/audit/theming/README.md +++ b/docs/audit/theming/README.md @@ -10,9 +10,9 @@ - **Medido**: 2026-08-23 · **162 recetas** con CSS + **8 componentes sin receta** = 170 fichas, el árbol entero de `eidos/components/` - **La pregunta**: ¿cuánto de la apariencia de cada componente puede cambiar un tema **sin tocar el sistema ni la receta**? -- **Alcance global**: **64%** — 2955 de 4592 knobs pasan por un token público del componente -- **Reparto**: público 2955 · privado 322 · global 863 · literal 452 · sistema transversal 509 · excepción firmada 90 _(los dos últimos, fuera del ratio)_ -- **Sin token público propio**: 24 · **alcance < 20 %**: 14 · **alcance 100 %**: 18 · **con eje `size`**: 58 +- **Alcance global**: **64%** — 2974 de 4622 knobs pasan por un token público del componente +- **Reparto**: público 2974 · privado 326 · global 870 · literal 452 · sistema transversal 509 · excepción firmada 90 _(los dos últimos, fuera del ratio)_ +- **Sin token público propio**: 24 · **alcance < 20 %**: 14 · **alcance 100 %**: 19 · **con eje `size`**: 58 ## Cómo se lee @@ -80,13 +80,13 @@ theming §6.7 lo pone delante (`hover-bg`). Las fichas proponen la forma | 5 | [time-picker](./time-picker.md) | 47 | 18% | 59 | 16 | | 6 | [field-langs](./field-langs.md) | 37 | 0% | 39 | 0 | | 7 | [date-range-picker](./date-range-picker.md) | 30 | 40% | 53 | 22 | -| 8 | [radio-cards](./radio-cards.md) | 27 | 61% | 72 | 40 | -| 9 | [color-picker](./color-picker.md) | 20 | 75% | 82 | 71 | -| 10 | [natural-time-picker](./natural-time-picker.md) | 20 | 74% | 77 | 62 | -| 11 | [navigation-menu](./navigation-menu.md) | 19 | 44% | 38 | 26 | -| 12 | [sidebar](./sidebar.md) | 19 | 55% | 45 | 50 | -| 13 | [mockup](./mockup.md) | 18 | 38% | 29 | 0 | -| 14 | [drawer](./drawer.md) | 17 | 72% | 62 | 54 | +| 8 | [drawer](./drawer.md) | 29 | 60% | 74 | 54 | +| 9 | [radio-cards](./radio-cards.md) | 27 | 61% | 72 | 40 | +| 10 | [color-picker](./color-picker.md) | 20 | 75% | 82 | 71 | +| 11 | [natural-time-picker](./natural-time-picker.md) | 20 | 74% | 77 | 62 | +| 12 | [navigation-menu](./navigation-menu.md) | 19 | 44% | 38 | 26 | +| 13 | [sidebar](./sidebar.md) | 19 | 55% | 45 | 50 | +| 14 | [mockup](./mockup.md) | 18 | 38% | 29 | 0 | | 15 | [radio-group](./radio-group.md) | 17 | 77% | 75 | 61 | | 16 | [popover](./popover.md) | 16 | 67% | 51 | 46 | | 17 | [form](./form.md) | 15 | 71% | 52 | 54 | @@ -121,7 +121,6 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [chronos](./chronos.md) | 1% | 244 | 3 | 21 | 202 | 11 | 7 | 9 | y | | [time-picker](./time-picker.md) | 18% | 59 | 10 | 12 | 24 | 11 | 2 | 16 | y | | [proof-of-human](./proof-of-human.md) | 19% | 79 | 14 | 17 | 29 | 15 | 4 | 20 | – | -| [anchor-nav](./anchor-nav.md) | 20% | 11 | 2 | 0 | 8 | 0 | 1 | 2 | – | | [time-range-picker](./time-range-picker.md) | 20% | 88 | 17 | 19 | 33 | 15 | 4 | 22 | y | | [skin-media-player](./skin-media-player.md) | 22% | 11 | 2 | 0 | 1 | 6 | 2 | 10 | y | | [palabras](./palabras.md) | 22% | 468 | 103 | 1 | 272 | 87 | 5 | 0 | – | @@ -149,6 +148,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [background](./background.md) | 57% | 26 | 13 | 1 | 6 | 3 | 3 | 31 | – | | [s-text-virtual-list](./s-text-virtual-list.md) | 58% | 12 | 7 | 0 | 3 | 2 | 0 | 9 | – | | [badge](./badge.md) | 59% | 22 | 13 | 6 | 1 | 2 | 0 | 65 | y | +| [drawer](./drawer.md) | 60% | 74 | 43 | 8 | 16 | 5 | 2 | 54 | y | | [cropper](./cropper.md) | 60% | 27 | 15 | 0 | 3 | 7 | 2 | 15 | – | | [mark](./mark.md) | 60% | 5 | 3 | 2 | 0 | 0 | 0 | 3 | – | | [emoji-picker](./emoji-picker.md) | 61% | 32 | 17 | 0 | 8 | 3 | 4 | 17 | – | @@ -156,24 +156,23 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [password-field](./password-field.md) | 62% | 35 | 21 | 2 | 2 | 9 | 1 | 30 | y | | [clipboard](./clipboard.md) | 63% | 8 | 5 | 2 | 1 | 0 | 0 | 6 | – | | [spin-field](./spin-field.md) | 64% | 27 | 16 | 0 | 8 | 1 | 2 | 20 | – | +| [spinner](./spinner.md) | 64% | 25 | 16 | 5 | 0 | 4 | 0 | 18 | – | | [meter](./meter.md) | 64% | 14 | 9 | 1 | 2 | 2 | 0 | 28 | y | | [pin-input](./pin-input.md) | 65% | 18 | 11 | 0 | 6 | 0 | 1 | 20 | y | | [tag-group](./tag-group.md) | 65% | 46 | 28 | 14 | 0 | 1 | 3 | 91 | y | | [popover](./popover.md) | 67% | 51 | 32 | 4 | 12 | 0 | 3 | 46 | y | +| [dialog](./dialog.md) | 67% | 36 | 24 | 4 | 7 | 1 | 0 | 37 | y | | [image-adjustments](./image-adjustments.md) | 67% | 18 | 10 | 0 | 4 | 1 | 3 | 12 | – | | [virtual-list](./virtual-list.md) | 67% | 13 | 8 | 0 | 0 | 4 | 1 | 12 | – | | [separator](./separator.md) | 67% | 6 | 4 | 0 | 0 | 2 | 0 | 3 | – | | [listbox](./listbox.md) | 68% | 37 | 23 | 9 | 1 | 1 | 3 | 21 | – | | [progress](./progress.md) | 69% | 35 | 24 | 2 | 3 | 6 | 0 | 41 | y | -| [search-field](./search-field.md) | 69% | 14 | 9 | 0 | 0 | 4 | 1 | 17 | y | -| [spinner](./spinner.md) | 70% | 23 | 16 | 3 | 0 | 4 | 0 | 18 | – | | [color-swatch](./color-swatch.md) | 70% | 10 | 7 | 0 | 3 | 0 | 0 | 12 | y | | [form](./form.md) | 71% | 52 | 36 | 8 | 5 | 2 | 1 | 54 | y | | [image-picker](./image-picker.md) | 71% | 20 | 12 | 0 | 1 | 4 | 3 | 13 | – | +| [search-field](./search-field.md) | 71% | 15 | 10 | 0 | 0 | 4 | 1 | 17 | y | | [skip-link](./skip-link.md) | 71% | 8 | 5 | 0 | 0 | 2 | 1 | 8 | – | -| [drawer](./drawer.md) | 72% | 62 | 43 | 8 | 4 | 5 | 2 | 54 | y | | [checkbox](./checkbox.md) | 72% | 30 | 21 | 8 | 0 | 0 | 1 | 47 | y | -| [dialog](./dialog.md) | 73% | 33 | 24 | 4 | 4 | 1 | 0 | 37 | y | | [virtual-grid](./virtual-grid.md) | 73% | 12 | 8 | 0 | 0 | 3 | 1 | 12 | – | | [natural-time-picker](./natural-time-picker.md) | 74% | 77 | 56 | 0 | 14 | 6 | 1 | 62 | y | | [color-picker](./color-picker.md) | 75% | 82 | 59 | 7 | 2 | 11 | 3 | 71 | y | @@ -185,16 +184,16 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [carousel](./carousel.md) | 77% | 35 | 23 | 2 | 0 | 5 | 5 | 27 | – | | [radio-group](./radio-group.md) | 77% | 75 | 56 | 10 | 7 | 0 | 2 | 61 | y | | [chat-log](./chat-log.md) | 77% | 42 | 30 | 0 | 4 | 5 | 3 | 31 | – | +| [card](./card.md) | 78% | 37 | 28 | 8 | 0 | 0 | 1 | 97 | y | | [knob](./knob.md) | 78% | 26 | 18 | 1 | 1 | 3 | 3 | 33 | – | | [stepper](./stepper.md) | 79% | 60 | 44 | 11 | 0 | 1 | 4 | 69 | y | | [callout](./callout.md) | 79% | 14 | 11 | 3 | 0 | 0 | 0 | 34 | – | | [chat-composer](./chat-composer.md) | 79% | 44 | 31 | 0 | 3 | 5 | 5 | 31 | – | -| [card](./card.md) | 80% | 36 | 28 | 7 | 0 | 0 | 1 | 97 | y | | [aura](./aura.md) | 80% | 35 | 28 | 0 | 1 | 6 | 0 | 37 | y | | [textarea](./textarea.md) | 80% | 28 | 20 | 2 | 0 | 3 | 3 | 37 | – | | [rating-group](./rating-group.md) | 80% | 16 | 12 | 0 | 1 | 2 | 1 | 25 | y | -| [code-block](./code-block.md) | 81% | 26 | 21 | 2 | 3 | 0 | 0 | 20 | – | | [toggle](./toggle.md) | 81% | 28 | 22 | 1 | 1 | 3 | 1 | 119 | y | +| [code-block](./code-block.md) | 81% | 27 | 22 | 2 | 3 | 0 | 0 | 20 | – | | [metrics](./metrics.md) | 82% | 50 | 41 | 5 | 0 | 4 | 0 | 72 | y | | [prose](./prose.md) | 83% | 53 | 44 | 0 | 0 | 9 | 0 | 37 | y | | [select](./select.md) | 83% | 72 | 54 | 5 | 3 | 3 | 7 | 101 | y | @@ -204,12 +203,12 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [chat-message](./chat-message.md) | 84% | 84 | 65 | 0 | 6 | 6 | 7 | 75 | y | | [grid-list](./grid-list.md) | 85% | 31 | 22 | 2 | 1 | 1 | 5 | 38 | – | | [combobox](./combobox.md) | 85% | 97 | 76 | 4 | 3 | 6 | 8 | 106 | – | -| [kbd](./kbd.md) | 86% | 14 | 12 | 0 | 1 | 1 | 0 | 13 | – | | [table](./table.md) | 86% | 46 | 37 | 3 | 0 | 3 | 3 | 45 | – | | [context-menu](./context-menu.md) | 87% | 33 | 26 | 0 | 1 | 3 | 3 | 28 | – | +| [kbd](./kbd.md) | 87% | 15 | 13 | 0 | 1 | 1 | 0 | 13 | – | | [fab](./fab.md) | 88% | 16 | 14 | 0 | 2 | 0 | 0 | 10 | – | | [announce](./announce.md) | 88% | 8 | 7 | 0 | 1 | 0 | 0 | 9 | – | -| [media-player](./media-player.md) | 88% | 49 | 43 | 0 | 0 | 6 | 0 | 61 | – | +| [media-player](./media-player.md) | 88% | 50 | 44 | 0 | 0 | 6 | 0 | 61 | – | | [tags-input](./tags-input.md) | 88% | 46 | 37 | 4 | 1 | 0 | 4 | 90 | y | | [gradient-picker](./gradient-picker.md) | 88% | 27 | 23 | 0 | 2 | 1 | 1 | 25 | – | | [menubar](./menubar.md) | 88% | 27 | 23 | 0 | 3 | 0 | 1 | 35 | y | @@ -221,19 +220,19 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [result](./result.md) | 90% | 21 | 19 | 0 | 0 | 2 | 0 | 27 | – | | [accordion](./accordion.md) | 91% | 65 | 58 | 1 | 0 | 5 | 1 | 76 | y | | [toolbar](./toolbar.md) | 91% | 35 | 30 | 2 | 0 | 1 | 2 | 54 | y | +| [feed](./feed.md) | 91% | 34 | 30 | 1 | 1 | 1 | 1 | 50 | – | | [button](./button.md) | 91% | 23 | 20 | 2 | 0 | 0 | 1 | 106 | y | | [tree-view](./tree-view.md) | 92% | 28 | 24 | 1 | 0 | 1 | 2 | 46 | – | | [gradient-builder](./gradient-builder.md) | 93% | 60 | 52 | 0 | 1 | 3 | 4 | 72 | – | -| [feed](./feed.md) | 94% | 33 | 30 | 0 | 1 | 1 | 1 | 50 | – | | [scroll-area](./scroll-area.md) | 94% | 17 | 15 | 0 | 0 | 1 | 1 | 13 | – | | [tree-grid](./tree-grid.md) | 95% | 42 | 37 | 1 | 0 | 1 | 3 | 47 | – | | [picker-shell](./picker-shell.md) | 95% | 21 | 20 | 0 | 1 | 0 | 0 | 31 | y | | [nav-tree](./nav-tree.md) | 96% | 24 | 22 | 0 | 0 | 1 | 1 | 18 | – | | [image](./image.md) | 96% | 23 | 22 | 1 | 0 | 0 | 0 | 23 | y | -| [tabs](./tabs.md) | 96% | 76 | 72 | 0 | 2 | 1 | 1 | 79 | y | +| [tabs](./tabs.md) | 96% | 78 | 74 | 0 | 2 | 1 | 1 | 79 | y | | [dropdown-menu](./dropdown-menu.md) | 96% | 30 | 27 | 0 | 1 | 0 | 2 | 28 | – | | [slider](./slider.md) | 97% | 33 | 31 | 0 | 0 | 1 | 1 | 39 | y | -| [toast](./toast.md) | 98% | 42 | 41 | 0 | 0 | 1 | 0 | 85 | – | +| [toast](./toast.md) | 98% | 46 | 45 | 0 | 0 | 1 | 0 | 85 | – | | [command](./command.md) | 98% | 52 | 49 | 0 | 0 | 1 | 2 | 57 | – | | [field](./field.md) | 98% | 64 | 58 | 1 | 0 | 0 | 5 | 68 | y | | [heading](./heading.md) | — | 36 | 0 | 0 | 0 | 0 | 36 | 0 | – | @@ -246,6 +245,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [empty-state](./empty-state.md) | 100% | 17 | 17 | 0 | 0 | 0 | 0 | 26 | y | | [link-preview](./link-preview.md) | 100% | 17 | 14 | 0 | 0 | 0 | 3 | 35 | – | | [code](./code.md) | 100% | 13 | 7 | 0 | 0 | 0 | 6 | 6 | – | +| [anchor-nav](./anchor-nav.md) | 100% | 12 | 11 | 0 | 0 | 0 | 1 | 12 | – | | [date-field](./date-field.md) | 100% | 11 | 11 | 0 | 0 | 0 | 0 | 21 | y | | [time-field](./time-field.md) | 100% | 11 | 11 | 0 | 0 | 0 | 0 | 21 | y | | [text-gradient](./text-gradient.md) | 100% | 10 | 10 | 0 | 0 | 0 | 0 | 7 | – | diff --git a/docs/audit/theming/anchor-nav.md b/docs/audit/theming/anchor-nav.md index 51aa1a00b..8d64b38c0 100644 --- a/docs/audit/theming/anchor-nav.md +++ b/docs/audit/theming/anchor-nav.md @@ -5,25 +5,16 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **20%** — 2 de 10 knobs por token público -- **Knobs de apariencia**: 11 — público 2 · privado 0 · global 8 · literal 0 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ -- **Contrato hoy** (`lib/recipes/base.ts`): 2 pública(s) — `indent`, `rail-width` +- **Medido**: 2026-08-23 · **Alcance**: **100%** — 11 de 11 knobs por token público +- **Knobs de apariencia**: 12 — público 11 · privado 0 · global 0 · literal 0 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Contrato hoy** (`lib/recipes/base.ts`): 12 pública(s) — `indent`, `rail-width`, `rail-fg`, `active-rail-fg`, `font-size`, `line-height`, `link-padding-block`, `link-radius`, `link-fg`, `hover-link-fg`, `active-link-fg`, `active-link-font-weight` - **Eje `size`**: no · **ficheros**: `anchor-nav.css` ## 1. Knobs fuera de alcance -### 1.1 Directo a primitivo global (8) +### 1.1 Directo a primitivo global (0) -| # | fichero:línea | selector | propiedad | valor | -| ---: | --- | --- | --- | --- | -| 1 | `anchor-nav.css:16` | `[data-anchor-nav]` | `font-size` | `var(--font-size-sm)` | -| 2 | `anchor-nav.css:17` | `[data-anchor-nav]` | `line-height` | `var(--font-line-height-sm)` | -| 3 | `anchor-nav.css:28` | `[data-anchor-nav-link]` | `padding-block` | `var(--space-1)` | -| 4 | `anchor-nav.css:33` | `[data-anchor-nav-link]` | `color` | `var(--color-content-muted)` | -| 5 | `anchor-nav.css:42` | `[data-anchor-nav-link]:hover` | `color` | `var(--color-content-primary)` | -| 6 | `anchor-nav.css:46` | `[data-anchor-nav-link][data-active]` | `color` | `var(--color-primary-text)` | -| 7 | `anchor-nav.css:48` | `[data-anchor-nav-link][data-active]` | `font-weight` | `var(--font-weight-medium)` | -| 8 | `anchor-nav.css:55` | `[data-anchor-nav-link]:focus-visible` | `border-radius` | `var(--radius-sm)` | +_Ninguno._ ### 1.2 A través de un privado (0) @@ -57,23 +48,7 @@ Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2). ## 4. Propuesta de corrección -### 4.1 Tokens a declarar en `lib/recipes/base.ts` (7) - -Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién -puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla -al final) y theming §6.7 (slots de color, modificador delante). Un token con -DOS valores distintos es una colisión de nombre: son dos knobs, o el nombre -no distingue lo que debería — se marca `⚠`. - -| token (`--anchor-nav-…`) | scope TSC | valor propuesto | usos | -| --- | --- | --- | ---: | -| `link-fg` | `root` | ⚠ `var(--color-content-muted)` / `var(--color-primary-text)` | 2 | -| `font-size` | `root` | `var(--font-size-sm)` | 1 | -| `line-height` | `root` | `var(--font-line-height-sm)` | 1 | -| `link-padding-block` | `root` | `var(--space-1)` | 1 | -| `hover-link-fg` | `root` | `var(--color-content-primary)` | 1 | -| `link-font-weight` | `root` | `var(--font-weight-medium)` | 1 | -| `link-radius` | `root` | `var(--radius-sm)` | 1 | +_Nada que proponer: no hay knobs fuera de alcance._ ### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4) @@ -87,6 +62,36 @@ no distingue lo que debería — se marca `⚠`. -_(pendiente — lo escribe el autor; se conserva al regenerar)_ +**EJECUTADO 2026-08-23 — 20 % → 100 %**, 11 claves nuevas (2 → 13). Lo único +fuera del ratio es el anillo de foco, que es del sistema. + +Tenía DOS knobs y todo lo demás a pelo: la escala del índice (que ahora lee el +bundle, no `--font-size-sm` suelto), el relleno y el radio del enlace, sus tres +tintas —reposo, hover y activo—, el peso del activo y **las dos tintas del +RAÍL**, que es lo que da nombre al componente. + +**El raíl activo estaba INVISIBLE para el censo.** Su color vive en un +`border-inline-start-color`, y `KNOB_PROPS` sólo conocía `border-color` y los +shorthands: los ocho longhands por lado (`border-{top,bottom,left,right, +block-start,block-end,inline-start,inline-end}-{color,width}`) no eran knobs para +la medida. El centinela los mira desde el 2026-08-22 —los añadió cuando prose y +nav-tree leyeron muertos—; el censo, no. **Corregido: +30 filas en el catálogo** +(5.101 → 5.131 knobs), y esta ficha baja de 20 % a 18 % antes de coserse, que es +lo honesto: el censo promete sobre-reportar, nunca infra-reportar. + +La tinta de reposo del raíl estaba escondida de otra manera: dentro del +shorthand `border-inline-start: var(--anchor-nav-rail-width) solid +var(--color-border-subtle)`, que el censo clasifica por el PRIMER token que casa +—el trazo, ya público— y da la fila por alcanzable. No hizo falta partir el +shorthand: basta con nombrar el color dentro. + +Verificación: **diff de computed = 0** sobre 1.760 valores en 7 estados (8 +nodos) · centinela **11/12**, el radio del foco adjudicado (el guard desenfoca a +propósito) · censo 100 % · `component:audit` PASS · suite eidos con el rojo +conocido ajeno · `rtl:check` 0 · `docs:check` 0 · captura del raíl. + +**Y un susto útil**: una coma que faltaba en el contrato rompió el build de +tokens, y lo que lo delató fue la sonda midiendo **0 nodos** — la página no +renderizaba. El recuento de nodos es el canario, tal como dice el handoff. diff --git a/docs/audit/theming/card.md b/docs/audit/theming/card.md index f72efce09..d98e7d7dd 100644 --- a/docs/audit/theming/card.md +++ b/docs/audit/theming/card.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **80%** — 28 de 35 knobs por token público -- **Knobs de apariencia**: 36 — público 28 · privado 7 · global 0 · literal 0 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **78%** — 28 de 36 knobs por token público +- **Knobs de apariencia**: 37 — público 28 · privado 8 · global 0 · literal 0 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 97 pública(s) — `padding-inline-xs`, `padding-inline-sm`, `padding-inline-md`, `padding-inline-lg`, `padding-inline-xl`, `padding-block-xs`, `padding-block-sm`, `padding-block-md`, `padding-block-lg`, `padding-block-xl`, `gap-xs`, `gap-sm`, `gap-md`, `gap-lg`, `gap-xl`, `font-family`, `line-height`, `title-font-family`, `title-font-size-xs`, `title-font-size-sm`, `title-font-size-md`, `title-font-size-lg`, `title-font-size-xl`, `title-font-weight`, `title-line-height`, `title-letter-spacing`, `description-font-family`, `description-font-size-xs`, `description-font-size-sm`, `description-font-size-md`, `description-font-size-lg`, `description-font-size-xl`, `description-line-height`, `description-fg`, `body-font-size-xs`, `body-font-size-sm`, `body-font-size-md`, `body-font-size-lg`, `body-font-size-xl`, `body-line-height`, `border-width`, `radius-sm`, `radius-md`, `radius-lg`, `header-gap`, `footer-gap`, `footer-padding-start`, `footer-border-fg`, `emerge-duration`, `emerge-ease`, `emerge-distance`, `hover-lift`, `hover-shadow`, `press-scale`, `press-duration`, `selected-ring-width`, `disabled-opacity`, `primary-solid`, `primary-text`, `primary-contrast`, `secondary-solid`, `secondary-text`, `secondary-contrast`, `neutral-solid`, `neutral-text`, `neutral-contrast`, `affirm-solid`, `affirm-text`, `affirm-contrast`, `fulfill-track`, `fulfill-border`, `fulfill-solid`, `fulfill-text`, `fulfill-contrast`, `risk-solid`, `risk-text`, `risk-contrast`, `threat-solid`, `threat-text`, `threat-contrast`, `loss-track`, `loss-border`, `loss-solid`, `loss-text`, `loss-contrast`, `primary-track`, `primary-border`, `secondary-track`, `secondary-border`, `neutral-track`, `neutral-border`, `affirm-track`, `affirm-border`, `risk-track`, `risk-border`, `threat-track`, `threat-border` · 5 privada(s) forward — `_palette-track`, `_palette-border`, `_palette-solid`, `_palette-text`, `_palette-contrast` - **Eje `size`**: sí · **ficheros**: `card.css` @@ -16,7 +16,7 @@ _Ninguno._ -### 1.2 A través de un privado (7) +### 1.2 A través de un privado (8) | # | fichero:línea | selector | propiedad | valor | | ---: | --- | --- | --- | --- | @@ -27,6 +27,7 @@ _Ninguno._ | 5 | `card.css:293` | `[data-card-title]` | `color` | `var(--_card-fg)` | | 6 | `card.css:309` | `[data-card][data-variant='solid'] [data-card-description]` | `color` | `color-mix(in srgb, var(--_card-palette-contrast) 80%, transparent)` | | 7 | `card.css:317` | `[data-card-body]` | `color` | `var(--_card-fg)` | +| 8 | `card.css:333` | `[data-card][data-variant='solid'] [data-card-footer]` | `border-block-start-color` | `color-mix(in srgb, var(--_card-palette-contrast) 18%, transparent)` | ### 1.3 Literales (0) @@ -84,9 +85,10 @@ no distingue lo que debería — se marca `⚠`. | `title-fg` | `root` | ⚠ `var(--_card-palette-text)` / `var(--_card-palette-contrast)` | 5 | | `body-fg` | `root` | ⚠ `var(--_card-palette-text)` / `var(--_card-palette-contrast)` | 5 | -### 4.2 Sin nombre mecánico (3) +### 4.2 Sin nombre mecánico (4) - **⚠ decisión: el privado que alimenta este knob no se declara en el CSS (viene de `base.ts` o de un estilo inline) — hay que resolverlo antes de nombrarlo** — 3: `background`, `color`. +- **⚠ decisión: la propiedad no tiene slot canónico en el vocabulario** — 1: `border-block-start-color`. ### 4.3 Avisos sobre los tokens propuestos (2) diff --git a/docs/audit/theming/code-block.md b/docs/audit/theming/code-block.md index 7ec710da2..b5a7a8495 100644 --- a/docs/audit/theming/code-block.md +++ b/docs/audit/theming/code-block.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **81%** — 21 de 26 knobs por token público -- **Knobs de apariencia**: 26 — público 21 · privado 2 · global 3 · literal 0 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **81%** — 22 de 27 knobs por token público +- **Knobs de apariencia**: 27 — público 22 · privado 2 · global 3 · literal 0 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 20 pública(s) — `radius`, `fg`, `border-width`, `border`, `surface-bg`, `header-gap`, `header-padding-inline`, `header-padding-block`, `header-bg`, `title-font-family`, `title-font-size`, `title-fg`, `lang-font-family`, `lang-font-size`, `lang-fg`, `lang-letter-spacing`, `copy-gap`, `copy-font-size`, `copy-fg`, `pre-padding` - **Eje `size`**: no · **ficheros**: `code-block.css` diff --git a/docs/audit/theming/dialog.md b/docs/audit/theming/dialog.md index 55623a697..ff3082db1 100644 --- a/docs/audit/theming/dialog.md +++ b/docs/audit/theming/dialog.md @@ -5,14 +5,14 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **73%** — 24 de 33 knobs por token público -- **Knobs de apariencia**: 33 — público 24 · privado 4 · global 4 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **67%** — 24 de 36 knobs por token público +- **Knobs de apariencia**: 36 — público 24 · privado 4 · global 7 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 37 pública(s) — `overlay-bg`, `overlay-opacity`, `overlay-blur`, `overlay-z`, `content-z`, `content-fg`, `content-radius`, `content-max-height`, `content-max-height-sheet`, `content-width-sm`, `content-width-md`, `content-width-lg`, `content-width-xl`, `content-width-inset`, `content-full-inset`, `content-full-radius`, `content-position-inset`, `content-padding-sm`, `content-padding-md`, `content-padding-lg`, `content-padding-full`, `stack-gap`, `header-gap`, `footer-gap`, `footer-margin-top`, `title-font-family`, `title-font-size`, `title-font-weight`, `title-line-height`, `title-letter-spacing`, `title-fg`, `description-font-family`, `description-font-size`, `description-line-height`, `description-fg`, `close-size`, `close-inset` - **Eje `size`**: sí · **ficheros**: `dialog.css` ## 1. Knobs fuera de alcance -### 1.1 Directo a primitivo global (4) +### 1.1 Directo a primitivo global (7) | # | fichero:línea | selector | propiedad | valor | | ---: | --- | --- | --- | --- | @@ -20,6 +20,9 @@ | 2 | `dialog.css:217` | `[data-dialog-content][data-color='risk'] [data-dialog-title]` | `color` | `var(--color-risk-text)` | | 3 | `dialog.css:221` | `[data-dialog-content][data-color='threat']` | `border-color` | `var(--color-threat-border)` | | 4 | `dialog.css:225` | `[data-dialog-content][data-color='threat'] [data-dialog-title]` | `color` | `var(--color-threat-text)` | +| 5 | `dialog.css:334` | `[data-dialog-content][data-state='closed'][data-last-action='saved']` | `border-top-color` | `var(--color-fulfill-element)` | +| 6 | `dialog.css:337` | `[data-dialog-content][data-state='closed'][data-last-action='failed']` | `border-top-color` | `var(--color-threat-element)` | +| 7 | `dialog.css:342` | `[data-dialog-content][data-state='closed'][data-last-action='cancelled'], [data-dialog-content][data-state='closed'][data-last-action='dismissed'], [data-dialog-content][data-state='closed'][data-last-action='dismissed-outside']` | `border-top-color` | `var(--color-neutral-element)` | ### 1.2 A través de un privado (4) @@ -77,12 +80,13 @@ no distingue lo que debería — se marca `⚠`. | `content-border` | `color:risk` | ⚠ `var(--color-risk-border)` / `var(--color-threat-border)` | 2 | | `content-fg` _(ya existe)_ | `color:risk` | ⚠ `var(--color-risk-text)` / `var(--color-threat-text)` | 2 | -### 4.2 Sin nombre mecánico (5) +### 4.2 Sin nombre mecánico (8) - **⚠ decisión: `min-width` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `min-width`. - **⚠ decisión: `height` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `height`. - **⚠ decisión: `min-height` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 1: `min-height`. - **⚠ decisión: `width` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 2: `width`. +- **⚠ decisión: la propiedad no tiene slot canónico en el vocabulario** — 3: `border-top-color`. ### 4.3 Avisos sobre los tokens propuestos (2) diff --git a/docs/audit/theming/drawer.md b/docs/audit/theming/drawer.md index e83ea85b1..cd27094a2 100644 --- a/docs/audit/theming/drawer.md +++ b/docs/audit/theming/drawer.md @@ -5,14 +5,14 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **72%** — 43 de 60 knobs por token público -- **Knobs de apariencia**: 62 — público 43 · privado 8 · global 4 · literal 5 · sistema 2 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **60%** — 43 de 72 knobs por token público +- **Knobs de apariencia**: 74 — público 43 · privado 8 · global 16 · literal 5 · sistema 2 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 54 pública(s) — `trigger-gap`, `trigger-min-block-size`, `trigger-padding-inline`, `trigger-border-width`, `trigger-border`, `trigger-radius`, `trigger-bg`, `trigger-fg`, `trigger-font-weight`, `hover-trigger-bg`, `overlay-bg`, `overlay-blur`, `overlay-opacity`, `overlay-z`, `content-z`, `inline-z`, `handle-bg`, `active-handle-bg`, `handle-radius`, `handle-length`, `handle-thickness`, `handle-active-scale`, `handle-transition-duration`, `handle-transition-ease`, `content-fg`, `content-radius`, `content-shadow-dragging`, `content-ring-dragging-width`, `content-ring-dragging-fg`, `content-ring-dragging-offset`, `content-inset`, `content-width-sm`, `content-width-md`, `content-width-lg`, `content-height-sm`, `content-height-md`, `content-height-lg`, `content-padding-sm`, `content-padding-md`, `content-padding-lg`, `content-padding-full`, `stack-gap`, `header-gap`, `footer-gap`, `footer-margin-top`, `title-font-family`, `title-font-size`, `title-font-weight`, `title-line-height`, `title-fg`, `description-font-family`, `description-font-size`, `description-line-height`, `description-fg` - **Eje `size`**: sí · **ficheros**: `drawer.css` ## 1. Knobs fuera de alcance -### 1.1 Directo a primitivo global (4) +### 1.1 Directo a primitivo global (16) | # | fichero:línea | selector | propiedad | valor | | ---: | --- | --- | --- | --- | @@ -20,6 +20,18 @@ | 2 | `drawer.css:113` | `[data-drawer-content][data-side='left']` | `border-right` | `var(--border-width-thick) solid transparent` | | 3 | `drawer.css:116` | `[data-drawer-content][data-side='top']` | `border-bottom` | `var(--border-width-thick) solid transparent` | | 4 | `drawer.css:119` | `[data-drawer-content][data-side='bottom']` | `border-top` | `var(--border-width-thick) solid transparent` | +| 5 | `drawer.css:291` | `[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='saved']` | `border-left-color` | `var(--color-fulfill-element)` | +| 6 | `drawer.css:292` | `[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='saved']` | `border-right-color` | `var(--color-fulfill-element)` | +| 7 | `drawer.css:293` | `[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='saved']` | `border-bottom-color` | `var(--color-fulfill-element)` | +| 8 | `drawer.css:294` | `[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='saved']` | `border-top-color` | `var(--color-fulfill-element)` | +| 9 | `drawer.css:296` | `[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='failed']` | `border-left-color` | `var(--color-threat-element)` | +| 10 | `drawer.css:297` | `[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='failed']` | `border-right-color` | `var(--color-threat-element)` | +| 11 | `drawer.css:298` | `[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='failed']` | `border-bottom-color` | `var(--color-threat-element)` | +| 12 | `drawer.css:299` | `[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='failed']` | `border-top-color` | `var(--color-threat-element)` | +| 13 | `drawer.css:303` | `[data-drawer-content][data-state='closed'][data-side='right'][data-last-action='cancelled'], [data-drawer-content][data-state='closed'][data-side='right'][data-last-action='dismissed'], [data-drawer-content][data-state='closed'][data-side='right'][data-last-action='dismissed-outside']` | `border-left-color` | `var(--color-neutral-element)` | +| 14 | `drawer.css:307` | `[data-drawer-content][data-state='closed'][data-side='left'][data-last-action='cancelled'], [data-drawer-content][data-state='closed'][data-side='left'][data-last-action='dismissed'], [data-drawer-content][data-state='closed'][data-side='left'][data-last-action='dismissed-outside']` | `border-right-color` | `var(--color-neutral-element)` | +| 15 | `drawer.css:311` | `[data-drawer-content][data-state='closed'][data-side='top'][data-last-action='cancelled'], [data-drawer-content][data-state='closed'][data-side='top'][data-last-action='dismissed'], [data-drawer-content][data-state='closed'][data-side='top'][data-last-action='dismissed-outside']` | `border-bottom-color` | `var(--color-neutral-element)` | +| 16 | `drawer.css:315` | `[data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='cancelled'], [data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='dismissed'], [data-drawer-content][data-state='closed'][data-side='bottom'][data-last-action='dismissed-outside']` | `border-top-color` | `var(--color-neutral-element)` | ### 1.2 A través de un privado (8) @@ -88,7 +100,7 @@ no distingue lo que debería — se marca `⚠`. | token (`--drawer-…`) | scope TSC | valor propuesto | usos | | --- | --- | --- | ---: | -### 4.2 Sin nombre mecánico (17) +### 4.2 Sin nombre mecánico (29) - **⚠ decisión: `1` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 1: `opacity`. - **⚠ decisión: el privado que alimenta este knob no se declara en el CSS (viene de `base.ts` o de un estilo inline) — hay que resolverlo antes de nombrarlo** — 2: `min-inline-size`, `min-block-size`. @@ -99,6 +111,7 @@ no distingue lo que debería — se marca `⚠`. - **⚠ decisión: `width` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 4: `width`. - **⚠ decisión: `height` es shorthand o eje físico — hay que partirlo en ejes lógicos antes de nombrarlo (recipe-contract §1, R-4.4)** — 4: `height`. - **⚠ decisión: `100%` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 2: `block-size`, `inline-size`. +- **⚠ decisión: la propiedad no tiene slot canónico en el vocabulario** — 12: `border-left-color`, `border-right-color`, `border-bottom-color`, `border-top-color`. ### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4) diff --git a/docs/audit/theming/feed.md b/docs/audit/theming/feed.md index 596d8b769..3c9695db8 100644 --- a/docs/audit/theming/feed.md +++ b/docs/audit/theming/feed.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **94%** — 30 de 32 knobs por token público -- **Knobs de apariencia**: 33 — público 30 · privado 0 · global 1 · literal 1 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **91%** — 30 de 33 knobs por token público +- **Knobs de apariencia**: 34 — público 30 · privado 1 · global 1 · literal 1 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 50 pública(s) — `font-size-xs`, `font-size-sm`, `font-size-md`, `font-size-lg`, `font-size-xl`, `article-padding-block-xs`, `article-padding-block-sm`, `article-padding-block-md`, `article-padding-block-lg`, `article-padding-block-xl`, `article-padding-inline-xs`, `article-padding-inline-sm`, `article-padding-inline-md`, `article-padding-inline-lg`, `article-padding-inline-xl`, `thread-indent-xs`, `thread-indent-sm`, `thread-indent-md`, `thread-indent-lg`, `thread-indent-xl`, `font-size`, `article-padding-block`, `article-padding-inline`, `thread-indent`, `radius`, `font-family`, `line-height`, `fg`, `border-width`, `divider`, `gap`, `surface-bg`, `article-gap`, `article-title-fg`, `article-title-font-weight`, `article-title-font-size-3`, `article-title-font-size-4`, `article-title-font-size-5`, `article-title-font-size-6`, `article-description-fg`, `thread-margin-block-start`, `thread-rail-width`, `sentinel-min-block-size`, `sentinel-fg`, `sentinel-font-size`, `sentinel-spinner-size`, `sentinel-spinner-width`, `sentinel-spinner-gap`, `sentinel-spinner-duration`, `sentinel-spinner-reduced-duration` · 2 privada(s) forward — `_palette-solid`, `_palette-element` - **Eje `size`**: no · **ficheros**: `feed.css` @@ -18,9 +18,11 @@ | ---: | --- | --- | --- | --- | | 1 | `feed.css:155` | `[data-feed][data-busy] [data-feed-sentinel]::before` | `border-radius` | `var(--radius-full)` | -### 1.2 A través de un privado (0) +### 1.2 A través de un privado (1) -_Ninguno._ +| # | fichero:línea | selector | propiedad | valor | +| ---: | --- | --- | --- | --- | +| 1 | `feed.css:154` | `[data-feed][data-busy] [data-feed-sentinel]::before` | `border-block-start-color` | `var(--_feed-palette-solid)` | ### 1.3 Literales (1) @@ -64,9 +66,10 @@ no distingue lo que debería — se marca `⚠`. | --- | --- | --- | ---: | | `sentinel-radius` | `root` | `var(--radius-full)` | 1 | -### 4.2 Sin nombre mecánico (1) +### 4.2 Sin nombre mecánico (2) - **⚠ decisión: `100%` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 1: `inline-size`. +- **⚠ decisión: la propiedad no tiene slot canónico en el vocabulario** — 1: `border-block-start-color`. ### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4) diff --git a/docs/audit/theming/kbd.md b/docs/audit/theming/kbd.md index 5d60991a1..dda4a3347 100644 --- a/docs/audit/theming/kbd.md +++ b/docs/audit/theming/kbd.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **86%** — 12 de 14 knobs por token público -- **Knobs de apariencia**: 14 — público 12 · privado 0 · global 1 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **87%** — 13 de 15 knobs por token público +- **Knobs de apariencia**: 15 — público 13 · privado 0 · global 1 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 13 pública(s) — `font-size`, `font-weight`, `letter-spacing`, `fg`, `radius`, `padding-inline`, `padding-block`, `min-width`, `border-width`, `border`, `surface-bg`, `surface-border-block-end-width`, `surface-shadow` - **Eje `size`**: no · **ficheros**: `kbd.css` diff --git a/docs/audit/theming/media-player.md b/docs/audit/theming/media-player.md index 9c657d33f..cee58864a 100644 --- a/docs/audit/theming/media-player.md +++ b/docs/audit/theming/media-player.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **88%** — 43 de 49 knobs por token público -- **Knobs de apariencia**: 49 — público 43 · privado 0 · global 0 · literal 6 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **88%** — 44 de 50 knobs por token público +- **Knobs de apariencia**: 50 — público 44 · privado 0 · global 0 · literal 6 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 61 pública(s) — `accent`, `accent-strong`, `bg`, `radius`, `fg`, `fg-muted`, `glass`, `border`, `scrim`, `scrim-height`, `track`, `buffered-bg`, `captions-bg`, `captions-fg`, `captions-radius`, `captions-gap`, `captions-padding-block`, `captions-padding-inline`, `title-font-size`, `title-font-weight`, `title-padding-block`, `title-padding-inline`, `buffering-size`, `buffering-border-width`, `gap-sm`, `gap-md`, `gap-lg`, `gap-xl`, `padding-block-sm`, `padding-block-md`, `padding-block-lg`, `padding-block-xl`, `padding-inline-sm`, `padding-inline-md`, `padding-inline-lg`, `padding-inline-xl`, `time-font-size-sm`, `time-font-size-md`, `time-font-size-lg`, `time-font-size-xl`, `control-height-sm`, `control-height-md`, `control-height-lg`, `control-height-xl`, `gap`, `padding-block`, `padding-inline`, `time-font-size`, `control-height`, `volume-width`, `volume-vertical-length`, `time-slider-min-width`, `rate-button-min-width`, `buffering-radius`, `captions-line-height`, `time-font-family`, `time-padding-inline`, `audio-title-padding-block`, `audio-title-padding-inline`, `volume-float-padding-block`, `volume-float-padding-inline` - **Eje `size`**: no · **ficheros**: `media-player.css` diff --git a/docs/audit/theming/search-field.md b/docs/audit/theming/search-field.md index 45f570834..993dc421d 100644 --- a/docs/audit/theming/search-field.md +++ b/docs/audit/theming/search-field.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **69%** — 9 de 13 knobs por token público -- **Knobs de apariencia**: 14 — público 9 · privado 0 · global 0 · literal 4 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **71%** — 10 de 14 knobs por token público +- **Knobs de apariencia**: 15 — público 10 · privado 0 · global 0 · literal 4 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 17 pública(s) — `clear-hidden-opacity`, `icon-size-xs`, `icon-size-sm`, `icon-size-md`, `icon-size-lg`, `icon-size-xl`, `icon-fg`, `icon-margin-inline-end`, `loading-indicator-size-xs`, `loading-indicator-size-sm`, `loading-indicator-size-md`, `loading-indicator-size-lg`, `loading-indicator-size-xl`, `loading-indicator-fg`, `loading-indicator-track`, `loading-indicator-thickness`, `loading-indicator-duration` - **Eje `size`**: sí · **ficheros**: `search-field.css` diff --git a/docs/audit/theming/spinner.md b/docs/audit/theming/spinner.md index d3c68652c..f9bd1cdbc 100644 --- a/docs/audit/theming/spinner.md +++ b/docs/audit/theming/spinner.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **70%** — 16 de 23 knobs por token público -- **Knobs de apariencia**: 23 — público 16 · privado 3 · global 0 · literal 4 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **64%** — 16 de 25 knobs por token público +- **Knobs de apariencia**: 25 — público 16 · privado 5 · global 0 · literal 4 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 18 pública(s) — `size-xs`, `size-sm`, `size-md`, `size-lg`, `size-xl`, `thickness-xs`, `thickness-sm`, `thickness-md`, `thickness-lg`, `thickness-xl`, `size`, `thickness`, `gap`, `duration`, `track-opacity`, `bar-radius`, `label-font-size`, `label-fg` · 2 privada(s) forward — `_palette-text`, `_palette-track` - **Eje `size`**: no · **ficheros**: `spinner.css` @@ -16,13 +16,15 @@ _Ninguno._ -### 1.2 A través de un privado (3) +### 1.2 A través de un privado (5) | # | fichero:línea | selector | propiedad | valor | | ---: | --- | --- | --- | --- | | 1 | `spinner.css:24` | `[data-spinner]` | `color` | `var(--_spinner-color)` | -| 2 | `spinner.css:67` | `[data-spinner][data-variant='dots'] [data-spinner-dot]` | `background` | `var(--_spinner-color)` | -| 3 | `spinner.css:92` | `[data-spinner][data-variant='bars'] [data-spinner-bar]` | `background` | `var(--_spinner-color)` | +| 2 | `spinner.css:49` | `[data-spinner][data-variant='ring'] [data-spinner-track]` | `border-top-color` | `var(--_spinner-color)` | +| 3 | `spinner.css:67` | `[data-spinner][data-variant='dots'] [data-spinner-dot]` | `background` | `var(--_spinner-color)` | +| 4 | `spinner.css:92` | `[data-spinner][data-variant='bars'] [data-spinner-bar]` | `background` | `var(--_spinner-color)` | +| 5 | `spinner.css:158` | `[data-spinner][data-variant='ring'] [data-spinner-track]` | `border-top-color` | `var(--_spinner-track)` | ### 1.3 Literales (4) @@ -75,8 +77,9 @@ no distingue lo que debería — se marca `⚠`. | `dots-dot-radius` | `root` | `50%` | 1 | | `opacity` | `root` | `0.5` | 1 | -### 4.2 Sin nombre mecánico (1) +### 4.2 Sin nombre mecánico (3) +- **⚠ decisión: la propiedad no tiene slot canónico en el vocabulario** — 2: `border-top-color`. - **⚠ decisión: `1` es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar** — 1: `opacity`. ### 4.3 Avisos sobre los tokens propuestos (1) diff --git a/docs/audit/theming/tabs.md b/docs/audit/theming/tabs.md index c6e04a1e4..da6214fc2 100644 --- a/docs/audit/theming/tabs.md +++ b/docs/audit/theming/tabs.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **96%** — 72 de 75 knobs por token público -- **Knobs de apariencia**: 76 — público 72 · privado 0 · global 2 · literal 1 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **96%** — 74 de 77 knobs por token público +- **Knobs de apariencia**: 78 — público 74 · privado 0 · global 2 · literal 1 · sistema 1 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 79 pública(s) — `stack-gap`, `line-stack-gap`, `provider-radius`, `provider-border-width`, `provider-border`, `provider-bg`, `surface-provider-padding`, `surface-provider-shadow`, `list-gap`, `list-padding`, `list-radius`, `list-width`, `list-bg`, `list-border-width`, `list-border`, `line-list-padding-inline-end`, `surface-list-bg`, `pills-list-bg`, `trigger-height-xs`, `trigger-height-sm`, `trigger-height-md`, `trigger-height-lg`, `trigger-padding-inline-xs`, `trigger-padding-inline-sm`, `trigger-padding-inline-md`, `trigger-padding-inline-lg`, `trigger-gap-xs`, `trigger-gap-sm`, `trigger-gap-md`, `trigger-gap-lg`, `trigger-radius`, `trigger-border-width`, `trigger-font-family`, `trigger-font-size-xs`, `trigger-font-size-sm`, `trigger-font-size-md`, `trigger-font-size-lg`, `trigger-font-weight`, `trigger-line-height`, `trigger-letter-spacing`, `trigger-fg`, `hover-trigger-fg`, `active-trigger-fg`, `trigger-bg`, `active-trigger-bg`, `trigger-border`, `active-trigger-border`, `trigger-transition-duration`, `trigger-transition-ease`, `trigger-focus-ring-offset`, `trigger-focus-ring-width`, `trigger-focus-ring-color`, `active-surface-trigger-border`, `pills-trigger-radius`, `active-pills-trigger-border`, `active-pills-trigger-shadow`, `segmented-list-bg`, `segmented-list-radius`, `segmented-trigger-radius`, `active-segmented-trigger-bg`, `active-segmented-trigger-fg`, `active-segmented-trigger-shadow`, `content-padding-xs`, `content-padding-sm`, `content-padding-md`, `content-padding-lg`, `content-radius`, `content-bg`, `content-border-width`, `content-border`, `content-fg`, `surface-content-border`, `surface-content-bg`, `pills-content-padding-block-start`, `disabled-opacity`, `indicator-transition-duration`, `indicator-transition-ease`, `indicator-line-thickness`, `scrollable-fade` - **Eje `size`**: sí · **ficheros**: `tabs.css` diff --git a/docs/audit/theming/toast.md b/docs/audit/theming/toast.md index dff2c1a79..fa790e9c5 100644 --- a/docs/audit/theming/toast.md +++ b/docs/audit/theming/toast.md @@ -5,8 +5,8 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-23 · **Alcance**: **98%** — 41 de 42 knobs por token público -- **Knobs de apariencia**: 42 — público 41 · privado 0 · global 0 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ +- **Medido**: 2026-08-23 · **Alcance**: **98%** — 45 de 46 knobs por token público +- **Knobs de apariencia**: 46 — público 45 · privado 0 · global 0 · literal 1 · sistema 0 · excepción 0 _(los dos últimos, fuera del ratio)_ - **Contrato hoy** (`lib/recipes/base.ts`): 85 pública(s) — `toaster-z`, `toaster-inset`, `toaster-gap`, `toaster-width`, `provider-min-width`, `provider-max-width`, `provider-gap`, `provider-stack-gap`, `provider-padding-inline`, `provider-padding-block`, `provider-border-width`, `provider-radius`, `provider-shadow`, `accent-width`, `transition-duration`, `transition-ease`, `closed-offset`, `closed-scale`, `status-size`, `status-radius`, `title-font-family`, `title-font-size`, `title-font-weight`, `title-line-height`, `title-fg`, `message-fg`, `description-font-family`, `description-font-size`, `description-line-height`, `description-fg`, `action-gap`, `action-padding-inline`, `action-padding-block`, `action-margin-top`, `action-border-width`, `action-radius`, `action-font-family`, `action-font-size`, `action-font-weight`, `action-line-height`, `action-focus-ring-offset`, `action-focus-ring-width`, `action-focus-ring-color`, `default-surface`, `default-border`, `default-accent`, `default-status-bg`, `default-status-fg`, `default-action-fg`, `default-action-hover-bg`, `affirm-surface`, `affirm-border`, `affirm-accent`, `affirm-status-bg`, `affirm-status-fg`, `affirm-action-fg`, `affirm-action-hover-bg`, `fulfill-surface`, `fulfill-border`, `fulfill-accent`, `fulfill-status-bg`, `fulfill-status-fg`, `fulfill-action-fg`, `fulfill-action-hover-bg`, `risk-surface`, `risk-border`, `risk-accent`, `risk-status-bg`, `risk-status-fg`, `risk-action-fg`, `risk-action-hover-bg`, `threat-surface`, `threat-border`, `threat-accent`, `threat-status-bg`, `threat-status-fg`, `threat-action-fg`, `threat-action-hover-bg`, `loss-surface`, `loss-border`, `loss-accent`, `loss-status-bg`, `loss-status-fg`, `loss-action-fg`, `loss-action-hover-bg` - **Eje `size`**: no · **ficheros**: `toast.css` diff --git a/docs/process/PLAN-theming.md b/docs/process/PLAN-theming.md index ed88709aa..4454f59f7 100644 --- a/docs/process/PLAN-theming.md +++ b/docs/process/PLAN-theming.md @@ -455,6 +455,32 @@ de commit. Sin artefacto, el paso no se ha hecho. ## 8. Registro +- 2026-08-23 — **`anchor-nav` 20 % → 100 %** (11 claves) **y el censo aprende los + bordes POR LADO**. Global 64 %. + + Tenía dos knobs y todo lo demás a pelo: la escala (que ahora lee el bundle), + el relleno y el radio del enlace, sus tres tintas, el peso del activo y las dos + tintas del RAÍL, que es lo que da nombre al componente. + + **El raíl activo era INVISIBLE para la medida**: su color vive en un + `border-inline-start-color` y `KNOB_PROPS` sólo conocía `border-color` y los + shorthands. El centinela mira esos longhands desde el 2026-08-22 (los añadió + cuando prose y nav-tree leyeron muertos); el censo no. Añadidos los ocho por + lado, color y grosor: **+30 filas en el catálogo** (5.101 → 5.131) y esta ficha + baja a 18 % antes de coserse — el censo promete sobre-reportar, NUNCA + infra-reportar. + + La tinta de reposo del raíl estaba escondida de otra manera: dentro de un + shorthand que el censo clasifica por el PRIMER token que casa (el trazo, ya + público). No hizo falta partirlo: basta nombrar el color dentro. + + **Un susto útil**: una coma que faltaba en el contrato rompió el build de + tokens y lo delató la SONDA midiendo 0 nodos — la página no renderizaba. El + recuento de nodos es el canario. + + Gates: sonda 0 diffs en 1.760 valores × 7 estados · centinela 11/12 · censo + 100 % · audit PASS · suite con el rojo conocido ajeno · rtl 0 · docs 0. + - 2026-08-23 — **`image` 60 % → 96 %** (1 clave, 12 literales anotados) **y dos instrumentos que morían en esta página**. Global 64 %. diff --git a/scripts/theming-census.ts b/scripts/theming-census.ts index 7861aa6db..4d087abaf 100644 --- a/scripts/theming-census.ts +++ b/scripts/theming-census.ts @@ -73,7 +73,7 @@ const CONTRACT = readFileSync(resolve('src/uix/eidos/lib/recipes/base.ts'), 'utf ); const KNOB_PROPS = - /^(background|background-color|background-image|color|border|border-color|border-width|border-radius|border-block|border-inline|border-top|border-bottom|border-left|border-right|border-block-start|border-block-end|border-inline-start|border-inline-end|box-shadow|outline|outline-color|outline-width|padding|padding-inline|padding-block|padding-top|padding-bottom|padding-left|padding-right|padding-inline-start|padding-inline-end|padding-block-start|padding-block-end|gap|row-gap|column-gap|font-size|font-weight|font-family|line-height|letter-spacing|min-block-size|block-size|min-inline-size|inline-size|height|min-height|width|min-width|opacity|fill|stroke|accent-color|caret-color|text-decoration-color|filter|backdrop-filter)$/; + /^(background|background-color|background-image|color|border|border-color|border-width|border-radius|border-block|border-inline|border-top|border-bottom|border-left|border-right|border-block-start|border-block-end|border-inline-start|border-inline-end|border-top-color|border-bottom-color|border-left-color|border-right-color|border-block-start-color|border-block-end-color|border-inline-start-color|border-inline-end-color|border-top-width|border-bottom-width|border-left-width|border-right-width|border-block-start-width|border-block-end-width|border-inline-start-width|border-inline-end-width|box-shadow|outline|outline-color|outline-width|padding|padding-inline|padding-block|padding-top|padding-bottom|padding-left|padding-right|padding-inline-start|padding-inline-end|padding-block-start|padding-block-end|gap|row-gap|column-gap|font-size|font-weight|font-family|line-height|letter-spacing|min-block-size|block-size|min-inline-size|inline-size|height|min-height|width|min-width|opacity|fill|stroke|accent-color|caret-color|text-decoration-color|filter|backdrop-filter)$/; /** Transversal systems a recipe CONSUMES (themeable at system level — not per component). */ const SYSTEM = diff --git a/scripts/theming-sentinel-exceptions.ts b/scripts/theming-sentinel-exceptions.ts index 0e0df51c4..1e994b0db 100644 --- a/scripts/theming-sentinel-exceptions.ts +++ b/scripts/theming-sentinel-exceptions.ts @@ -615,6 +615,10 @@ export const SENTINEL_EXCEPTIONS: Record> = { 'stroke-duration': 'same, on the check path dash animation; measured unfrozen -> reaches (0.12s -> 11.5s)', 'stroke-ease': 'same, measured unfrozen -> reaches' }, + 'anchor-nav': { + 'link-radius': + 'the link only rounds under :focus-visible (the ring corner), and the guard blurs on purpose; focused from the keyboard 2026-08-23 -> reaches (4px -> 1234px)' + }, button: { // Measured one by one 2026-08-23. The icon scale needs a real child, // the radius scale is an OPT-IN prop the demo does not set, and the four diff --git a/src/uix/eidos/components/anchor-nav/anchor-nav.css b/src/uix/eidos/components/anchor-nav/anchor-nav.css index fb06b2b22..5e0a29bd2 100644 --- a/src/uix/eidos/components/anchor-nav/anchor-nav.css +++ b/src/uix/eidos/components/anchor-nav/anchor-nav.css @@ -13,8 +13,8 @@ */ [data-anchor-nav] { - font-size: var(--font-size-sm); - line-height: var(--font-line-height-sm); + font-size: var(--anchor-nav-font-size); + line-height: var(--anchor-nav-line-height); } [data-anchor-nav] ul { @@ -25,12 +25,12 @@ [data-anchor-nav-link] { display: block; - padding-block: var(--space-1); + padding-block: var(--anchor-nav-link-padding-block); padding-inline-start: calc( var(--space-3) + var(--_anchor-nav-depth, 0) * var(--anchor-nav-indent) ); - border-inline-start: var(--anchor-nav-rail-width) solid var(--color-border-subtle); - color: var(--color-content-muted); + border-inline-start: var(--anchor-nav-rail-width) solid var(--anchor-nav-rail-fg); + color: var(--anchor-nav-link-fg); text-decoration: none; text-wrap: pretty; transition: @@ -39,20 +39,20 @@ } [data-anchor-nav-link]:hover { - color: var(--color-content-primary); + color: var(--anchor-nav-hover-link-fg); } [data-anchor-nav-link][data-active] { - color: var(--color-primary-text); - border-inline-start-color: var(--color-primary-solid); - font-weight: var(--font-weight-medium); + color: var(--anchor-nav-active-link-fg); + border-inline-start-color: var(--anchor-nav-active-rail-fg); + font-weight: var(--anchor-nav-active-link-font-weight); } /* Focus ring for keyboard users (native anchors are focusable). */ [data-anchor-nav-link]:focus-visible { outline: var(--focus-ring-width) solid var(--focus-ring-color); outline-offset: calc(var(--focus-ring-width) * -1); - border-radius: var(--radius-sm); + border-radius: var(--anchor-nav-link-radius); } /* Depth → indent. TOC base is h2 (level 2 = flush); deeper headings step in. */ diff --git a/src/uix/eidos/generated/base.css b/src/uix/eidos/generated/base.css index a52f6ef6b..52a565e84 100644 --- a/src/uix/eidos/generated/base.css +++ b/src/uix/eidos/generated/base.css @@ -4055,6 +4055,16 @@ --prose-line-height: 1.75; --anchor-nav-indent: var(--space-3); --anchor-nav-rail-width: 2px; + --anchor-nav-rail-fg: var(--color-border-subtle); + --anchor-nav-active-rail-fg: var(--color-primary-solid); + --anchor-nav-font-size: var(--size-sm-font-size); + --anchor-nav-line-height: var(--size-sm-font-line-height); + --anchor-nav-link-padding-block: var(--space-1); + --anchor-nav-link-radius: var(--radius-sm); + --anchor-nav-link-fg: var(--color-content-muted); + --anchor-nav-hover-link-fg: var(--color-content-primary); + --anchor-nav-active-link-fg: var(--color-primary-text); + --anchor-nav-active-link-font-weight: var(--font-weight-medium); --nav-tree-indent: var(--space-4); --nav-tree-font-size: var(--size-sm-font-size); --nav-tree-line-height: var(--size-sm-font-line-height); diff --git a/src/uix/eidos/lib/recipes/base.ts b/src/uix/eidos/lib/recipes/base.ts index c8469e172..9123e0235 100644 --- a/src/uix/eidos/lib/recipes/base.ts +++ b/src/uix/eidos/lib/recipes/base.ts @@ -6381,7 +6381,20 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({ // ───────────────────────────────────────────────────────────────────── 'anchor-nav': { indent: 'var(--space-3)', - 'rail-width': '2px' + 'rail-width': '2px', + // El raíl: su trazo ya era público, su TINTA no — y la del enlace activo + // vivía en un `border-inline-start-color` que el censo ni veía. + 'rail-fg': 'var(--color-border-subtle)', + 'active-rail-fg': 'var(--color-primary-solid)', + // La escala del índice: lee el bundle, no el primitivo suelto. + 'font-size': 'var(--size-sm-font-size)', + 'line-height': 'var(--size-sm-font-line-height)', + 'link-padding-block': 'var(--space-1)', + 'link-radius': 'var(--radius-sm)', + 'link-fg': 'var(--color-content-muted)', + 'hover-link-fg': 'var(--color-content-primary)', + 'active-link-fg': 'var(--color-primary-text)', + 'active-link-font-weight': 'var(--font-weight-medium)', }, // ─────────────────────────────────────────────────────────────────────