Contraste AA medido
Cada texto del contrato se mide sobre las superficies donde se usa: 22 pares en 14 escenarios (6 personalidades y theme.css, en claro y oscuro), mínimo 4.5:1.
text-muted-foreground · AA
Convenciones
Lo que cumple cada pieza y cómo comprobarlo.
Un cliente reserva con el pulgar en el metro, una maestra pasa lista con el teclado, alguien más escucha la página con un lector de pantalla. Los tres usan los mismos componentes, así que la accesibilidad vive dentro de cada pieza, no en una revisión al final: si usas el componente, ya cumple.
Cada texto del contrato se mide sobre las superficies donde se usa: 22 pares en 14 escenarios (6 personalidades y theme.css, en claro y oscuro), mínimo 4.5:1.
text-muted-foreground · AA
Un anillo nítido aparece con Tab y nunca con el clic. Es el único color fuera de la marca (azul en carbón) y sigue el dial --eui-focus.
eui-focus · :focus-visible
Cada pieza implementa el patrón ARIA de su tipo con sus teclas: flechas en grupos, Esc en capas, Enter y Espacio en todo lo que se activa.
role · tabindex itinerante
Con prefers-reduced-motion o --eui-motion en 0 los resortes saltan al final y las animaciones CSS se quedan quietas. Nada depende de ver la animación.
eclipse_ui/spring · --eui-m
Sin JavaScript queda el control nativo: el select debajo del select con resorte, los input date del selector de fechas, details en el acordeón, popovertarget en los menús.
HTML primero
Lo que cambia sin recargar (un crédito menos, un aviso nuevo, el cupo) se anuncia una vez en una región aria-live cortés; role=alert solo para lo urgente.
aria-live=polite · role=status
Los valores de contraste en vivo, por personalidad, están en Color.
text-faint y text-subtle quedan fuera del contrato a propósito: son para
metadatos e íconos, no para texto que haya que leer.
Pon el cursor en la página y recorre esta muestra con Tab, Mayús+Tab, Espacio y las flechas: el anillo aparece solo con el teclado.
Sale de las fichas (pattern: y keyboard:); 42 de
42 lo declaran y la prueba del catálogo no deja entrar una ficha sin ellos. La
ficha de cada pieza tiene el detalle.
| Componente | Patrón | Teclado |
|---|---|---|
| Actividad de la semana | Barras role=img + tabla equivalente | Una parada de Tab · ←/→ · Inicio/Fin |
| Actividad reciente | Sección con nombre + lista ordenada + aria-live | Tab por los enlaces de cada fila |
| Control segmentado | nav (aria-current), radiogroup o tabs según el modo | Tab · ←/→ · Inicio/Fin en tabs |
| Encabezado de página | header + migas (nav, aria-current) | Tab |
| Estado vacío | Encabezado real + acciones nativas | Tab a la acción |
| Número del panel | Texto completo (tooltip extra) | Tab al enlace |
| Pastilla de estado | Texto (el punto es decorativo) | No es interactivo |
| Tabla del admin | Tabla nativa (caption, th scope) | Tab al enlace de cada fila · flechas en la caja con scroll |
| Tarjeta de precio | article + acción nativa | Tab a la acción |
| Botón con estados | Botón nativo + aria-busy + región aria-live | Enter o Espacio |
| Botón con luz | Botón nativo (la luz es decorativa) | Enter o Espacio |
| Botón magnético | Botón o enlace nativo | Enter o Espacio |
| Botón que se hunde | Botón o enlace nativo | Enter o Espacio (se hunde mientras se sostiene) |
| Celebración | Decorativo (aria-hidden) | No es interactivo |
| Deslizar para reservar | Botón + región aria-live | Enter o Espacio confirma sin arrastrar |
| Tarjeta con luz | Enlace estirado con nombre propio | Tab · Enter |
| Número que rueda | Texto real + aria-live (rodillos decorativos) | No es interactivo |
| Pila de avatares | Lista con nombre + tooltip | Una parada de Tab · ←/→ · Inicio/Fin · Esc |
| Aviso | role=alert (urgente) o role=status (cortés) | Tab a las acciones y a la ✕; Enter o Espacio descarta |
| Campana de notificaciones | Botón + panel role=dialog | Enter o Espacio abre · Esc cierra y regresa el foco |
| Pila de avisos | Región con nombre; cada aviso role=status | Tab a cada aviso · Esc cierra el enfocado |
| Confirmación | alertdialog modal (dialog) | Tab atrapado · Esc cancela · Enter o Espacio sostenido para mantener |
| Hoja lateral | Dialog modal (dialog) | Tab atrapado · Esc cierra · arrastrar es opcional |
| Menú de acciones | Menu button (menu, menuitem) | ↓/Enter/Espacio abre · ↑/↓ · Inicio/Fin · letra · Esc · Tab |
| Modal | Dialog modal (dialog + showModal) | Tab atrapado · Esc cierra · el foco regresa al disparador |
| Popover | Botón + dialog no modal | Enter o Espacio abre · el foco entra · Esc regresa |
| Tooltip direccional | Tooltip (role=tooltip, aria-describedby) | Aparece con el foco · Esc lo oculta |
| Acordeón suave | Disclosure nativo (details / summary) | Tab entre títulos · Enter o Espacio abre y cierra |
| Pestañas animadas | Tabs (tablist, tab, tabpanel) | ←/→ con vuelta · Inicio/Fin · Tab al panel |
| Campo de color | Radios nativos (muestras) + campo hex | Flechas entre muestras · Tab al hex |
| Campo de texto | Campo nativo (for, aria-describedby, aria-invalid) | Tab · la ✕ y el ojo también se alcanzan |
| Casilla que se dibuja | Checkbox nativo | Espacio marca y desmarca |
| Código de verificación | role=group + one-time-code | Teclea y avanza · ←/→ · Inicio/Fin · Retroceso · pegar llena |
| Formulario | Formulario nativo + resumen role=alert | Tab · cada error enlaza a su campo |
| Interruptor | Switch (checkbox role=switch) | Espacio · Enter |
| Select con resorte | Combobox select-only (listbox) | Flechas · Inicio/Fin · RePág/AvPág · letra · Enter · Esc · Tab |
| Select nativo | Select nativo | El del sistema |
| Selector de fechas | Diálogo con cuadrícula (grid) | Flechas día y semana · RePág/AvPág mes · Inicio/Fin · Enter elige · Esc |
| Selector numérico | Spinbutton (input number) | ↑/↓ un paso · RePág/AvPág diez · Inicio/Fin |
| Tarjetas de opción | Radios nativos (fieldset, legend) | Flechas eligen · Tab sale del grupo |
| Zona para subir archivos | Input file nativo + aria-live | Enter o Espacio abre el selector · Tab a cada Quitar |
| Área de texto | Campo nativo + aria-live al acercarse al límite | Tab |
Antes de dar por terminada una pieza o una vista, cinco minutos con cada lista. Ningún lector automático encuentra lo que encuentran estas.
h-control-sm).| En lugar de | Haz | Por qué |
|---|---|---|
| Un botón de ícono sin nombre, confiando en el tooltip | aria-label="Cerrar aviso" en el botón; el tooltip es la versión visible |
En el teléfono no hay hover: el tooltip no aparece y el botón se lee «botón». |
| El estado solo con color (un punto verde) | Punto + palabra: Ui::StatusPillComponent con su label: |
Uno de cada doce hombres no distingue bien el rojo del verde, y el lector de pantalla no ve colores. |
text-faint para texto que hay que leer |
text-muted-foreground (AA medido) |
text-faint es para metadatos; no está en el contrato de contraste. |
Un div con data-action="click->…" |
Un <button> (o un <a> si navega) |
El div no recibe foco, no responde a Enter y no tiene rol. |
outline-none sin nada a cambio |
eui-focus |
Quien navega con teclado se queda sin saber dónde está. |
| El placeholder como etiqueta | label: siempre; hide_label: true si no cabe a la vista |
El placeholder se borra al escribir y muchos lectores no lo leen. |
aria-live en todo el bloque |
Una región cortés solo para el cambio («Te quedan 5 créditos») | El lector repite el bloque entero en cada cambio y la persona deja de escuchar. |
role="alert" para avisar que algo salió bien |
role="status"; alert solo para errores y urgencias |
alert interrumpe lo que se esté leyendo. |
| Un enlace o botón dentro de una tarjeta que ya es enlace | Enlace estirado en el título y la acción encima (relative z-10) |
Los interactivos anidados son HTML inválido y el lector los lee revueltos. |
| «Clic aquí», «Ver más» repetido | Texto que dice adónde lleva: «Ver horario de la semana» | En la lista de enlaces del lector, «Ver más» ×6 no dice nada. |
| Una animación con duración fija o infinita sin salida | eclipse_ui/spring o duraciones con nombre divididas entre --eui-m |
Con movimiento reducido debe quedarse quieta, y el dial debe poder apagarla. |
| Abrir una capa y dejar el foco donde estaba | Ui::ModalComponent, Ui::SheetComponent o Ui::PopoverComponent |
Ya llevan el foco adentro, lo atrapan si deben y lo regresan al cerrar. |