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-composer.md

10 KiB

chat-composer — 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 · método y protocolo: PLAN-theming.md §1, §2, §7.

  • Medido: 2026-08-25 · Alcance: 100% — 34 de 34 knobs por token público
  • Knobs de apariencia: 39 — público 34 · privado 0 · global 0 · literal 0 · sistema 5 · excepción 1 · estructural 0 · puente 0 · canal 0 (los cinco últimos, fuera del ratio)
  • Contrato hoy (lib/recipes/base.ts): 33 pública(s) — gap, bg, border, border-width, focus-border, radius, padding-inline, padding-block, input-font-size, input-line-height, input-fg, input-placeholder-fg, input-padding-inline, input-padding-block, context-bg, context-accent, context-accent-size, context-font-size, context-fg, context-radius, context-padding-inline, context-padding-block, context-gap, context-close-size, context-close-radius, attachments-gap, send-size, send-bg, send-fg, send-radius, transition-duration, transition-ease, disabled-opacity
  • Eje size: no · ficheros: chat-composer.css

1. Knobs fuera de alcance

1.1 Directo a primitivo global (0)

Ninguno — los tres que había (los dos ejes y el radio del chip de descarte) están cosidos en dos claves: la clave es del componente, el valor sigue siendo el del sistema.

Ninguno.

1.2 A través de un privado (0)

Ninguno.

1.3 Literales (0)

Ninguno.

1.4 Excepciones firmadas (1) — fuera del ratio

Y cuatro declaraciones RETIRADAS por muertas (no son excepción: dejaron de existir): [data-chat-composer-context-close] > svg (1em en los dos ejes) y [data-chat-composer-send] > svg (1.1em). Los dos glifos son Icon compuestos y el Icon escribe style="width: var(--icon-size-{k})" EN LÍNEA: ningún selector gana a un estilo en línea. Medido — cierre 14 px (--icon-size-xs) contra los 13,3 px de 1em; envío 16 px (--icon-size-sm) contra los 14,7 px de 1.1em. Retirarlas da 0 diffs.

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.

# fichero:línea selector propiedad valor
1 chat-composer.css:123 [data-chat-composer-input] inline-size 100%

2. Sistema transversal (5) — 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-composer.css:78 [data-chat-composer-context-close]:hover background-image linear-gradient(var(--state-hover), var(--state-hover))
2 chat-composer.css:82 [data-chat-composer-context-close]:active background-image linear-gradient(var(--state-press), var(--state-press))
3 chat-composer.css:160 [data-chat-composer-send]:hover background-image linear-gradient(var(--state-hover), var(--state-hover))
4 chat-composer.css:164 [data-chat-composer-send]:active background-image linear-gradient(var(--state-press), var(--state-press))
5 chat-composer.css:177 [data-chat-composer-context-close]:focus-visible, [data-chat-composer-send]:focus-visible outline var(--focus-ring-width) solid var(--focus-ring-color)

3. Privados de la receta — ¿de dónde sale su valor?

La receta no declara privados propios en su CSS.

4. Propuesta de corrección

4.1 Tokens a declarar en lib/recipes/base.ts (0)

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-composer-…) scope TSC valor propuesto usos

4.2 Sin nombre mecánico (1)

  • ⚠ decisión: 100% es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar — 1: inline-size.

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

EJECUTADO 2026-08-23 — 79 % → 100 %. Contrato 31 → 33 claves; censo público 34 · global 0 · literal 0 · sistema 5 · excepción 1. Centinela 29/33, 4 adjudicadas. component-audit PASS · eidos-lint 0 invalid · rtl:check 0 · docs:check 0 · check sin errores del componente.

La mitad de su contrato no la montaba la demo. El guard leía 14 muertas de 31, y once eran la barra de contexto (context-*, opt-in tras un chip) más la bandeja de adjuntos (attachments-gap, opt-in tras un botón). Con las dos encendidas por prepareWith, 29/33 sin tocar el código. El resto es lo de siempre: enciende sólo lo que no tapa y cuenta los nodos (9 aquí, con los dos glifos).

Lo acuñado son DOS claves, no cuatro. El generador partía el chip de descarte en width + height y marcaba conflicto porque metía en el mismo saco la caja (26 px) y su glifo (1em). Son dos cosas distintas: la caja es un control cuadrado —una clave, context-close-size, como send-size en esta misma receta— y el glifo no es un knob de tema.

Cuatro declaraciones RETIRADAS por muertas. Los dos > svg (el 1em del cierre y el 1.1em del envío) no pueden ganar: el Icon compuesto emite style="width: var(--icon-size-{k})" INLINE. Medido: 14 px contra 13,3 px, y 16 px contra 14,7 px. Retirarlas da 0 diffs. Es la misma lección que chat-log la misma tarde, con dos glifos en vez de uno.

Y una colisión ENTRE RECETAS que no es del instrumento: --chat-composer-disabled-opacity no alcanza porque file-upload barre DESCENDIENTES — [data-file-upload] [data-disabled] pesa (0,2,0), lo mismo que [data-chat-composer-send][data-disabled], y su receta se emite MÁS TARDE. El composer vive dentro de un FileUpload en su composición DOCUMENTADA (su propio README y su demo), así que pierde siempre. Fuera del envoltorio alcanza (0.4 → 0.123 en el send y en el shell). Los dos defaults son --opacity-disabled, así que la colisión es invisible hasta que un tema mueva uno. No se arregla aquí (tocar file-upload es cascadear): se adjudica.

Las otras tres adjudicaciones son hábitos del guard: focus-border (sólo bajo :focus-within, y el guard blurea tras abrir; enfocado el textarea REAL, color(srgb …/0.48) → rgb(1, 2, 3)) y el par de la transición, que ES la transición que el guard congela (0,12 s → 4,321 s sin congelar).

El diff de computed NO salió limpio del todo, y la causa está medida: 2-4 valores de [data-chat-composer-attach] blockSize, que flota entre 30 px y 36 px. Flota igual corriendo dos veces el MISMO código (control: after vs after2, 2 diffs). La causa: en una recarga de cada ocho, el Button size="sm" variant="ghost" que la demo compone en esa ranura computa 36 px y fondo oklch(0.5556 0.1829 305.86) —el sólido primario— con sus data-size y data-variant puestos: las reglas de talla/variante de button llegan tarde en el dev server. Un nodo que esta receta no dimensiona, y una carrera de CSS del servidor de desarrollo. Los 2.204 valores restantes, idénticos.

Lo que queda fuera y por qué: una identidad firmada (§1.4) y cinco knobs de sistema transversal (§2). El componente está CERRADO en su número.

Nota 2026-08-24 — la adjudicación de disabled-opacity de aquí arriba era FALSA, y la firma la retira. file-upload acotó su barrido a sus cinco partes propias, así que la colisión ya no existe. Pero además la cuenta que la justificaba estaba incompleta: «invisible hasta que un tema mueva uno» es cierto para las dos reglas PROPIAS del composer —[data-chat-composer][data-disabled] y [data-chat-composer-send][data-disabled] empataban a 0.4 y ahí se quedaban— y falso para el textarea, que esa lectura no miró. [data-textarea] y [data-textarea-input] no declaran atenuación propia (y esta ficha ya declara el wrapper «celda neutra»), así que el barrido les imponía 0.4 dos veces más sobre el 0.4 legítimo del provider. La opacidad multiplica: 0.4³ = 0.064 de alpha efectiva en el TEMA POR DEFECTO, seis veces más apagado de lo diseñado, con el placeholder casi ilegible — 925 de 25.568 px, 3,6 % de la superficie, sin tocar un tema.

Recuperado, medido: exactamente 6 de 25 valores del estado disabled —[data-textarea] opacity 0.4 → 1, alpha 0.16 → 0.4 y cursor default → auto; [data-textarea-input] opacity 0.4 → 1 y alpha 0.064 → 0.4; y el cursor default → auto del provider del composer—. Nada más: send y el provider siguen en 0.4 porque ambos defaults son --opacity-disabled; lo que recuperan es que su --chat-composer-disabled-opacity vuelve a alcanzar. Centinela 29 → 30/33 y la entrada del ledger sale como STALE (retirada de theming-sentinel-exceptions.ts). La sonda estándar da 0 diffs reales — es CIEGA a esto: no tiene paso disabled y su filtro de nodos no incluye [data-textarea]; los 4 crudos son otra vez el blockSize flotante del attach ya documentado arriba, reproducido sobre código idéntico.

Powered by TurnKey Linux.