Accesibilidad
El widget se ha construido para cumplir WCAG 2.2 AA. Esta página enumera lo que el widget hace por ti y lo que tu integración debe seguir respetando.
Cumplimiento por criterio
| Criterio WCAG | Implementación |
|---|---|
| 1.4.3 Contraste mínimo | Tokens light/dark con contraste ≥ 4.5:1 en texto y 3:1 en componentes UI. |
| 1.4.10 Reflow | Layout responsive sin scroll horizontal a 320 px. |
| 1.4.13 Content on hover/focus | Tooltips dismissibles con Esc y persistentes hasta blur. |
| 2.1.1 Keyboard | Toda la UI es accesible por teclado. Sin trampas. |
| 2.1.2 No keyboard trap | Los modales atrapan foco con escape vía Esc y restauración. |
| 2.4.3 Focus order | Orden lógico: input → toolbar (voz/imagen) → resultados → filtros. |
| 2.4.7 Focus visible | Anillos de foco con --nrn-primary a 4 px. |
| 3.2.2 On input | El submit no se dispara hasta que el usuario presiona Enter o el botón. |
| 3.3.2 Labels | Todos los inputs tienen aria-label o <label> asociado. |
| 4.1.2 Name, role, value | combobox, listbox, option, dialog, status correctamente aplicados. |
| 4.1.3 Status messages | Cambios de estado anunciados con role="status" (resultados cargados, filtros aplicados). |
Shadow DOM y herramientas asistivas
El widget se monta en un Shadow DOM en modo open. Los lectores de pantalla modernos (NVDA, JAWS, VoiceOver, TalkBack) navegan correctamente entre los nodos del Shadow Tree y el documento del host.
Navegación por teclado
| Tecla | Acción |
|---|---|
Tab / Shift+Tab | Mover foco entre elementos del widget. |
Enter | Lanzar la búsqueda con la query del input. Activa botones y enlaces. |
Esc | Cerrar modal/drawer/dropdown abierto. Si no hay nada abierto, limpia la query. |
↑ / ↓ | Navegar sugerencias y resultados. |
← / → | Navegar carouseles (top products, comparación, kit). |
Cmd+K / Ctrl+K | Atajo opcional para abrir el widget desde el host (lo declaras tú en tu plantilla y haces widget.openChat?. o foco programático sobre el input). |
Focus trap en modales
El widget incluye un focus trap genérico (createFocusTrap) que se activa al abrir cualquier dialog (búsqueda por voz, búsqueda por imagen, comparador, drawer de filtros, drawer de carrito). Comportamiento:
- Al activarse, mueve el foco al primer elemento focuseable o al
initialFocusdeclarado. - Cicla con
Tab/Shift+Tabdentro del contenedor. - Al cerrarse, restaura el foco al elemento que lo abrió.
ARIA en componentes clave
// SearchInput
<input
role="combobox"
aria-expanded={showSuggestions}
aria-controls="neuroon-suggestions"
aria-activedescendant={activeSuggestionId}
aria-autocomplete="list"
aria-label={t('search.inputAriaLabel')}
/>
<ul role="listbox" id="neuroon-suggestions" aria-label={t('suggestions.autocompleteAriaLabel')}>
<li role="option" aria-selected={isActive}>…</li>
</ul>
Las claves de aria-label viven en el bundle de i18n y se traducen al locale activo.
Reduced motion
El widget respeta prefers-reduced-motion: reduce:
export function prefersReducedMotion(): boolean {
if (typeof window === 'undefined') return false
return window.matchMedia('(prefers-reduced-motion: reduce)').matches
}
Anuncios para screen reader
El widget emite anuncios role="status" / aria-live="polite" cuando:
- Los resultados terminan de cargar (
Mostrando N de T productos). - Se aplica/quita un filtro (
Filtro Marca: Apple aplicado). - Se añade/quita un producto del comparador.
- El asistente IA termina de "pensar".
Las claves están bajo filters.filtersAndSuggestions, comparison.panelExpanded, cart.a11y.itemAdded, etc..
Inputs en mobile
Todos los inputs tienen font-size >= 16px en mobile para evitar el auto-zoom de iOS. El widget también añade touch-action: manipulation y -webkit-text-size-adjust: 100% en el root para uniformizar el comportamiento entre navegadores.
Buenas prácticas para el host
- Evita encerrar el widget dentro de un contenedor con
overflow: hiddenque recorte modales — el widget renderiza modales en su propioportalContainerdentro del Shadow Root, pero si el host limita elposition: fixeddesde un ancestro contransform, el modal puede recortarse. - Si tu tema fuerza
prefers-reduced-motion: reducecon un toggle propio, propaga la media query: el widget la lee dewindow.matchMediadirectamente. - No reordenes nodos del shadow tree desde el host. El árbol está controlado por Preact.
Próximas lecturas
- Eventos del widget — eventos a los que tu host puede reaccionar.
- Theming — tokens de contraste y ratio.
- Reference → Variables CSS — tokens de tipografía y radii.