HTML
Foco, tabindex, hidden e inert
Cómo gestionar navegación por teclado, foco programático y disponibilidad del contenido mediante tabindex, hidden e inert sin romper la interacción.
- Última actualización
- Actualizada
- Nivel
- Aplicación
HTML
Cómo gestionar navegación por teclado, foco programático y disponibilidad del contenido mediante tabindex, hidden e inert sin romper la interacción.
Incluyen normalmente:
href.No añadas tabindex a controles que ya lo reciben.
<a href="/">Inicio</a>
<input name="query">
<button>Buscar</button>Tab sigue normalmente el orden DOM.
CSS puede cambiar posición visual, pero no necesariamente foco. Mantén ambos alineados.
<div tabindex="0">...</div>Añade el elemento al orden natural según su posición.
No lo uses para convertir texto o contenedores en controles. Primero elige un elemento nativo.
<div id="error-summary" tabindex="-1">...</div>Permite foco programático sin entrar al orden de Tab. Aporta para:
El foco debe moverse por una razón comprensible, no en cada actualización menor.
<button tabindex="3">...</button>Crea un orden manual antes de los elementos con 0 y se vuelve frágil al cambiar la interfaz. Evítalo.
Corrige el orden DOM.
No:
:focus { outline: none; }Sin reemplazo, el usuario no sabe dónde está.
Utiliza :focus-visible para un indicador adaptado:
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 3px;
}Verifica contraste, clipping y estados de high contrast.
Todo contenido o acción disponible mediante hover debe poder descubrirse mediante foco, click/touch u otra interacción equivalente.
.card:hover .actions,
.card:focus-within .actions {
opacity: 1;
}<section hidden>...</section>El contenido no participa normalmente en render, foco o árbol accesible.
No dejes un descendiente enfocado y luego ocultes su parent sin mover foco a un lugar lógico.
<div aria-hidden="true">...</div>Oculta del árbol accesible, pero puede seguir visible y sus descendants incluso recibir foco si no los deshabilitas. No lo uses como sustituto de hidden.
Nunca coloques focusables utilizables dentro de un subtree aria-hidden.
<main inert>...</main>Impide interacción y foco en un subtree, y lo retira normalmente de búsqueda/accesibilidad. Es útil al abrir un modal cuando el navegador o componente no gestiona automáticamente el resto del documento.
No es solo visual. El usuario debe conservar una salida clara del estado modal.
<button disabled>Guardar</button>Un control disabled sale normalmente del orden de foco y no activa acciones.
Para comunicar una acción no disponible, puede ser mejor mantener un button activo que explique requisitos, o mostrar texto cercano. Depende del flujo.
aria-disabled="true" comunica estado, pero no impide foco ni activación; JavaScript debe bloquearla.
Al abrir:
Al cerrar:
El elemento dialog.showModal() aporta gran parte del comportamiento modal, pero el contenido y foco inicial siguen necesitando diseño.
Al cambiar de vista sin recarga, el foco puede permanecer en el link anterior mientras el contenido cambia.
Una estrategia puede moverlo al h1 o main con tabindex="-1", anunciar cambios importantes y actualizar title.
No muevas foco en actualizaciones parciales pequeñas.
<a class="skip-link" href="#main-content">
Saltar al contenido principal
</a>
<main id="main-content" tabindex="-1">...</main>Permite evitar navegación repetitiva. Hazlo visible al recibir foco.
Widgets como tabs pueden usar:
un item tabindex="0"
los demás tabindex="-1"
flechas mueven foco y actualizan cuál es 0Es un patrón JavaScript avanzado. No lo uses en listas de links normales, donde Tab entre enlaces es esperado.
<button type="button" id="open-dialog">
Eliminar proyecto
</button>
<dialog aria-labelledby="dialog-title">
<h2 id="dialog-title">Eliminar proyecto</h2>
<p>Esta acción no se puede deshacer.</p>
<button type="button">Cancelar</button>
<button type="button">Eliminar</button>
</dialog>¿Por qué un botón dentro de aria-hidden="true" sigue siendo peligroso?
Porque puede permanecer visible y enfocarse, pero no existir para tecnologías de asistencia, creando una interacción sin nombre ni contexto accesible.
details, summary, dialog y popover utiliza componentes interactivos nativos.