uix(anchor-nav): temable — 20 % → 100 %, y el censo aprende los bordes POR LADO

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 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent c08e88598d
commit 7fabee1972

@ -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 | – |

@ -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 `⚠`.
<!-- veredicto:start -->
_(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.
<!-- veredicto:end -->

@ -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)

@ -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`

@ -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)

@ -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)

@ -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)

@ -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`

@ -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`

@ -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`

@ -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)

@ -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`

@ -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`

@ -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 %.

@ -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 =

@ -615,6 +615,10 @@ export const SENTINEL_EXCEPTIONS: Record<string, Record<string, string>> = {
'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 <svg> child,
// the radius scale is an OPT-IN prop the demo does not set, and the four

@ -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. */

@ -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);

@ -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)',
},
// ─────────────────────────────────────────────────────────────────────

Loading…
Cancel
Save

Powered by TurnKey Linux.