Navigation Clavier
Permettre à tous les utilisateurs de naviguer efficacement sans souris
Raccourcis clavier
- Tab
- Naviguer entre les éléments interactifs
- Shift + Tab
- Navigation arrière
- Entrée / Espace
- Activer les boutons et liens
- Échap
- Fermer les modales et menus
- ↑ ↓
- Naviguer dans les listes et menus
Navigation au clavier dans les listes
❌ Mauvais exemple
Menu avec navigation Tab uniquement :
<!-- Mauvais : navigation Tab uniquement -->
<div class="menu">
<div tabindex="0">🏠 Accueil</div>
<div tabindex="0">📋 Projets</div>
<div tabindex="0">👥 Équipe</div>
<div tabindex="0">📊 Stats</div>
<div tabindex="0">⚙️ Paramètres</div>
</div>
<!-- Utilisateur doit Tab 5 fois -->
<!-- Pas de navigation par flèches -->
<!-- Pas de sémantique de menu -->✅ Bon exemple
Menu avec navigation par flèches :
💡 Utilisez ↑ ↓ pour naviguer, Entrée pour sélectionner
<!-- Bon : navigation par flèches avec roving tabindex -->
<div
role="menu"
@keydown="handleKeydown"
>
<div
v-for="(item, i) in items"
role="menuitem"
:tabindex="selected === i ? 0 : -1"
@focus="selected = i"
:class="{ selected: selected === i }"
>
{{ item }}
</div>
</div>// Navigation par flèches avec roving tabindex
function handleKeydown(e) {
switch(e.key) {
case 'ArrowDown':
selected = (selected + 1) % items.length
break
case 'ArrowUp':
selected = selected === 0
? items.length - 1
: selected - 1
break
case 'Enter':
selectItem(selected)
break
}
}Explication : Les listes navigables au clavier permettent d'utiliser les flèches directionnelles pour se déplacer entre les éléments, en plus de la navigation Tab classique.
Styles de focus visibles
❌ Mauvais exemple
Essayez de naviguer avec Tab dans cette zone :
/* ❌ Supprime tous les indicateurs de focus */
*:focus {
outline: none !important;
}
button:focus,
a:focus,
input:focus {
outline: none !important;
}<!-- ❌ Problèmes d'accessibilité : -->
<button>Bouton 1</button> <!-- Pas de type, focus invisible -->
<button>Bouton 2</button> <!-- Pas de type, focus invisible -->
<a href="#">Lien</a> <!-- Focus invisible -->
<input type="text" /> <!-- Focus invisible -->
<!-- Navigation clavier impossible à suivre visuellement -->✅ Bon exemple
Essayez de naviguer avec Tab dans cette zone :
/* ✅ Indicateurs de focus visibles et accessibles */
button:focus-visible {
outline: 3px solid #4F46E5;
outline-offset: 2px;
}
input:focus-visible {
border-color: #4F46E5;
outline: 2px solid #4F46E5;
outline-offset: 1px;
}<button type="button">Bouton 1</button>
<button type="button">Bouton 2</button>
<a href="#">Lien vers la page</a>
<input type="text" placeholder="Saisissez du texte" />Explication : Les utilisateurs qui naviguent au clavier ont besoin d'indicateurs visuels clairs pour savoir où ils se trouvent sur la page.
Lien d'évitement (Skip to content)
❌ Mauvais exemple
Contenu principal
L'utilisateur doit naviguer à travers tous les liens de navigation...
<!-- Mauvais : pas de lien d'évitement -->
<header>
<nav>
<a href="#">Accueil</a>
<a href="#">Produits</a>
<a href="#">Services</a>
<!-- 10+ liens de navigation... -->
</nav>
</header>
<main>
<!-- L'utilisateur doit traverser toute la nav -->
<h1>Contenu principal</h1>
</main>✅ Bon exemple
Contenu principal
L'utilisateur peut accéder directement ici grâce au lien d'évitement !
/* Lien d'évitement caché par défaut, visible au focus */
.skip-link {
position: absolute;
top: -100px;
left: 1rem;
z-index: 1000;
padding: 0.5rem 1rem;
background: var(--color-primary);
color: white;
text-decoration: none;
border-radius: 0.625rem;
}
.skip-link:focus {
top: 1rem;
}<a href="#main-content" class="skip-link">
Aller au contenu principal
</a>
<header>
<nav aria-label="Navigation principale">
<ul>
<li><a href="/">Accueil</a></li>
<li><a href="/produits">Produits</a></li>
<li><a href="/services">Services</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<h1>Contenu principal</h1>
</main>Explication : Les liens d'évitement permettent aux utilisateurs de clavier de passer directement au contenu principal sans devoir parcourir toute la navigation.
Modal avec piège à focus
❌ Mauvais exemple
<!-- Mauvais : modal sans gestion du focus -->
<div v-if="modalOpen" class="modal-overlay" @click="close">
<div class="modal-content" @click.stop>
<div class="modal-header">
<h3>Confirmer l'action</h3>
<button @click="close">×</button>
</div>
<div class="modal-body">
<p>Êtes-vous sûr de vouloir continuer ?</p>
<input type="text" placeholder="Raison (optionnel)" />
</div>
<div class="modal-footer">
<button @click="close">Annuler</button>
<button @click="confirm">Confirmer</button>
</div>
</div>
</div>✅ Bon exemple
<!-- Bon : modal avec piège à focus -->
<div
v-if="modalOpen"
class="modal-overlay"
@click="closeModal"
@keydown.esc="closeModal"
>
<div
class="modal-content"
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
@click.stop
>
<header class="modal-header">
<h2 id="modal-title">Confirmer l'action</h2>
<button
type="button"
@click="closeModal"
aria-label="Fermer la modal"
class="close-btn"
>×</button>
</header>
<div class="modal-body">
<p>Êtes-vous sûr de vouloir continuer ?</p>
<input
ref="firstInput"
type="text"
placeholder="Raison (optionnel)"
@keydown.tab="handleFocusTrap"
/>
</div>
<footer class="modal-footer">
<button
type="button"
@click="closeModal"
@keydown.tab="handleFocusTrap"
>Annuler</button>
<button
type="button"
ref="lastButton"
@click="confirmAction"
@keydown.tab="handleFocusTrap"
>Confirmer</button>
</footer>
</div>
</div>// Fonctions essentielles :
// - Focus automatique sur premier élément
// - Piège à focus (Tab/Shift+Tab)
// - Fermeture par Échap
// - Retour du focus à l'élément déclencheur
function handleFocusTrap(event) {
const focusableElements = modalContent.querySelectorAll(
'button, input, [tabindex]:not([tabindex="-1"])'
)
const firstElement = focusableElements[0]
const lastElement = focusableElements[focusableElements.length - 1]
if (event.shiftKey && document.activeElement === firstElement) {
event.preventDefault()
lastElement.focus()
} else if (!event.shiftKey && document.activeElement === lastElement) {
event.preventDefault()
firstElement.focus()
}
}Explication : Les modales doivent garder le focus à l'intérieur et permettre de les fermer avec la touche Échap. Le focus doit retourner à l'élément qui a ouvert la modal.