Convenciones

Accesibilidad

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.

Lo que garantiza el sistema

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

Foco visible, solo con teclado

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

Teclado completo

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

Movimiento reducido

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

Mejora progresiva

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

Cambios que se anuncian

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.

Pruébalo aquí

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.

Foco y teclado
Política de cancelación

Patrón y teclado de cada componente

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.

ComponentePatrónTeclado
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

Cómo probarlo

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.

Con el teclado

  1. Deja el ratón. Con Tab recorre todo: el orden sigue el de lectura y nunca te pierdes el foco.
  2. El anillo se ve en cada parada, también sobre la marca y sobre fotos.
  3. Enter y Espacio activan botones; Enter sigue enlaces.
  4. En un grupo (pestañas, radios, menús, el calendario) las flechas se mueven adentro y Tab sale del grupo.
  5. Esc cierra la capa de arriba (y solo esa) y el foco regresa al botón que la abrió.
  6. No hay trampas: solo un modal atrapa el foco, y Esc o su botón de cerrar te sacan.
  7. Lo que se arrastra (deslizar para reservar, la hoja lateral) también se hace sin arrastrar.

Con un lector de pantalla

  • VoiceOver en Mac (Cmd+F5; el rotor con Ctrl+Opción+U lista encabezados, enlaces y controles), NVDA en Windows (gratis) y TalkBack en Android.
  • Cada botón dice qué hace, también los de ícono («Cerrar aviso», no «botón»).
  • Cada campo lee su etiqueta, su ayuda y, si lo hay, su error.
  • Los cambios se anuncian una vez («Quedan 2 lugares»), no en cada tecla ni en cada cuadro de la animación.
  • Los encabezados arman un índice que tiene sentido sin ver la página.
  • Lo decorativo calla: el eclipse del cupo cuando el cupo ya va en texto, las partículas, los puntos de las pastillas.

Movimiento, zoom y pantalla chica

  • Activa «Reducir movimiento» (macOS: Accesibilidad › Pantalla; en Chrome: DevTools › Rendering › prefers-reduced-motion) o pon el dial de movimiento en 0 en Movimiento: todo debe seguir funcionando, solo que quieto.
  • Acerca al 200%: el texto crece, nada se encima ni se corta.
  • A 375px de ancho no hay scroll horizontal; todo blanco táctil mide al menos 24px (WCAG 2.2) y los controles, 32px o más (h-control-sm).
  • Corre el guardián de contraste (ver Calidad) si tocaste un color o una personalidad.

Errores comunes

En lugar deHazPor 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.