accessibility
Best practice per tabindex
Segnala valori positivi di tabindex e l'uso eccessivo di tabindex="-1". I valori positivi rompono l'ordine DOM; in produzione vanno solo 0 e -1.
Cosa verifica questo controllo
Analizza tutti gli elementi con un attributo tabindex e segnala due problemi:
- Valori positivi (
tabindex="1","2", ecc.) — sovrascrivono l’ordine naturale del DOM e causano quasi sempre bug di focus. - Uso eccessivo di
tabindex="-1"su elementi che dovrebbero essere raggiungibili da tastiera — errore comune su pulsanti e link custom.
Il controllo segnala anche quando tabindex è applicato a elementi nativamente focalizzabili (<a href>, <button>, <input>), dove di solito è ridondante.
Perché è importante
Un tabindex positivo crea un universo parallelo di navigazione da tastiera. Il browser passa prima per tutti i valori positivi — in ordine numerico — poi ricade sull’ordine DOM per il resto. Aggiungi tabindex="1" a un elemento e l’hai appena promosso davanti a ogni link, pulsante e campo form della pagina. Aggiungilo a dieci elementi e hai rinumerato a mano l’ordine di tabulazione, il che significa che ogni modifica futura (una voce di menu in più, un campo riordinato) rompe silenziosamente la sequenza.
WCAG 2.4.3 (Focus Order) richiede che il focus si muova in un ordine che preservi significato e operabilità. L’ordine visivo deve corrispondere all’ordine di tabulazione. Un tabindex positivo viola quasi sempre questa regola — gli utenti da tastiera e da screen reader finiscono in punti sorprendenti, perdono il contesto e abbandonano il flusso.
tabindex="-1" ha il problema opposto se mal usato: rimuove del tutto l’elemento dalla sequenza di tabulazione. Applicalo a un pulsante e gli utenti da tastiera non potranno raggiungerlo.
Come sistemarlo
Regola d’oro: nel codice applicativo normale, gli unici valori accettabili sono tabindex="0" e tabindex="-1", e ti servono solo su elementi non nativi.
Quando tabindex="0" è giusto
Un widget interattivo custom che il browser non focalizza di default:
<!-- Tab custom in una tablist -->
<div role="tab" tabindex="0" aria-selected="true">Panoramica</div>
<!-- Card che apre una modal al click -->
<div role="button" tabindex="0" onclick="openModal()">
Vedi dettagli
</div>
Preferisci un vero <button> ogni volta che puoi — ti dà focus, attivazione con Enter/Spazio e semantica screen-reader gratis. Ricorri a tabindex="0" solo quando l’elemento nativo non basta.
Quando tabindex="-1" è giusto
Un target di focus programmatico — un elemento che porterai sotto focus via JavaScript ma che non deve comparire nella sequenza di tabulazione:
<!-- Riepilogo errori, focalizzato al submit del form -->
<div id="errors" tabindex="-1" role="alert">
3 campi richiedono attenzione
</div>
<!-- Container modal, focalizzato all'apertura -->
<div id="modal" tabindex="-1" role="dialog" aria-modal="true">
...
</div>
<!-- Target di skip-link (contenuto principale) -->
<main id="main" tabindex="-1">...</main>
Poi in JS:
document.getElementById("errors").focus();
Il -1 fa funzionare .focus() senza inserire l’elemento nell’ordine di Tab.
Quando un tabindex positivo è giusto
Quasi mai. L’unico caso difendibile è l’integrazione con un form legacy in cui non puoi riordinare il DOM e l’ordine visivo richiede un risequenziamento — e anche allora, la risposta giusta è sistemare il DOM.
Lintalo in CI
Intercettalo prima della review:
bun add -D eslint-plugin-jsx-a11y
// eslint.config.js
import jsxA11y from "eslint-plugin-jsx-a11y";
export default [{
plugins: { "jsx-a11y": jsxA11y },
rules: {
"jsx-a11y/tabindex-no-positive": "error",
"jsx-a11y/no-noninteractive-tabindex": "warn",
},
}];
Per repo HTML/template vanilla, axe-core intercetta lo stesso problema in fase di audit — vedi errori di accessibilità in Lighthouse.
Sistemare una pagina che ha già tabindex positivi
- Rimuovi ogni attributo
tabindex="1+". - Tabula la pagina con la tastiera. Annota i salti sorprendenti.
- Riordina il DOM in modo che la sequenza naturale corrisponda a quella visiva. Usa CSS (
order, posizionamento grid) solo per riposizionamenti visivi che non alterano l’ordine di lettura. - Rilancia l’audit.
Domande frequenti
Posso usare tabindex="0" per rendere un <div> cliccabile?
Puoi, ma aggiungere role="button" e handler Enter/Spazio è metà del lavoro — un vero <button> è un solo elemento con tutto cablato. Il pattern tabindex="0" + role="button" è riservato ai casi in cui ti serve un elemento non-button (es. un treeitem, un tab) che la spec non include come nativamente focalizzabile.
tabindex="-1" nasconde l’elemento agli screen reader?
No. tabindex="-1" rimuove l’elemento solo dalla navigazione sequenziale da tastiera. Gli screen reader possono comunque raggiungerlo con la loro navigazione (intestazioni, landmark, cursore virtuale). Per nasconderlo alle tecnologie assistive usa aria-hidden="true" — ma mai su un elemento interattivo.
E il roving tabindex dentro un menu o una griglia?
È il pattern giusto per i widget compositi (menu, tablist, griglie). Esattamente un elemento ha tabindex="0", gli altri hanno tabindex="-1", e i tasti freccia muovono il focus alternando i valori. Il controllo tiene conto di questo: un singolo 0 insieme a molti -1 dentro lo stesso role="menu" / role="tablist" / role="grid" non viene segnalato.
Fonti
Ultimo aggiornamento 2026-05-11