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

12 KiB

button — 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-23 · Alcance: 91% — 20 de 22 knobs por token público
  • Knobs de apariencia: 23 — público 20 · privado 2 · global 0 · literal 0 · sistema 1 · excepción 7 (los dos últimos, fuera del ratio)
  • Contrato hoy (lib/recipes/base.ts): 106 pública(s) — height-xs, height-sm, height-md, height-lg, height-xl, padding-inline-xs, padding-inline-sm, padding-inline-md, padding-inline-lg, padding-inline-xl, gap-xs, gap-sm, gap-md, gap-lg, gap-xl, font-family, line-height, letter-spacing, font-size-xs, font-size-sm, font-size-md, font-size-lg, font-size-xl, font-weight-xs, font-weight-sm, font-weight-md, font-weight-lg, font-weight-xl, icon-size-xs, icon-size-sm, icon-size-md, icon-size-lg, icon-size-xl, radius-sm, radius-md, radius-lg, radius-xl, radius-full, border-width, shadow, transition-duration, transition-ease, disabled-opacity, primary-track, primary-element, primary-border, primary-solid, primary-solid-hover, primary-text, primary-contrast, secondary-track, secondary-element, secondary-border, secondary-solid, secondary-solid-hover, secondary-text, secondary-contrast, neutral-track, neutral-element, neutral-border, neutral-solid, neutral-solid-hover, neutral-text, neutral-contrast, affirm-track, affirm-element, affirm-border, affirm-solid, affirm-solid-hover, affirm-text, affirm-contrast, fulfill-track, fulfill-element, fulfill-border, fulfill-solid, fulfill-solid-hover, fulfill-text, fulfill-contrast, risk-track, risk-element, risk-border, risk-solid, risk-solid-hover, risk-text, risk-contrast, threat-track, threat-element, threat-border, threat-solid, threat-solid-hover, threat-text, threat-contrast, loss-track, loss-element, loss-border, loss-solid, loss-solid-hover, loss-text, loss-contrast, palette-track, palette-element, palette-border, palette-solid, palette-solid-hover, palette-text, palette-contrast
  • Eje size: sí · ficheros: button.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 button.css:100 [data-button][data-gradient][data-variant='solid'] background-image var(--_button-fill-finish)
2 button.css:103 [data-button][data-gradient][data-variant='solid']:hover:not([data-disabled]):not([data-loading]) background-image var(--_button-fill-finish)

1.3 Literales (0)

Ninguno.

1.4 Excepciones firmadas (7) — 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 button.css:63 [data-button] inline-size fit-content
2 button.css:64 [data-button] block-size fit-content
3 button.css:244 [data-button][data-block] inline-size 100%
4 button.css:262 [data-button][data-icon-only] [data-button-body] inline-size 1px
5 button.css:263 [data-button][data-icon-only] [data-button-body] block-size 1px
6 button.css:309 [data-button-sr-only] inline-size 1px
7 button.css:310 [data-button-sr-only] block-size 1px

2. Sistema transversal (1) — 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 button.css:121 [data-button]:focus-visible 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?
--_button-height 6 var(--button-height-md), var(--button-height-xs), var(--button-height-sm), var(--button-height-lg), var(--button-height-xl) public sí
--_button-padding-inline 6 var(--button-padding-inline-md), var(--button-padding-inline-xs), var(--button-padding-inline-sm), var(--button-padding-inline-lg), var(--button-padding-inline-xl) public sí
--_button-gap 6 var(--button-gap-md), var(--button-gap-xs), var(--button-gap-sm), var(--button-gap-lg), var(--button-gap-xl) public sí
--_button-radius 6 var(--button-radius-md), var(--button-radius-sm), var(--button-radius-lg), var(--button-radius-xl), var(--button-radius-full) public sí
--_button-font-size 6 var(--button-font-size-md), var(--button-font-size-xs), var(--button-font-size-sm), var(--button-font-size-lg), var(--button-font-size-xl) public sí
--_button-font-weight 6 var(--button-font-weight-md), var(--button-font-weight-xs), var(--button-font-weight-sm), var(--button-font-weight-lg), var(--button-font-weight-xl) public sí
--_button-icon-size 6 var(--button-icon-size-md), var(--button-icon-size-xs), var(--button-icon-size-sm), var(--button-icon-size-lg), var(--button-icon-size-xl) public sí
--_button-bg 7 var(--button-palette-solid), var(--button-palette-track), transparent exception, public no
--_button-fg 7 var(--_button-finish-ink, var(--button-palette-contrast)), var(--button-palette-text) public sí
--_button-border 7 var(--button-palette-solid), transparent, var(--button-palette-border) exception, public no
--_button-hover-bg 7 var(--button-palette-solid-hover), var(--button-palette-element), var(--button-palette-track), transparent exception, public no
--_button-hover-border 7 var(--button-palette-solid-hover), transparent, var(--button-palette-border) exception, public no

Consumidos y no declarados en el CSS (vienen de base.ts o de un estilo inline del wrapper): --_button-fill-finish, --_button-finish-ink.

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 (6)

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 (--button-…) scope TSC valor propuesto usos
width root fit-content 1
height root fit-content 1
body-width root 1px 1
body-height root 1px 1
sr-only-width root 1px 1
sr-only-height root 1px 1

4.2 Sin nombre mecánico (3)

  • ⚠ decisión: el privado que alimenta este knob no se declara en el CSS (viene de base.ts o de un estilo inline) — hay que resolverlo antes de nombrarlo — 2: background-image.
  • ⚠ 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 — 55 % → 91 %, UNA clave nueva y 17 declaraciones anotadas. Y al medirlo salió lo más gordo del eje hasta hoy: 56 de sus 106 claves públicas no pintan nada, y no por deuda de este componente.

Lo acuñado es una sola cosa, la que faltaba: el paso full de la escala de radio. [data-rounded='full'] leía el primitivo global a pelo, así que la forma que más se retoca —la píldora— era el único escalón fuera del contrato.

Diecisiete declaraciones pasan a excepción firmada, y son de dos clases:

  • Identidad (7): fit-content × 2 (un botón ES su contenido), el 100 % de [data-block] (que ES «a todo el ancho») y los cuatro 1px de las dos cajas sr-only.

  • Ausencia (10): los transparent de soft, outline, ghost y plain. La variante ES la ausencia de cromo; ponerle fondo a un ghost es pedir un soft. Diez claves de transparent habrían sido el antipatrón que la propia doctrina avisa (agrupa por VALOR).

    Para que eso cuente, el censo aprende la válvula un piso más abajo: un PRIVADO cuyo valor es un literal CON su razón escrita es una ausencia firmada, no deuda. Sin eso, cuatro privados marcaban «no deriva» y seis knobs leían inalcanzables mientras TODAS las demás ramas del mismo privado leen un público.

⚠⚠ EL HALLAZGO: la cascada de paleta ANULA los tonos del componente. El último bloque del forward, [data-button][data-color], resuelve --button-palette-* desde el --palette-* GLOBAL —que [data-color='{tono}'] llena desde --color-{tono}-*— y gana a las reglas por tono [data-button][data-color='{tono}'] por ORDEN, con la misma especificidad (0,2,0). Medido sobre data-color='risk': --button-risk-solid no mueve nada ni en :root ni en el nodo, mientras --palette-solid y --color-risk-solid en el nodo repintan. Las siete primary-* son el RESPALDO de esa misma regla y sí actúan en una instancia SIN data-color.

No es de este componente: 49 recetas emiten el mismo forward genérico y el catálogo tiene 419 claves de tono. Medido igual en badge y callout. El arreglo cabe en el orden de emisión —sacar la regla genérica ANTES de las de tono— y no movería un píxel (el valor por defecto de --button-risk-solid ES var(--color-risk-solid)), pero cambia el contrato de la jaula del color: un ancestro que inyecte --palette-* dejaría de pisar un tono semántico. Es firma. → §13.

El techo restante (2 filas) es canal de valor: --_button-fill-finish, el acabado de degradado que el generador deriva por instancia. Su dial de tema existe y es del sistema (--gradient-finish-lift).

Los 23 rojos restantes, medidos: la escala de iconos pide un <svg> de verdad (la demo no lleva icono), la de radio es una prop OPT-IN que la demo no usa, transition-* es el punto ciego del congelado, disabled-opacity pide el estado, y las cuatro palette-* son nombres RESUELTOS: escribirlas en :root pierde por diseño (desde el nodo alcanzan).

Un error de método, anotado porque casi cuela: anclé la clave nueva en la primera línea 'radius-xl' del fichero y aterrizó en meter. Lo delató el navegador (--button-radius-full sin definir ⇒ border-radius a 0). Una inserción se ancla en el BLOQUE del componente, nunca en una línea de clave.

Verificación: diff de computed = 0 (416 valores, 7 estados; la demo monta UN botón) · equivalencia del paso full comprobada aparte (9999px antes y después, y el token lo mueve) · centinela 34/106, todo lo demás adjudicado por escrito · censo 91 % · component:audit PASS · suite eidos con el rojo conocido ajeno · rtl:check 0 · docs:check 0 · check sin errores propios · captura de las seis variantes + píldora + tono risk.

Powered by TurnKey Linux.