10 KiB
textarea — 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-22 · Alcance: 80% — 20 de 25 knobs por token público
- Knobs de apariencia: 28 — público 20 · privado 2 · global 0 · literal 3 · sistema 3 · excepción 0 (los dos últimos, fuera del ratio)
- Contrato hoy (
lib/recipes/base.ts): 37 pública(s) —input-font-size-xs,input-font-size-sm,input-font-size-md,input-font-size-lg,input-font-size-xl,input-padding-inline-xs,input-padding-inline-sm,input-padding-inline-md,input-padding-inline-lg,input-padding-inline-xl,input-padding-block-xs,input-padding-block-sm,input-padding-block-md,input-padding-block-lg,input-padding-block-xl,input-font-size,input-padding-inline,input-padding-block,gap,input-radius,input-border-width,input-border,hover-input-border,focus-input-border,invalid-input-border,input-bg,input-fg,input-placeholder-fg,input-font-family,input-font-weight,input-line-height,count-font-size,count-gap,count-fg,count-font-family,overflow-count-fg,overflow-count-font-weight· 1 privada(s) forward —_palette-solid - Eje
size: no · ficheros:textarea.css
1. Knobs fuera de alcance
1.1 Directo a primitivo global (0)
Ninguno.
1.2 A través de un privado (2)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | textarea.css:71 |
[data-textarea][data-focused] [data-textarea-input] |
border-color |
var(--_textarea-border-focus) |
| 2 | textarea.css:91 |
[data-textarea][data-variant='ghost'][data-focused] [data-textarea-input] |
border-color |
var(--_textarea-border-focus) |
1.3 Literales (3)
| # | fichero:línea | selector | propiedad | valor |
|---|---|---|---|---|
| 1 | textarea.css:19 |
[data-textarea] |
inline-size |
100% |
| 2 | textarea.css:47 |
[data-textarea-input] |
inline-size |
100% |
| 3 | textarea.css:113 |
[data-textarea-count] |
line-height |
1 |
1.4 Excepciones firmadas (0) — fuera del ratio
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.
Ninguno.
2. Sistema transversal (3) — 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 | textarea.css:74 |
[data-textarea][data-focused] [data-textarea-input] |
outline |
var(--focus-ring-width) solid var(--focus-ring-color) |
| 2 | textarea.css:96 |
[data-textarea-input][data-disabled] |
opacity |
var(--opacity-disabled) |
| 3 | textarea.css:122 |
[data-textarea-count-sep] |
opacity |
var(--opacity-muted) |
3. Privados de la receta — ¿de dónde sale su valor?
| privado | declaraciones | valor(es) | origen | ¿deriva de un público? |
|---|---|---|---|---|
--_textarea-border-focus |
2 | var(--textarea-focus-input-border), var(--_textarea-palette-solid) |
private, public | no |
--_textarea-resize |
4 | vertical, none, horizontal, both |
literal | no |
Consumidos y no declarados en el CSS (vienen de base.ts o de un estilo inline del wrapper): --_textarea-palette-solid.
4. Propuesta de corrección
4.1 Tokens a declarar en lib/recipes/base.ts (2)
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 (--textarea-…) |
scope TSC | valor propuesto | usos |
|---|---|---|---|
input-border (ya existe) |
root |
⚠ var(--textarea-focus-input-border) / var(--_textarea-palette-solid) |
2 |
ghost-input-border |
root |
⚠ var(--textarea-focus-input-border) / var(--_textarea-palette-solid) |
2 |
4.2 Sin nombre mecánico (3)
- ⚠ decisión:
100%es un valor identidad o geometría de layout, no un knob de tema — el perímetro de «knob» es D-TH.2, sin firmar — 2:inline-size. - ⚠ decisión:
1es 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:line-height.
4.3 Avisos sobre los tokens propuestos (1)
- el privado
--_textarea-border-focusdebe pasar a leer este público (o desaparecer) —--textarea-input-border,--textarea-ghost-input-border
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, unbackgrounden 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
Medido 2026-08-21 (sonda ×2 sobre el mismo código = 0 diffs en 1.247 valores · 8 estados). La §4 propone 35 tokens y cuatro de ellos son colisiones que hay que partir o retirar, más un error que cambiaría el default. Corregido queda en 37 claves.
count-font-size-{k}× 5 BORRARÍA el factor 0.85 — es el error grave de la propuesta. El CSS calculacalc(var(--_textarea-font-size) * 0.85)(con su anotación/* literal: */), y la §4 propone cinco tokens con el font-size del INPUT crudo: el contador crecería un 18 % en las cinco tallas. D-TH.5 lo prohíbe. La corrección: UN tokencount-font-sizeque conserva elcalcsobre el público del input, así sigue la talla sin repetir la escala. Mismo reparto quetree-grid.guide-fg— el knob es el valor, la construcción se queda en la receta.input-fg⚠ son DOS knobs: la tinta del texto (--color-content-primary) y la del placeholder (--color-content-muted). El clasificador las fundió por caer ambas sobre la parteinput. →input-fg+input-placeholder-fg(precedente exacto:command.input-fg/command.input-placeholder-fg).count-fg⚠ son DOS knobs: reposo (content-muted) y rebasado (threat-text). →count-fg+overflow-count-fg;overflowes valor dedata-overflow, declarado en el morfo (textarea.ts:163), así que entra como modificador delante.input-border⚠ NO son dos knobs, y su nombre está mal. Los dos valores son UNA variable (--_textarea-border-focus) con su swap de paleta:--focus-ring-colorde base y--_textarea-palette-solidcuando haydata-color. El swap es el forward THM-2 y se queda privado. Y el knob es el borde de FOCO, no el de reposo →focus-input-border. El de reposo (--color-neutral-border) es otro token, que la §4 ni listó porque viaja dentro del atajoborder:.ghost-input-border⚠ se RETIRA de la propuesta: la regla ghost lee exactamente la misma--_textarea-border-focusque la regla base. Un token propio sería un duplicado con dos nombres para un valor.bordershorthand se parte eninput-border-width+input-border(color) — moldecommand.input-border/input-border-width.- Qué casa con el bundle, medido (
generated/base.css:409-450):font-sizecasa 1:1 en las cinco (font-size-{k}ES--size-{k}-font-size) → van al bundle.padding-inlinecasa en sm/md/lg/xl pero NO en xs: el textarea pone--space-2(8px) donde el bundle dice--space-1-5(6px).padding-blockno casa en ninguna salvo xs: sm 6px vs 4px, md 8px vs 6px, lg 12px vs 8px. El campo multi-línea respira más que un control de una línea, y es correcto. Los dos ejes de spacing van verbatim en--space-*, con la desviación escrita: el guard del bundle gobierna control-height / font-size / icon-size, no el spacing (precedentetable.cell-padding-*ytree-grid.row-padding-*). Los resueltos sinparts—data-sizese estampa en[data-textarea]y el input hereda; moldecommand.
- Fuera del contrato, a propósito: los dos
inline-size: 100%y elline-height: 1del count (identidad, D-TH.2);--_textarea-resize, que no es apariencia sino el enum de comportamiento que el morfo conduce; y el forward de paleta. - Alcance final 80 % (20 de 25) — mi propia predicción de «≈100 %» era
errónea y la medición la corrigió. Cero globales y cero privados de
valor: los 16 de la propuesta leen ya su público. Lo que queda son las
DOS declaraciones que consumen
--_textarea-border-focus(base y ghost), que el censo cuenta comoprivateporque el privado sobrevive a propósito — es el conmutador de una variable con dos fuentes (el token público y el forward de paleta), y aplanarlo exigiría duplicar la regla por color. Es el mismo techo delistbox(68 % por consumir bien su capa) y el mismo que §13 registra para los forwards THM-2: la métrica penaliza hacer lo correcto. Más los tres literales de identidad (dos100%y elline-height: 1del contador), fuera del perímetro por D-TH.2.