You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/audit/theming/chat-message.md

195 lines
13 KiB

docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
# chat-message — alcance de tema: análisis y propuesta
> Generado por `node --import tsx/esm scripts/theming-census.ts --report`.
> Lo **medido** y la **propuesta** se regeneran; el **Veredicto** (§5) se conserva.
> Vista de conjunto: [README](./README.md) · método y protocolo:
> [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7.
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Medido**: 2026-08-24 · **Alcance**: **100%** — 71 de 71 knobs por token público
- **Knobs de apariencia**: 78 — público 71 · privado 0 · global 0 · literal 0 · sistema 7 · excepción 4 _(los dos últimos, fuera del ratio)_
- **Contrato hoy** (`lib/recipes/base.ts`): 81 pública(s) — `gap`, `row-gap-run`, `row-gap-group`, `focus-radius`, `gutter-inline-size`, `bubble-padding-inline-sm`, `bubble-padding-inline-md`, `bubble-padding-inline-lg`, `bubble-padding-block-sm`, `bubble-padding-block-md`, `bubble-padding-block-lg`, `bubble-radius`, `bubble-radius-run`, `bubble-tail-radius`, `bubble-max-inline-size`, `bubble-font-size-sm`, `bubble-font-size-md`, `bubble-font-size-lg`, `bubble-line-height`, `bubble-bg-in`, `bubble-fg-in`, `bubble-bg-out`, `bubble-fg-out`, `bubble-shadow`, `header-gap`, `header-font-size`, `header-font-weight`, `header-fg`, `meta-gap`, `meta-font-size`, `meta-fg`, `status-size`, `status-fg`, `read-status-fg`, `failed-status-fg`, `read-by-gap`, `read-by-font-size`, `read-by-fg`, `read-by-margin`, `emphasize-bg`, `emphasize-radius`, `emphasize-duration`, `reply-padding-inline`, `reply-padding-block`, `reply-accent-size`, `reply-radius`, `reply-bg`, `reply-fg`, `reply-accent`, `reply-font-size`, `reaction-gap`, `reaction-inner-gap`, `reaction-height`, `reaction-padding-inline`, `reaction-radius`, `reaction-font-size`, `reaction-bg`, `reaction-fg`, `reaction-border`, `reaction-shadow`, `reaction-overlap`, `reaction-pressed-bg`, `reaction-pressed-border`, `reaction-pressed-fg`, `quick-react-gap`, `quick-react-pad`, `quick-react-size`, `quick-react-radius`, `quick-react-emoji-size`, `quick-react-hover-bg`, `quick-react-hover-scale`, `actions-bg`, `actions-border`, `actions-shadow`, `actions-radius`, `actions-padding`, `mentioned-bg`, `mentioned-fg`, `mentioned-accent`, `actions-gap`, `actions-offset`
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Eje `size`**: sí · **ficheros**: `chat-message.css`
## 1. Knobs fuera de alcance
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.1 Directo a primitivo global (0)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
_Ninguno._
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(theming): el censo aprende que un privado DERIVADO sí alcanza — 50 % → 56 % Seis puntos de alcance sin tocar una línea de componente: **la medición estaba mal, no el código**. EL DEFECTO. F0 encargaba «detección "privado que deriva de público" (leer `--_c-x: var(--c-y)` en la misma receta y reclasificar)». Se implementó a medias: el censo CALCULA `derives` y lo imprime en la columna «¿deriva de un público?» de la §3 de cada ficha, pero **el contador nunca lo usó** — esos knobs seguían puntuando como deuda. Medido: **240 privados en 59 componentes** estaban en ese estado, es decir, haciendo exactamente lo que la doctrina prescribe («privados sólo si derivan de públicos») y penalizados por ello. Lo destapó tokenizar `avatar`: sus 17 privados YA leían públicos (`--_avatar-size: var(--avatar-size-md)`), así que no había nada que tokenizar — había que arreglar el instrumento. LA CORRECCIÓN. Segunda pasada tras recoger privados y knobs: un knob que lee `var(--_c-x)` pasa a `public` si ese privado alcanza un público. El cierre es **TRANSITIVO** (`--_a: calc(var(--_b) * .3)` alcanza si `--_b` alcanza) con conjunto de visitados para que un ciclo no lo cuelgue. **Muta-prueba** (obligatoria, memoria `a-guard-that-inspects-nothing-passes`): apuntar `--_avatar-radius` al primitivo crudo baja avatar de 75 % a 72 % y el knob vuelve a `private`; revertirlo lo devuelve a 75 % y deja el árbol idéntico. EFECTO MEDIDO: - Global **50 % → 56 %** · privados 625 → 341 · al 100 % 10 → 14 · <20 % 22 → 20. - `avatar` 36 % → **75 %**, `picker-shell` 76 % → **95 %**, `menu-dial` 25 % → 38 %, y otras 51 fichas con cifras nuevas — **ninguna por cambio de código**. - Los READMEs de `picker-shell` y `menu-dial` se actualizan con la cifra nueva y con la razón, para que nadie crea que el componente cambió. ⚠ **Cualquier cifra anterior a este commit no es comparable con las de ahora.** Queda escrito en el handoff. Y un hueco de F0 que sale al hacerlo, registrado en §13: **el test del SUELO que el plan pedía NO EXISTE** («alcance global ≥ el de hoy y literales ≤ 614 — el número sube o el test falla»). Hoy nada impide que el alcance BAJE entre sesiones: sólo se vería mirando la cifra a mano. Con el censo ya endurecido, es el momento de escribirlo. Gates: `component:audit` 162 PASS / 4 NEEDS-WORK (preexistentes) · `--names` DESVIADAS 0 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el censo compila. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 1.2 A través de un privado (0)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(theming): el censo aprende que un privado DERIVADO sí alcanza — 50 % → 56 % Seis puntos de alcance sin tocar una línea de componente: **la medición estaba mal, no el código**. EL DEFECTO. F0 encargaba «detección "privado que deriva de público" (leer `--_c-x: var(--c-y)` en la misma receta y reclasificar)». Se implementó a medias: el censo CALCULA `derives` y lo imprime en la columna «¿deriva de un público?» de la §3 de cada ficha, pero **el contador nunca lo usó** — esos knobs seguían puntuando como deuda. Medido: **240 privados en 59 componentes** estaban en ese estado, es decir, haciendo exactamente lo que la doctrina prescribe («privados sólo si derivan de públicos») y penalizados por ello. Lo destapó tokenizar `avatar`: sus 17 privados YA leían públicos (`--_avatar-size: var(--avatar-size-md)`), así que no había nada que tokenizar — había que arreglar el instrumento. LA CORRECCIÓN. Segunda pasada tras recoger privados y knobs: un knob que lee `var(--_c-x)` pasa a `public` si ese privado alcanza un público. El cierre es **TRANSITIVO** (`--_a: calc(var(--_b) * .3)` alcanza si `--_b` alcanza) con conjunto de visitados para que un ciclo no lo cuelgue. **Muta-prueba** (obligatoria, memoria `a-guard-that-inspects-nothing-passes`): apuntar `--_avatar-radius` al primitivo crudo baja avatar de 75 % a 72 % y el knob vuelve a `private`; revertirlo lo devuelve a 75 % y deja el árbol idéntico. EFECTO MEDIDO: - Global **50 % → 56 %** · privados 625 → 341 · al 100 % 10 → 14 · <20 % 22 → 20. - `avatar` 36 % → **75 %**, `picker-shell` 76 % → **95 %**, `menu-dial` 25 % → 38 %, y otras 51 fichas con cifras nuevas — **ninguna por cambio de código**. - Los READMEs de `picker-shell` y `menu-dial` se actualizan con la cifra nueva y con la razón, para que nadie crea que el componente cambió. ⚠ **Cualquier cifra anterior a este commit no es comparable con las de ahora.** Queda escrito en el handoff. Y un hueco de F0 que sale al hacerlo, registrado en §13: **el test del SUELO que el plan pedía NO EXISTE** («alcance global ≥ el de hoy y literales ≤ 614 — el número sube o el test falla»). Hoy nada impide que el alcance BAJE entre sesiones: sólo se vería mirando la cifra a mano. Con el censo ya endurecido, es el momento de escribirlo. Gates: `component:audit` 162 PASS / 4 NEEDS-WORK (preexistentes) · `--names` DESVIADAS 0 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el censo compila. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
_Ninguno._
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.3 Literales (0)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
_Ninguno._
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 1.4 Excepciones firmadas (4) — fuera del ratio
tooling(theming): el censo aprende a leer la válvula del canon — clase `exception` Global 58 % → **59 %** sin tocar una línea de CSS: 54 knobs que eran deuda resultan ser desviaciones que el canon ya había firmado. `recipe-contract` §3 dice que una desviación anotada `/* literal: <razón> */` EN SU PROPIA DECLARACIÓN es válida, y `component-audit` la honra desde siempre (`declarationWindow` + `/literal:/i` en R-2.1, R-2.6 y R-2.7). El censo era el único instrumento que no: su `strip()` blanquea los comentarios ANTES de clasificar, así que las **81 anotaciones del catálogo** contaban como literales sin excusa. Dos instrumentos, un canon, dos respuestas — y la que mandaba era la del que no sabía leerlo. Ahora un literal anotado cae en la clase **`exception`, fuera del ratio** como `system`, y por la misma razón: no es que un tema no lo alcance, es que el canon ya aceptó por escrito que ahí no va un token. **54 knobs en 12 componentes**: `prose` 21 (su escala editorial em-relativa, que su README documenta como decisión — «No 5× duplicated scale»), `proof-of-human` 16 (la escena de tono fijo), `palabras` 3, `timeline` 2 (73 % → **77 %**), `result` 1 (86 % → **90 %**), y siete más con una cada uno. Detalles que costaron medición: - **La anotación vale para la DECLARACIÓN entera**, no sólo para su primera línea: un valor partido en varias líneas lleva la nota al final. Se comprueban todas las líneas que abarca la declaración. - **`strip()` blanquea en el sitio** (sustituye por espacios, conserva los saltos), así que el cuerpo despojado y el crudo quedan alineados línea a línea y basta con guardar el crudo al lado. - Muta-prueba: retirar una anotación de `prose` mueve el knob de `exception` a `literal` (21 → 20) y el conteo vuelve al restaurarla. Efecto colateral bueno: cada ficha gana una **§1.4 «Excepciones firmadas»** que las LISTA con su razón. Estaban escritas en el CSS y no se leían desde ninguna parte; ahora la ficha del componente las enseña, que es donde se decide si una sigue siendo cierta. Las 163 fichas cambian porque la plantilla cambia (sección nueva + la línea de reparto); el README de la auditoría gana la fila de la clase y la nota de que `system` y `exception` quedan fuera del denominador. Guards: `--names` 4.105 claves, DESVIADAS 0 · component-audit PASS · docs:check 0 · `check` con 72 errores globales y 0 en los ficheros tocados. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Literales que llevan su anotación `/* literal: <razón> */` en la propia
declaración: la válvula de recipe-contract §3, la misma que honra
`component-audit`. **Una desviación firmada no es deuda** — se listan para que la
razón se lea, no para acuñarlas.
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `chat-message.css:371` | `[data-popover-content][data-chat-message-quick-reactions]` | `inline-size` | `max-content` |
| 2 | `chat-message.css:401` | `[data-chat-message-quick-react]` | `line-height` | `1` |
| 3 | `chat-message.css:470` | `[data-chat-message]:hover [data-chat-message-actions], [data-chat-message]:focus-within [data-chat-message-actions]` | `opacity` | `1` |
| 4 | `chat-message.css:476` | `[data-chat-message-actions]` | `opacity` | `1` |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 2. Sistema transversal (7) — informativo, fuera del ratio
Un tema los alcanza **a nivel de sistema**, por diseño (recipe-contract §2).
| # | fichero:línea | selector | propiedad | valor |
| ---: | --- | --- | --- | --- |
| 1 | `chat-message.css:36` | `[data-chat-message]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 2 | `chat-message.css:270` | `[data-chat-message][data-delivery='sending'] [data-chat-message-bubble]` | `opacity` | `var(--opacity-muted)` |
| 3 | `chat-message.css:296` | `[data-chat-message-reply]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 4 | `chat-message.css:300` | `[data-chat-message-reply]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
| 5 | `chat-message.css:342` | `[data-chat-message-reaction]:hover, [data-chat-message-reaction-add]:hover` | `background-image` | `linear-gradient(var(--state-hover), var(--state-hover))` |
| 6 | `chat-message.css:347` | `[data-chat-message-reaction]:focus-visible, [data-chat-message-reaction-add]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
| 7 | `chat-message.css:416` | `[data-chat-message-quick-react]:focus-visible` | `outline` | `var(--focus-ring-width) solid var(--focus-ring-color)` |
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
## 3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
| --- | ---: | --- | --- | :-: |
| `--_chat-message-bubble-bg` | 2 | `var(--chat-message-bubble-bg-in)`, `var(--chat-message-bubble-bg-out)` | public | **sí** |
| `--_chat-message-bubble-fg` | 2 | `var(--chat-message-bubble-fg-in)`, `var(--chat-message-bubble-fg-out)` | public | **sí** |
## 4. Propuesta de corrección
- **Tiene eje `size`**: los tokens dimensionales van por talla (`{part}-{eje}-{k}`) apuntando al bundle `--size-{k}-*`, nunca al primitivo crudo (theming §5; el guard `recipe-css-contract` prohíbe el primitivo).
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
### 4.1 Tokens a declarar en `lib/recipes/base.ts` (1)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
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 (`--chat-message-…`) | scope TSC | valor propuesto | usos |
| --- | --- | --- | ---: |
| `quick-reactions-width` | `root` | `max-content` | 1 |
uix(theming): el censo aprende que un privado DERIVADO sí alcanza — 50 % → 56 % Seis puntos de alcance sin tocar una línea de componente: **la medición estaba mal, no el código**. EL DEFECTO. F0 encargaba «detección "privado que deriva de público" (leer `--_c-x: var(--c-y)` en la misma receta y reclasificar)». Se implementó a medias: el censo CALCULA `derives` y lo imprime en la columna «¿deriva de un público?» de la §3 de cada ficha, pero **el contador nunca lo usó** — esos knobs seguían puntuando como deuda. Medido: **240 privados en 59 componentes** estaban en ese estado, es decir, haciendo exactamente lo que la doctrina prescribe («privados sólo si derivan de públicos») y penalizados por ello. Lo destapó tokenizar `avatar`: sus 17 privados YA leían públicos (`--_avatar-size: var(--avatar-size-md)`), así que no había nada que tokenizar — había que arreglar el instrumento. LA CORRECCIÓN. Segunda pasada tras recoger privados y knobs: un knob que lee `var(--_c-x)` pasa a `public` si ese privado alcanza un público. El cierre es **TRANSITIVO** (`--_a: calc(var(--_b) * .3)` alcanza si `--_b` alcanza) con conjunto de visitados para que un ciclo no lo cuelgue. **Muta-prueba** (obligatoria, memoria `a-guard-that-inspects-nothing-passes`): apuntar `--_avatar-radius` al primitivo crudo baja avatar de 75 % a 72 % y el knob vuelve a `private`; revertirlo lo devuelve a 75 % y deja el árbol idéntico. EFECTO MEDIDO: - Global **50 % → 56 %** · privados 625 → 341 · al 100 % 10 → 14 · <20 % 22 → 20. - `avatar` 36 % → **75 %**, `picker-shell` 76 % → **95 %**, `menu-dial` 25 % → 38 %, y otras 51 fichas con cifras nuevas — **ninguna por cambio de código**. - Los READMEs de `picker-shell` y `menu-dial` se actualizan con la cifra nueva y con la razón, para que nadie crea que el componente cambió. ⚠ **Cualquier cifra anterior a este commit no es comparable con las de ahora.** Queda escrito en el handoff. Y un hueco de F0 que sale al hacerlo, registrado en §13: **el test del SUELO que el plan pedía NO EXISTE** («alcance global ≥ el de hoy y literales ≤ 614 — el número sube o el test falla»). Hoy nada impide que el alcance BAJE entre sesiones: sólo se vería mirando la cifra a mano. Con el censo ya endurecido, es el momento de escribirlo. Gates: `component:audit` 162 PASS / 4 NEEDS-WORK (preexistentes) · `--names` DESVIADAS 0 · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · el censo compila. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
### 4.2 Sin nombre mecánico (3)
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **⚠ 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** — 3: `line-height`, `opacity`.
### 4.4 Lo que hay que comprobar a mano (PLAN-theming §1.3 · §7.4)
- [ ] **Privado que no deriva de un público** — §3 lo marca; el privado debe leer el público o desaparecer.
- [ ] **Velo o acento en el nodo equivocado** (`archetype: 'item'` en un envoltorio, un `background` en shorthand que mata la capa de estado) — se mide desde el píxel hacia arriba.
- [ ] **Doble animación** al mover un sello a una superficie con animación propia — registro de `animationstart`/`animationend`.
- [ ] **Diff de computed = 0** en reposo · hover · abierto · disabled · foco, por talla, antes y después.
- [ ] **Centinela por token nuevo**: valor imposible en el root → el nodo lo sigue. Si no, el token miente.
## 5. Veredicto
<!-- veredicto:start -->
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**EJECUTADO 2026-08-24 — 84 % → 100 %.** Contrato 75 → **81** claves; censo
`público 71 · global 0 · literal 0 · sistema 7 · excepción 4`. Diff de computed
**VACÍO** (4.192 valores, 8 estados, 16 nodos) y capturas 2× **idénticas byte a
byte**, en reposo y con la barra tapback abierta. Centinela **77/81**, cuatro
adjudicadas. `component-audit` PASS · `eidos-lint` 0 invalid · `rtl:check` 0 ·
`docs:check` 0 · `check` sin errores del componente.
**Otra vez, lo caro fue VER.** El guard daba **45/75** antes de tocar una línea
y las treinta «muertas» estaban las treinta VIVAS: una fila de chat es una PILA
de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de
ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en
su posición más pobre. Con las cuatro superficies encendidas por `prepareWith`
y los cinco ejes barridos como producto: **71/75 antes de acuñar nada**. La
sonda pasó de 12 a 16 nodos en reposo y a 24 con la barra abierta.
**Lo que entra (6 claves, la COSTURA de los seis globales)**: `focus-radius`,
`mentioned-fg`, `reply-fg`, `reaction-fg`, `reaction-inner-gap` y
`quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser
del componente.
**Los nombres salen del CATÁLOGO.** La ficha proponía `radius` (colisiona con
`emphasize-radius`, que ES la esquina en reposo de la MISMA fila: la de foco
lleva su modificador delante, `focus-radius`), `bubble-fg` (el knob es de la
MENCIÓN — `mentioned-bg` / `mentioned-accent` ya existen, faltaba
`mentioned-fg`), `reaction-width` + `reaction-height` para el glifo (dos
literales de una regla muerta, y `reaction-height` ya existe con otro sentido:
la altura del CHIP) y `quick-reactions-width` para un `max-content` que es
identidad.
**Y el caso que obligó a decidir: `reaction-gap` ya existía con otro sentido.**
La receta la pinta en la fila `reactions` (el hueco ENTRE chips) mientras el
knob nuevo es el ritmo INTERNO del chip — el emoji y su cuenta. Mismo valor
(`--space-1`), dos papeles: un chip más denso no es una fila más apretada.
Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa,
así que entra como `reaction-inner-gap` y `reaction-gap` se queda como está. El
resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`,
`header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la
vieja, y renombrarla es churn con riesgo.
**Lo que SALE: una regla que no ha pintado nunca.**
`[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba
`1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0×0». El glifo
por defecto es el `Icon` compuesto, que emite
`style="width: var(--icon-size-sm); height: …"` INLINE — y un estilo en línea
gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px
que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene
nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en
la familia (chat-log, chat-composer, y aquí).
**Identidades firmadas (4, fuera del ratio)**: `inline-size: max-content` de la
barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la
celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones
(el final del fundido 0 → 1).
**Adjudicadas (4), todas límites del INSTRUMENTO y medidas a mano**:
1. `bubble-tail-radius` — el guard fotografía UNA esquina
(`borderTopLeftRadius`) y la cola es la de ABAJO (`border-end-start` en `in`,
`border-end-end` en `out`). Medido en las 8 combinaciones dirección × run:
4 px → 1234 px en `solo` y `last` en las dos direcciones. Su hermana
`bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí
está en la lista.
2. `emphasize-duration` — ES la transición que el guard congela para poder medir
todo lo demás. Sin congelar: 0,24 s → 4,321 s.
3. `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma
fusiona la identidad de `Feed.Article` cuando hay un Feed ancestro) y su
propia demo la monta suelta, así que `:focus-visible` no tiene nodo ahí.
Medido en `/uix/components/chat-log`, donde las filas SÍ son artículos de
feed (`tabindex=0`): reposo 6 px (la esquina de `emphasize`), enfocada 4 px →
1234 px, por foco programático y por recorrido real con Tab.
4. `quick-react-hover-scale` — dos límites a la vez: `transform` no está en la
lista de propiedades del guard (el punto ciego ya adjudicado en `background`,
`rating-group` y `card`) y la celda es un botón PORTALADO con regla `:hover`.
Con puntero real y transiciones congeladas: `matrix(1.18…)` →
`matrix(7.77…)`.
**Defecto real encontrado y NO corregido aquí (mueve píxel)**: el chip de añadir
reacción lleva también `data-popover-trigger`, y
`[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0)
contra los (0,1,0) de `[data-chat-message-reaction-add]` — así que su fondo, su
borde, su radio, su tinta, su altura, su padding y su tamaño de letra los pinta
`popover.css`. Se ve en la captura: los dos chips de reacción son píldoras
redondas y el de añadir es un cuadrado gris. Es la clase `gradient-picker` (36
de 45) / `emoji-picker` (3). Los `reaction-*` NO mienten —alcanzan sobre los
chips normales—, así que no hay nada que retirar; lo que hay es una decisión de
diseño pendiente. Registrado, no tocado (D-TH.5).
feat(theming)!: el sobre de trigger de popover es un SUELO - tercera ley 55 El "baseline button envelope" que popover.css pinta a sus triggers vivia a (0,2,0) con hover a (0,5,0): le ganaba a las recetas de sus HUESPEDES (el chip de anadir reaccion de chat-message perdia 9/9 propiedades disputadas y salia cuadrado gris entre pildoras - SU receta lo pintaba en el mismo selector-lista y PERDIA), mataba la capa de estado del sistema en hover (chronos plain = caja gris) y dejaba el trigger de calendar en manos del orden de carga (14/16/14/14px medidos - la moneda al aire). El sobre baja a :where() - base y hover, con el :not(field-trigger) DENTRO (where anula especificidad, no matching). El suelo pinta lo que el huesped CALLA; la receta gana donde habla; la capa de estado compone otra vez. Tercera aplicacion de la ley firmada (12.9 planos - B'/53 paleta - y archetypes.css:9-21 ya lo decia para SU capa). Cambio de codigo: 2 lineas. Medido (gate de dos niveles, 76 instancias, 16 rutas, 4 estados): - No afectados: 3 field-trigger diff VACIO; los 66 desnudos ganan EXACTAMENTE los dos valores del SISTEMA que el sobre tapaba (velo de hover de archetypes + su transition). 167 nodos de referencia: 0. - Los 7 movers, valor a valor por CDP hacia lo que SU receta declara: el chip vuelve pildora (36->26px, r9999) - chronos recupera su plain y su hover - calendar mes/anio pasa a su ghost compuesto Y SE VUELVE DETERMINISTA (8 cargas en dos ordenes, mismo pixel; antes 2 de 6 caian del otro lado) - emoji/palabras/ntp a sus recetas (ntp revive 4 claves: 44->48/62). - Capturas antes/despues x8 revisadas por el supervisor (chip, chronos, calendar, emoji, gradient, popover generico, palabras, ntp). - Guarda 55 en active-eidos-config.test.ts validada por SEIS mutaciones (la sexta se anadio porque la primera pasada dejo pasar un verde falso; y el arnes mintio con -t sobre 0 tests - detector de cero-tests anadido). - vitest 35/36 (rojo ajeno skin-media-player) - check 0 atribuibles - docs-check 0/0 - eidos-lint popover/chat-message 0 invalid. Registrado en 13 y fichas: los parches-pin de chronos/calendar pasan a REVISABLES (ahora redundantes o semi) - el suelo comparte peldano (0,0,0) con archetypes.css (medido 16 cargas: archetypes cae despues y el resultado es identico, pero es un empate real sin verificar en produccion) - la dependencia sin registrar de palabras queda anotada junto a su regla (no tiene README de eidos). changelog 55 escrito (la guarda citaba una seccion que debia existir). CON ESTA, LA LISTA "Lo que espera TU FIRMA" DEL CONTINUE QUEDA VACIA. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
**D-TH.5 — CERRADO 2026-08-25 por la firma «el sobre de trigger de popover es el
SUELO»** (changelog §55). Se resolvió donde estaba el defecto, no aquí: el sobre
de `popover.css` bajó a `:where(...)` (0,0,0) —tercera aplicación de la ley de
§12.9 y §53— y el bloque de esta receta gana ya sus siete propiedades. El chip
mide **34×26, radio 9999px, fondo `oklch(0.9911 0 0)`, 12px de letra, 8px de
padding**: la misma píldora que sus hermanas. Cero claves retiradas y cero
añadidas — el número del componente no se mueve, el centinela sigue en 77/81 con
las mismas cuatro adjudicadas.
uix(chat-message): temable — 84 % → 100 %, y las TREINTA muertas eran ejes que la demo no enciende Protocolo PLAN-theming §7 sobre `chat-message`, el mayor de la familia chat. Censo 84 % → **100 %**, contrato 75 → **81** claves, centinela **77/81** con 4 adjudicadas. El default NO se mueve: diff de computed VACÍO (4.192 valores · 8 estados · 16 nodos) y capturas 2x antes/después **idénticas byte a byte**, en reposo y con la barra tapback abierta. ## Otra vez, lo caro fue VER El guard leía **45/75** antes de tocar una línea de código. Una fila de chat es una PILA de superficies opt-in (reply citado, read-by, estado de entrega, mención) y de ejes de PRESENTACIÓN (`run`, `direction`, `emphasized`) que la demo arranca en su posición más pobre: `solo` + `in`, sin ninguna de las cuatro. Con las superficies encendidas por `prepareWith` y los cinco ejes barridos como PRODUCTO: **71/75 antes de acuñar nada**. La sonda pasó de 12 nodos a 16 en reposo, y a 24 con el panel abierto. ## Lo que entra (6 claves, la COSTURA de los seis globales) `focus-radius` · `mentioned-fg` · `reply-fg` · `reaction-fg` · `reaction-inner-gap` · `quick-react-radius`. El valor sigue siendo el del sistema; el knob pasa a ser del componente. **Los nombres salen del CATÁLOGO, no del generador.** La ficha proponía `radius` (colisiona con `emphasize-radius`, que ES la esquina en reposo de la MISMA fila), `bubble-fg` (el knob es de la MENCIÓN: `mentioned-bg` y `mentioned-accent` ya existían), `reaction-width` + `reaction-height` para el glifo (`reaction-height` ya existe y es la altura del CHIP) y `quick-reactions-width` para un `max-content` que es identidad. ## El caso que obligó a decidir: `reaction-gap` ya existía con otro sentido La receta la pinta en la fila `reactions` — el hueco ENTRE chips — mientras el knob nuevo es el ritmo INTERNO del chip, el emoji y su cuenta. Mismo valor (`--space-1`), dos papeles: un chip más denso no es una fila más apretada. Reusar la clave habría cambiado en silencio lo que mueve un tema que ya la usa, así que entra como `reaction-inner-gap`. El resto del bloque `{parte}-gap` de esta receta (`meta-gap`, `read-by-gap`, `header-gap`, `actions-gap`) significa «dentro de la parte»: la anómala es la vieja, y renombrarla es churn con riesgo. ## Lo que SALE: una regla que no ha pintado nunca `[data-chat-message-reaction] svg, [data-chat-message-reaction-add] svg` fijaba `1.1em` en los dos ejes «porque un svg de sólo viewBox computa 0x0». El glifo por defecto es el `Icon` compuesto, que emite `style="width: var(--icon-size-sm); height: ..."` EN LÍNEA, y un estilo en línea gana a todo selector. Medido: **16 px** (el paso `sm` del icono), no los 15,4 px que `1.1em` daría sobre los 14 px heredados. La otra mitad del selector no tiene nodo: los chips llevan un emoji de texto. **Retirada: 0 diffs.** Tercera vez en la familia (chat-log y chat-composer, ayer). ## Identidades firmadas (4, fuera del ratio) `inline-size: max-content` de la barra tapback (la barra ES su fila de emoji), `line-height: 1` de la celda (la celda es la caja del glifo) y los dos `opacity: 1` de la pastilla de acciones (el final del fundido 0 → 1). ## Adjudicadas (4) — las cuatro son límites del INSTRUMENTO, medidas a mano - `bubble-tail-radius` — el guard fotografía UNA esquina (`borderTopLeftRadius`) y la cola es la de ABAJO. Medido en las 8 combinaciones dirección x run: 4 px → 1234 px en `solo` y `last`, en las dos direcciones. Su hermana `bubble-radius-run` lee viva porque `middle` aplana la esquina START, que sí está en la lista. - `emphasize-duration` — ES la transición que el guard congela para medir todo lo demás. Sin congelar: 0,24 s → 4,321 s. - `focus-radius` — la fila sólo es tabulable compuesta DENTRO de un Feed (soma fusiona la identidad de `Feed.Article` cuando hay Feed ancestro) y su propia demo la monta suelta: `:focus-visible` no tiene nodo ahí. Medido en la demo de `chat-log`, donde las filas SÍ son artículos de feed (`tabindex=0`): reposo 6 px, enfocada 4 px → 1234 px, por foco programático y por recorrido con Tab. - `quick-react-hover-scale` — `transform` no está en la lista de propiedades del guard (punto ciego ya adjudicado en background / rating-group / card) y la celda es un botón PORTALADO con regla `:hover`. Con puntero real: `matrix(1.18…)` → `matrix(7.77…)`. ## Un defecto real medido y NO corregido aquí (mueve píxel) El chip de añadir reacción lleva también `data-popover-trigger`, y `[data-popover-trigger]:not([data-archetype='field-trigger'])` pesa (0,2,0) contra los (0,1,0) de `[data-chat-message-reaction-add]`: su fondo, borde, radio, tinta, altura, padding y tamaño de letra los pinta `popover.css`. Se ve en la captura — los dos chips de reacción son píldoras redondas y el de añadir es un cuadrado gris. Los `reaction-*` no mienten (alcanzan sobre los chips normales), así que no hay nada que retirar: es una decisión de diseño. Clase `gradient-picker` / `emoji-picker`. Queda REGISTRADO, no tocado (D-TH.5). ## Guards censo `--only chat-message` 100 % (global 68 %, no baja) · `component-audit` PASS · `eidos-lint` 0 invalid (37 morfo-backed / 41 eidos-only) · `vitest run src/uix/eidos` sin rojos nuevos (el único vivo es el conocido `skin-media-player`) · `rtl:check` 0 · `docs:check` 0 · `check` COMPLETED sin un solo error del componente · prettier limpio sobre el contenido normalizado a LF. README con su sección «Talla y tema», demo con pestaña `Tokens` (81 claves, verificada en navegador) y ficha con su veredicto §5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**Lo que queda fuera y por qué**: cuatro identidades firmadas (§1.4) y siete
knobs de sistema transversal (§2, anillo de foco × 4, velo de estado × 2,
`--opacity-muted` del `sending`). No hay deuda: el componente está CERRADO en su
número.
docs(theming): la auditoría de alcance de tema, componente a componente, con su propuesta El censo medía el eje y no lo explicaba: 5.205 knobs en dos tablas del plan no dicen QUÉ knob de QUÉ línea no alcanza un tema, ni qué token habría que crear. Ahora `theming-census.ts --report` escribe la auditoría entera bajo `docs/audit/theming/`: un README con la vista de conjunto y 170 fichas — 162 recetas con CSS + 8 componentes sin receta, el árbol completo de `eidos/components/`. Cada ficha de receta: knobs fuera de alcance con fichero:línea Y selector (agrupados por clase), sistema transversal aparte, los privados con la columna que decide («¿deriva de un público?»), y una PROPUESTA de corrección — el token a declarar en `base.ts` con su valor VERBATIM y su scope TSC. La propuesta deriva los nombres de recipe-contract §1 y theming §6.7; donde la doctrina no decide, marca `⚠ decisión` en vez de inventar. Y no propone acuñar lo que la doctrina prohíbe: lo que posee una capa compartida, el foco, la capa de estado, un token prestado de otra receta, un shorthand o un eje físico. Las 8 fichas sin receta dicen —medido, no supuesto— dónde vive su visual: el componente que componen (icon-button → button), la capa que consumen (affix → viewport-placement) o la receta ajena que pinta sus attrs (svg → badge/button). Defectos del instrumento corregidos en el mismo pase, todos encontrados mirando la salida: un `@import` pegado al primer selector se comía 2 knobs de color-picker (el suelo del censo no puede moverse); `:not(:disabled)` se leía como estado y producía `hover-disabled-*`; un knob que lee un privado se proponía a sí mismo en vez de resolverse por talla; `--icon-size-sm` se reportaba como préstamo del componente `icon` (ahora el préstamo se verifica contra las claves del dueño en `base.ts`); y el default de talla salía sin nombrar en vez de `-md`. Verificación: censo global idéntico al suelo publicado (5205 · 1621/33% · 856 · 1880 · 614 · 234) y COMPONENTE A COMPONENTE contra la tabla §9 del plan — 0 diferencias en 162 · regenerar dos veces da el árbol idéntico · el bloque de veredicto escrito a mano sobrevive · docs:check 0 errores sobre 813 docs · prettier limpio (el árbol generado entra en .prettierignore junto a eidos/generated). Las 10 primeras fichas llevan ya su veredicto de revisión: análisis correcto en las 10, propuesta apta en 2 (gradient-builder como piloto, combobox con cinco correcciones — dos de ellas evitaban romper el default), y 5 que no se tokenizan en solitario porque son alias de --calendar-*/--field-* y esperan la decisión de familia. Cuatro destaparon privados con prefijo ajeno o abreviado declarados en su propio CSS. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
<!-- veredicto:end -->

Powered by TurnKey Linux.