Tabindex : bonnes pratiques et pièges à éviter
En Bref : L'essentiel à retenir
- tabindex="0" ajoute un élément à l'ordre de tabulation naturel, tabindex="-1" le rend focusable uniquement par JavaScript.
- Les valeurs positives de tabindex (1, 2, 3...) sont à proscrire car elles perturbent l'ordre naturel de navigation.
- Les éléments interactifs natifs (button, a, input) sont focusables par défaut et n'ont pas besoin de tabindex.
- Le roving tabindex est une technique pour gérer le focus dans les composants complexes comme les onglets ou menus.
L'attribut tabindex contrôle si et comment un élément peut recevoir le focus clavier. Bien utilisé, il améliore l'accessibilité. Mal utilisé, il peut rendre la navigation complètement chaotique. Ce guide vous explique comment maîtriser cet attribut essentiel.
Les trois valeurs de tabindex
tabindex="0" : Ajouter à l'ordre de tabulation
Un élément avec tabindex="0" devient focusable et s'insère dans l'ordre naturel de tabulation (défini par l'ordre du DOM).
<!-- Ce div devient focusable au clavier -->
<div tabindex="0" role="button" onclick="action()">
Élément interactif custom
</div>
Cas d'usage :
- Rendre focusable un élément non interactif
- Permettre le focus sur une région de contenu (tabpanel, dialog)
tabindex="-1" : Focusable par script uniquement
Un élément avec tabindex="-1" peut recevoir le focus via JavaScript (élément.focus()) mais n'est pas accessible via la touche Tab.
<!-- Focusable par script, pas par Tab -->
<div tabindex="-1" id="zone-erreur">
Message d'erreur qui recevra le focus programmatiquement
</div>
// Focus programmatique
document.getElementById('zone-erreur').focus();
Cas d'usage :
- Éléments qui doivent recevoir le focus après une action (messages d'erreur, modales)
- Éléments dans un composant avec roving tabindex (onglets inactifs)
- Zones de contenu non interactives qui doivent être lues par les lecteurs d'écran
tabindex > 0 : À éviter absolument
Une valeur positive (1, 2, 3, etc.) place l'élément avant tous les éléments avec tabindex="0" dans l'ordre de tabulation.
<!-- NE FAITES PAS CECI -->
<button tabindex="3">Troisième focus</button>
<button tabindex="1">Premier focus</button>
<button tabindex="2">Deuxième focus</button>
[!NOTE] Les valeurs positives de tabindex sont considérées comme une mauvaise pratique par le W3C et les experts en accessibilité. Elles créent un ordre de navigation artificiel déconnecté du DOM, source de confusion pour les utilisateurs.
Les éléments nativement focusables
Ces éléments HTML sont focusables par défaut, sans tabindex :
| élément | Condition |
|---|---|
<a> | Avec attribut href |
<button> | Non désactivé |
<input> | Non désactivé, non type="hidden" |
<select> | Non désactivé |
<textarea> | Non désactivé |
<details> | L'élément <summary> est focusable |
<iframe> | Toujours focusable |
N'ajoutez pas tabindex="0" à ces éléments, c'est inutile :
<!-- Inutile : le bouton est déjà focusable -->
<button tabindex="0">Cliquer</button>
<!-- Correct -->
<button>Cliquer</button>
Quand utiliser tabindex="0"
Éléments interactifs custom
Quand vous créez un composant interactif avec un élément non focusable :
<div
role="button"
tabindex="0"
onclick="action()"
onkeydown="if(event.key==='Enter'||event.key===' ')action()"
>
Bouton personnalisé
</div>
[!TIP] Privilégiez toujours l'élément natif quand c'est possible.
<button>est préférable à<div role="button" tabindex="0">.
Conteneurs de contenu non interactif
Pour les zones qui doivent être accessibles aux lecteurs d'écran via Tab (comme les tabpanels) :
<div role="tabpanel" tabindex="0" aria-labelledby="tab-1">
Contenu du panneau sans éléments interactifs.
Le tabindex permet de naviguer jusqu'ici avec Tab.
</div>
Éléments avec rôle de landmark
Certains landmarks bénéficient d'un tabindex pour être directement accessibles :
<main tabindex="-1" id="contenu-principal">
<!-- Permet le focus après un "skip to main content" -->
</main>
Quand utiliser tabindex="-1"
Gestion du focus programmatique
Après une action, déplacer le focus vers un élément spécifique :
<div id="notification" tabindex="-1" role="alert">
Votre message a été envoyé avec succès.
</div>
function afficherNotification(message) {
const notif = document.getElementById('notification');
notif.textContent = message;
notif.focus();
}
Éléments temporairement inactifs dans un widget
Dans les onglets, menus, ou autres composants avec roving tabindex :
<div role="tablist">
<button role="tab" aria-selected="true" tabindex="0">Actif</button>
<button role="tab" aria-selected="false" tabindex="-1">Inactif</button>
<button role="tab" aria-selected="false" tabindex="-1">Inactif</button>
</div>
Empêcher le focus sur un élément temporairement
<!-- Pendant le chargement, le bouton est ignore par Tab -->
<button tabindex="-1" disabled>Chargement...</button>
<!-- Une fois prêt, il redevient focusable -->
<button tabindex="0">Envoyer</button>
La technique du roving tabindex
Le "roving tabindex" est un pattern pour gérer le focus dans les composants complexes comme les menus, onglets, ou listes.
Principe
- Un seul élément du groupe a
tabindex="0"(celui qui est actif/sélectionné) - Tous les autres ont
tabindex="-1" - Les flèches déplacent le focus et mettent à jour les tabindex
Exemple : système d'onglets
<div role="tablist">
<button role="tab" aria-selected="true" tabindex="0" id="tab-1">
Onglet 1
</button>
<button role="tab" aria-selected="false" tabindex="-1" id="tab-2">
Onglet 2
</button>
<button role="tab" aria-selected="false" tabindex="-1" id="tab-3">
Onglet 3
</button>
</div>
const tabs = document.querySelectorAll('[role="tab"]');
let currentIndex = 0;
tabs.forEach((tab, index) => {
tab.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') {
e.preventDefault();
goToTab((currentIndex + 1) % tabs.length);
}
if (e.key === 'ArrowLeft') {
e.preventDefault();
goToTab((currentIndex - 1 + tabs.length) % tabs.length);
}
});
});
function goToTab(newIndex) {
// Ancien onglet
tabs[currentIndex].setAttribute('tabindex', '-1');
tabs[currentIndex].setAttribute('aria-selected', 'false');
// Nouvel onglet
currentIndex = newIndex;
tabs[currentIndex].setAttribute('tabindex', '0');
tabs[currentIndex].setAttribute('aria-selected', 'true');
tabs[currentIndex].focus();
}
Avantages du roving tabindex
- Un seul Tab suffit pour entrer/sortir du composant
- Flèches pour naviguer à l'intérieur
- Expérience prévisible : conforme aux patterns ARIA
Erreurs courantes avec tabindex
1. tabindex positif
<!-- INCORRECT : crée un ordre de navigation chaotique -->
<div tabindex="1">Premier</div>
<div tabindex="3">Troisième</div>
<div tabindex="2">Deuxième</div>
Solution : Réorganisez le DOM pour refléter l'ordre de navigation souhaité.
2. tabindex="0" sur éléments non interactifs
<!-- INCORRECT : le paragraphe ne devrait pas être dans l'ordre de tab -->
<p tabindex="0">Un simple texte.</p>
Ajouter un paragraphe à l'ordre de tabulation oblige les utilisateurs clavier à tabuler sur des éléments où ils ne peuvent rien faire.
Solution : Ne rendez focusables que les éléments interactifs ou les régions importantes.
3. Oublier de gérer le clavier
<!-- INCORRECT : focusable mais pas actionnable au clavier -->
<div tabindex="0" onclick="action()">
Cliquez-moi
</div>
Solution : Ajoutez la gestion du clavier ou utilisez un <button>.
<div
tabindex="0"
role="button"
onclick="action()"
onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();action()}"
>
Cliquez-moi
</div>
4. tabindex="-1" pour "cacher" un élément
<!-- INCORRECT : l'élément reste visible et cliquable -->
<button tabindex="-1">Invisible au clavier mais visible</button>
Solution : utilisez disabled, hidden, ou aria-hidden="true" selon le besoin.
5. tabindex sur tous les éléments d'une page
Certains croient (à tort) que tabindex="0" améliore l'accessibilité en général.
Réalité : Cela force les utilisateurs à tabuler sur chaque élément, ralentissant considérablement la navigation.
CSS et ordre visuel vs ordre du DOM
Attention : CSS (flex order, grid, position) peut changer l'ordre visuel sans modifier l'ordre du DOM. Cela crée une déconnexion entre ce que voit l'utilisateur et l'ordre de tabulation.
<div style="display: flex;">
<button style="order: 2">Visuellement second</button>
<button style="order: 1">Visuellement premier</button>
</div>
<!-- Le focus ira d'abord sur "Visuellement second" ! -->
Solution : Alignez l'ordre du DOM avec l'ordre visuel, ou utilisez le roving tabindex si nécessaire.
Test de l'ordre de tabulation
Test manuel
- Placez votre focus en haut de la page
- Appuyez sur Tab répétitivement
- Vérifiez que :
- L'ordre est logique et prévisible
- Vous atteignez tous les éléments interactifs
- Vous ne vous retrouvez pas "piégé"
- Le focus ne "saute" pas de manière inattendue
Test avec l'inspecteur d'accessibilité
Les DevTools Chrome permettent de visualiser l'ordre de tabulation dans le panneau Accessibility.
Test automatisé
Notre outil d'audit RGAA détecte les tabindex positifs et d'autres problèmes de navigation clavier.
Résumé des bonnes pratiques
| Valeur | Utilisation | À éviter |
|---|---|---|
tabindex="0" | Éléments custom interactifs, tabpanels | Éléments non interactifs, éléments déjà focusables |
tabindex="-1" | Focus programmatique, roving tabindex | Cacher visuellement un élément |
tabindex > 0 | Jamais | Toujours |
Conclusion
tabindex est un outil puissant mais qui doit être utilisé avec parcimonie. Dans la majorité des cas, les éléments HTML natifs fournissent déjà le comportement de focus nécessaire. N'ajoutez tabindex que lorsque vous avez une raison spécifique de le faire.
Retenez ces trois règles :
- Utilisez les éléments natifs quand c'est possible
- N'utilisez jamais de valeurs positives
- Testez la navigation clavier après toute modification
En suivant ces bonnes pratiques, vous garantissez une navigation clavier fluide et prévisible pour tous vos utilisateurs.
Guides RGAA associés
Pour aller plus loin sur les sujets abordés dans cet article, consultez nos fiches techniques :
Chaque champ de formulaire doit avoir une étiquette (label) qui lui est liée explicitement.
Dans chaque page web, un lien d'évitement ou d'accès rapide au contenu principal doit être présent, visible au focus, et placé en premier élément focusable.
Chaque image porteuse d'information doit avoir une alternative textuelle pertinente via l'attribut alt. Les images décoratives doivent avoir un attribut alt vide.
Articles similaires
Votre site est-il conforme ?
Ne prenez pas de risques avec l'accessibilité. Lancez un audit complet de votre site en quelques minutes et obtenez un rapport détaillé des corrections à apporter.