Checkbox
El Checkbox parece trivial hasta que uno cuenta sus estados (marcado, no marcado, indeterminado, disabled, focus, error) y sus modos (single, grupo con select-all, persistente entre sesiones). Esta ficha documenta el Checkbox del sistema en mayo de 2026, con énfasis en su estado indeterminado, históricamente la fuente del 80% de los bugs reportados.
Decomposición del componente
Las seis capas del componente
01 · Átomo color.neutral.950 · color.red.500 · space.4 · radius.xs Primitivos. Sin verdes ni ámbares como decoración: el checkbox usa el rojo accent solo para estado marcado.
02 · Compuesto color.checkbox.bg.unchecked · color.checkbox.bg.checked · color.checkbox.icon.checked · color.checkbox.border · space.checkbox.size Semánticos. El size 18px corresponde a la altura de x del cuerpo tipográfico.
03 · Regla contract test tres estados · hit-target ampliado 44×44 · grupo padre con estado mixed Governance específica del estado indeterminate: propagable solo vía JS. La técnica exige validación de grupo completo, no del item aislado.
04 · Pieza <Checkbox checked|indeterminate|unchecked label='...'> Tres estados semánticos. indeterminate solo se deriva de grupo (select-all), nunca se declara aisladamente.
05 · Familia Inputs / Form fields · emparentado con Radio y Toggle Checkbox es binario con confirmación posterior. Radio es excluyente. Toggle es binario con aplicación inmediata.
06 · Estado reposo · hover · focus-visible · checked · indeterminate · disabled · invalid · agent-aware indeterminate existe solo via JS, no como atributo HTML.
Tokens consumidos
Semánticos
color.checkbox.bg.unchecked oklch(0.99 0.005 75) Fondo en estado no marcado
color.checkbox.bg.checked oklch(0.53 0.18 26) Fondo cuando está marcado, color action primary
color.checkbox.icon.checked oklch(0.99 0.005 75) Icono check sobre fondo accent, contraste AAA
color.checkbox.border oklch(0.78 0.005 75) Borde unificado con resto de inputs
space.checkbox.size 1.125rem 18px, alineado con la altura de la x del cuerpo de texto
Técnicas de governance aplicadas
Técnicas activas
Estado indeterminado obligatorio
Si el componente recibe estado=indeterminate, el atributo HTML5 indeterminate se aplica via JS (no es propagable como atributo declarativo). El test cubre los tres estados visualmente y aria-checked='mixed'.
Hit-target ampliado
WCAG 2.5.5. La regla rechaza checkboxes con label de menos de 24 caracteres porque el área hit no alcanzaría el mínimo táctil.
Estado físico
Agentic-consumable desde noviembre de 2025. Un agente puede componer formularios con grupos de checkboxes; el contract incluye reglas para grupo (todo seleccionado, ninguno, mixed).
Estados soportados
hover, focus-visible, disabled, checked, indeterminate, invalid, agent-aware. El estado indeterminate es la única que requiere JavaScript para aplicarse (no existe atributo HTML).
Composiciones prohibidas
Reglas activas
indeterminate declarativo aislado
Agente que proponga checked='mixed' como atributo declarativo en un Checkbox aislado recibe rechazo. Solo grupo padre + hijos puede producir indeterminate.
Label inexistente o menor de 24 caracteres sin wrapper
La suma del cuadro (18px) más el label debe alcanzar hit-target 44×44. Labels muy cortos requieren wrapper clickeable ampliado.
Grupo sin fieldset/legend
Un grupo de checkboxes relacionados debe vivir dentro de fieldset con legend. Sin legend, el SR lee items inconexos.
Interoperabilidad
- Consumido por:
CheckboxGroup,FormRow,SettingsPanel,TableRowSelect. - Consume:
Labelasociado por for/id, opcionalmenteHelperTextyErrorMessage. - Con qué compone mal: junto a un Toggle con semántica parecida (decisión sin confirmar), dentro de Toast (efímero).
- Sustituibles por:
Togglesi el cambio es inmediato sin submit;Switches sinónimo no canónico (se rechaza).
Medición propuesta
Eventos planificados
checkbox.toggle
Atributos: from, to, group (si aplica), ruta. Permite detectar patrones donde el usuario marca y desmarca en secuencia rápida (indicador de error de interpretación).
checkbox.group_mixed_resolution
Si el grupo padre llega a all vs none desde mixed, qué acción lo disparó. Informa si el 'select all' se usa o si los usuarios resuelven item-a-item.
Mutación plástica (Estado 3)
Qué muta y qué permanece
Hit-target size-clickeable = derive(pointer × viewport) Touch amplía área clickeable del label a 48×48; desktop fine mantiene 44×44 por el wrapper ampliado.
Focus ring ring-intensity = derive(prefers-contrast) Con high-contrast el focus ring sube un tono de contraste y dobla grosor a 3px.
Animación del check duration = derive(prefers-reduced-motion) Con reduced-motion, el tick aparece instantáneo. Sin preferencia, fade-in 120ms.
Fijo por contract three-state enum · aria-checked mixed · label obligatorio Contract que el agente no muta.
Rollback automático si se propone ocultar el check visual en estado checked (el lector debe ver el estado siempre) o si el hit-target se compacta por debajo de 44×44 bajo cualquier pretexto contextual.
Genealogía
Árbol de evolución
<input type='checkbox'> nativo estilizado
Reemplazo CSS clásico con clip-path para esconder el control y SVG decorativo encima.
Consumidor Diseñador y desarrollador
Componente React + estado indeterminado
Primer support para grupos. El bug del estado mixed apareció en cuatro productos durante seis meses.
Consumidor Producto · 12 productos
Checkbox governable
Contract test que cubre los tres estados como matriz. Hit-target ampliado obligatorio. Migrado a accent-color CSS donde el navegador lo soporta.
Consumidor Producto · 28 productos
Checkbox.AI · expuesto vía MCP
Schema de grupo expuesto con estados (all, none, mixed). Agente tier-2 puede componer listas filtrables sin inventar estados.
Consumidor Humano + agente
Notas del editor
Descartes catalogados
Variants rechazadas
variant='switch-style' Checkbox con apariencia de switch. Confusión semántica: switch implica on/off inmediato, checkbox implica selección que se confirma luego. Se mantuvo como componente <Toggle /> separado con su contrato propio.
variant='tri-state-cycle' Click cíclico checked → indeterminate → unchecked. Imposible de explicar al usuario en menos de cinco palabras y rompía las expectativas de gestores de pantalla. Se restringió a estados binarios con indeterminate sólo derivado de grupo.
Referencias cruzadas
- Familia: Inputs / Form fields.
- Componente hermano: Nº 0251 · Radio.
- Componente relacionado: Nº 0252 · Toggle.