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/field.md

12 KiB

field — 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-24 · Alcance: 98% — 58 de 59 knobs por token público
  • Knobs de apariencia: 64 — público 58 · privado 1 · global 0 · literal 0 · sistema 5 · excepción 9 (los dos últimos, fuera del ratio)
  • Contrato hoy (lib/recipes/base.ts): 68 pública(s) — gap-xs, gap-sm, gap-md, gap-lg, gap-xl, font-family, label-gap, label-font-weight, label-line-height, label-fg, invalid-label-fg, disabled-label-fg, floating-label-top, floating-label-padding-inline, layout-column-gap, required-fg, optional-fg, optional-font-weight, control-height-xs, control-height-sm, control-height-md, control-height-lg, control-height-xl, segment-height, control-padding-inline-xs, control-padding-inline-sm, control-padding-inline-md, control-padding-inline-lg, control-padding-inline-xl, control-gap-xs, control-gap-sm, control-gap-md, control-gap-lg, control-gap-xl, control-font-size-xs, control-font-size-sm, control-font-size-md, control-font-size-lg, control-font-size-xl, control-line-height, control-font-weight, control-radius, control-border-width, control-border, hover-control-border, focus-control-border, invalid-control-border, control-bg, control-ghost-border, control-ghost-bg, control-bg-readonly, disabled-control-bg, control-fg, control-placeholder-fg, affix-fg, control-trigger-size, control-trigger-radius, control-trigger-fg, hover-control-trigger-fg, focus-control-trigger-fg, segment-active-bg, segment-active-text, message-line-height, helper-fg, error-fg, transition-duration, transition-ease, disabled-opacity · 1 privada(s) forward — _palette-text
  • Eje size: sí · ficheros: field-control-trigger.css, field-segment-state.css, field.css

1. Knobs fuera de alcance

1.1 Directo a primitivo global (0)

Ninguno.

1.2 A través de un privado (1)

# fichero:línea selector propiedad valor
1 field.css:320 [data-field][data-floating-label]:focus-within > [data-field-label] color var(--_field-palette-text)

1.3 Literales (0)

Ninguno.

1.4 Excepciones firmadas (9) — 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.

# fichero:línea selector propiedad valor
1 field-control-trigger.css:45 [data-field-control-trigger] :is(svg, [data-icon]) inline-size 1em
2 field-control-trigger.css:46 [data-field-control-trigger] :is(svg, [data-icon]) block-size 1em
3 field.css:13 [data-field] inline-size 100%
4 field.css:139 [data-field-required-indicator] line-height 1
5 field.css:144 [data-field-optional-indicator] font-size 0.85em
6 field.css:146 [data-field-optional-indicator] line-height 1
7 field.css:159 [data-field-control] inline-size 100%
8 field.css:208 [data-field-input] inline-size 100%
9 field.css:299 [data-field][data-floating-label] > [data-field-control] 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 field-control-trigger.css:50 [data-field-control-trigger]:hover:not([data-disabled]):not(:disabled) background-image linear-gradient(var(--state-hover), var(--state-hover))
2 field-control-trigger.css:55 [data-field-control-trigger]:focus-visible outline var(--focus-ring-width) solid var(--focus-ring-color)
3 field-control-trigger.css:63 [data-field-control-trigger][data-disabled], [data-field-control-trigger]:disabled opacity var(--opacity-disabled)
4 field-segment-state.css:33 [data-field-segment]:not([data-segment='literal']):not([data-readonly]):not([data-disabled]):not( :focus ):not(:focus-visible):hover background-image linear-gradient(var(--state-hover), var(--state-hover))
5 field.css:187 [data-field-control]:focus-within outline var(--focus-ring-width) solid var(--focus-ring-color)

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

privado declaraciones valor(es) origen ¿deriva de un público?
--_field-gap 5 var(--field-gap-md), var(--field-gap-xs), var(--field-gap-sm), var(--field-gap-lg), var(--field-gap-xl) public sí
--_field-control-height 5 var(--field-control-height-md), var(--field-control-height-xs), var(--field-control-height-sm), var(--field-control-height-lg), var(--field-control-height-xl) public sí
--_field-control-padding-inline 5 var(--field-control-padding-inline-md), var(--field-control-padding-inline-xs), var(--field-control-padding-inline-sm), var(--field-control-padding-inline-lg), var(--field-control-padding-inline-xl) public sí
--_field-control-gap 5 var(--field-control-gap-md), var(--field-control-gap-xs), var(--field-control-gap-sm), var(--field-control-gap-lg), var(--field-control-gap-xl) public sí
--_field-control-font-size 5 var(--field-control-font-size-md), var(--field-control-font-size-xs), var(--field-control-font-size-sm), var(--field-control-font-size-lg), var(--field-control-font-size-xl) public sí
--_field-control-border 2 var(--field-control-border), var(--field-control-ghost-border) public sí
--_field-control-bg 4 var(--field-control-bg), var(--field-control-ghost-bg) public sí

Consumidos y no declarados en el CSS (vienen de base.ts o de un estilo inline del wrapper): --_field-palette-text.

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

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

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 (--field-…) scope TSC valor propuesto usos
control-trigger-width root 1em 1
control-trigger-height root 1em 1
optional-indicator-font-size root 0.85em 1

4.2 Sin nombre mecánico (7)

  • ⚠ 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 — 4: inline-size.
  • ⚠ 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 — 2: line-height.
  • ⚠ decisión: el privado que alimenta este knob no se declara en el CSS (viene de base.ts o de un estilo inline) — hay que resolverlo antes de nombrarlo — 1: color.

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 — 73 % → 98 %, 8 claves nuevas (60 → 68) y 8 literales ANOTADOS. El 2 % que falta es el PUENTE DE PALETA (_palette-text, THM-2), que no es deuda. Y el guard, que arrastraba 18 rojos, queda en verde con sus veinte adjudicaciones escritas.

Es el componente más COMPUESTO del sistema: lo que otras fichas llaman «préstamo con dueño» se cobra aquí. Por eso el techo importa más que en otros.

Lo acuñado:

  • El disparador de control (field-control-trigger.css, el icono que abre el calendario / el reloj / la muestra, o revela la contraseña): size, radius, fg y los dos estados (hover- y focus-). Su caja se mide en em a propósito —la afordancia crece con la fuente del campo—, así que el token guarda la RAZÓN (1.75em), no un píxel por talla.
  • El indicador optional, gemelo del required que ya tenía token: tinta y peso.
  • El lateral de la etiqueta flotante (floating-label-padding-inline): es lo que CORTA el borde bajo el texto, así que un tema que mueva el padding mueve el corte.

Ocho literales pasan a excepción firmada, y no por inflar el ratio: cuatro inline-size: 100% (el campo, el control, el input y el control flotante llenan su fila), dos line-height: 1 (un glifo suelto no tiene interlínea) y dos 1em del glifo dentro del disparador (el icono ES el tamaño del texto). Ninguno es una perilla: son identidades. Llevan su /* literal: … */ con la razón, que es la válvula de recipe-contract §3.

Los 20 rojos del guard estaban VIVOS, uno por uno — y tres enseñan algo:

  • disabled-opacity vive en el CONTROL, no en la raíz: el guard leía el nodo equivocado.
  • control-placeholder-fg no leía porque el input de la demo NO TIENE placeholder: sin atributo no hay caja ::placeholder, y getComputedStyle(nd, '::placeholder') devuelve entonces el estilo del ELEMENTO. Con un placeholder puesto, alcanza. Es genérico: cualquier token de placeholder leerá muerto sobre un input vacío.
  • segment-height es un token de FAMILIA que field posee y los pickers consumen: en las rutas de field no lo lee nadie. Y además es RESUELTO (declarado por [data-field][data-size]), así que una escritura en :root pierde POR DISEÑO — el tema mueve la coordenada. Medido en /time-picker: desde :root nada, desde el host del campo 28px → 1234px, y moviendo --field-control-height-md 28px → 992px.

El resto son estado (invalid, disabled, readonly, foco), variante (ghost), orientación (horizontal), la franja de segmentos (medida en /date-field con el segmento pulsado), el texto de error —que sólo se RENDERIZA con el interruptor de la demo: forzar el atributo no hace que Svelte monte el nodo— y los dos tokens de transición, que el guard no puede medir porque los congela.

Instrumento: el disparador de control no lo monta NINGUNA demo de field (/field lleva inputs planos y los segmentados llevan segmentos), así que sus cuatro tokens leían muertos; el guard mide ahora field sobre DOS rutas (/field + /date-picker).

Verificación: diff de computed = 0 sobre 2.280 valores en 8 estados · equivalencia del disparador comprobada aparte en /date-picker (28px = 1.75em con fuente 16px · 4px = --radius-sm · misma tinta) · centinela 48/68 con las 20 adjudicadas · censo 98 % · --names 0 desviadas · component:audit PASS · rtl:check 0 · docs:check 0 · suite eidos con el rojo conocido ajeno · check sin errores propios · capturas de reposo, invalid, disabled, etiqueta flotante (en reposo y con foco) y disparador en hover.

Powered by TurnKey Linux.