Aller au contenu principal
Design & Contenu9 février 20268 min

Créer des onglets accessibles : rôles ARIA et navigation clavier

En Bref : L'essentiel à retenir

  • Les onglets utilisent les rôles tablist, tab et tabpanel pour communiquer leur structure aux technologies d'assistance.
  • La navigation entre onglets se fait avec les flèches gauche/droite, Tab permettant de passer au contenu du panneau.
  • L'onglet actif a aria-selected="true" et tabindex="0", les autres ont aria-selected="false" et tabindex="-1".
  • L'activation automatique (changement de panneau au focus) est recommandée pour une meilleure expérience utilisateur.
ARIAComposantsClavierInterface

Les interfaces à onglets sont un pattern de navigation courant pour organiser du contenu en sections. Cependant, leur implémentation accessible requiert une attention particulière aux rôles ARIA et à la navigation clavier. Ce guide vous montre comment créer des onglets conformes aux standards d'accessibilité.

Structure sémantique des onglets

Un système d'onglets accessible se compose de trois éléments principaux :

  1. Tablist : Le conteneur de la liste d'onglets
  2. Tabs : Les onglets eux-mêmes (boutons de sélection)
  3. Tabpanels : Les panneaux de contenu associés

HTML sémantique avec ARIA

CODE
<div class="tabs">
  <!-- Liste des onglets -->
  <div role="tablist" aria-label="Options de livraison">
    <button
      role="tab"
      id="tab-standard"
      aria-selected="true"
      aria-controls="panel-standard"
      tabindex="0"
    >
      Livraison standard
    </button>
    <button
      role="tab"
      id="tab-express"
      aria-selected="false"
      aria-controls="panel-express"
      tabindex="-1"
    >
      Livraison express
    </button>
    <button
      role="tab"
      id="tab-relais"
      aria-selected="false"
      aria-controls="panel-relais"
      tabindex="-1"
    >
      Point relais
    </button>
  </div>

  <!-- Panneaux de contenu -->
  <div
    role="tabpanel"
    id="panel-standard"
    aria-labelledby="tab-standard"
    tabindex="0"
  >
    <h3>Livraison standard</h3>
    <p>Livraison en 3-5 jours ouvrables...</p>
  </div>

  <div
    role="tabpanel"
    id="panel-express"
    aria-labelledby="tab-express"
    tabindex="0"
    hidden
  >
    <h3>Livraison express</h3>
    <p>Livraison en 24-48h...</p>
  </div>

  <div
    role="tabpanel"
    id="panel-relais"
    aria-labelledby="tab-relais"
    tabindex="0"
    hidden
  >
    <h3>Point relais</h3>
    <p>Retirez votre colis dans un point relais...</p>
  </div>
</div>

Les attributs ARIA en détail

role="tablist"

Conteneur qui regroupe les onglets. Doit avoir un label accessible :

CODE
<!-- Via aria-label -->
<div role="tablist" aria-label="Sections du produit">

<!-- Ou via aria-labelledby -->
<h2 id="titre-tabs">Spécifications</h2>
<div role="tablist" aria-labelledby="titre-tabs">

role="tab"

Chaque onglet individuel. Attributs requis :

AttributDescription
aria-selectedtrue pour l'onglet actif, false pour les autres
aria-controlsID du tabpanel associé
tabindex0 pour l'onglet actif, -1 pour les autres
CODE
<button
  role="tab"
  aria-selected="true"
  aria-controls="panel-1"
  tabindex="0"
>
  Onglet actif
</button>

role="tabpanel"

Le panneau de contenu associé à un onglet :

AttributDescription
aria-labelledbyID de l'onglet associé
tabindex="0"Rend le panneau focusable (utile si pas d'élément interactif)
hiddenMasque les panneaux non actifs
CODE
<div
  role="tabpanel"
  aria-labelledby="tab-1"
  tabindex="0"
>
  Contenu du panneau...
</div>

[!NOTE] Le tabindex="0" sur le tabpanel permet aux utilisateurs clavier d'accéder au contenu même s'il ne contient pas d'éléments focusables. C'est particulièrement utile pour les lecteurs d'écran.

Navigation clavier

Le pattern ARIA Authoring Practices définit une navigation spécifique pour les onglets.

Comportement attendu

ToucheAction
TabEntre dans le tablist (sur l'onglet actif), puis passe au tabpanel
Flèche gaucheOnglet précédent
Flèche droiteOnglet suivant
HomePremier onglet
EndDernier onglet
Entrée/EspaceActive l'onglet (si activation manuelle)

Roving tabindex

La technique du "roving tabindex" permet une navigation fluide :

  • L'onglet actif a tabindex="0" et peut recevoir le focus via Tab
  • Les autres onglets ont tabindex="-1" et sont accessibles via les flèches
CODE
function activerOnglet(tablist, ongletCible) {
  const onglets = tablist.querySelectorAll('[role="tab"]');

  onglets.forEach(onglet => {
    const estActif = onglet === ongletCible;

    // Mise à jour de l'état
    onglet.setAttribute('aria-selected', estActif);
    onglet.setAttribute('tabindex', estActif ? '0' : '-1');

    // Affichage du panneau correspondant
    const panneau = document.getElementById(
      onglet.getAttribute('aria-controls')
    );
    panneau.hidden = !estActif;
  });

  // Focus sur l'onglet activé
  ongletCible.focus();
}

Activation automatique vs manuelle

Activation automatique (recommandée)

L'onglet s'active dès qu'il reçoit le focus (via flèches). C'est le comportement recommandé par le W3C pour la plupart des cas :

CODE
onglet.addEventListener('focus', () => {
  activerOnglet(tablist, onglet);
});

Avantages :

  • Expérience plus fluide
  • Moins de manipulations requises
  • Comportement prévisible

Activation manuelle

L'utilisateur doit appuyer sur Entrée ou Espace pour activer l'onglet après l'avoir sélectionné :

CODE
onglet.addEventListener('keydown', (e) => {
  if (e.key === 'Enter' || e.key === ' ') {
    e.preventDefault();
    activerOnglet(tablist, onglet);
  }
});

Quand l'utiliser :

  • Contenu lourd à charger (évite les chargements intempestifs)
  • Onglets nombreux à parcourir

[!TIP] Dans le doute, privilégiez l'activation automatique. Elle correspond aux attentes de la majorité des utilisateurs et réduit la charge cognitive.

Implémentation JavaScript complète

CODE
class AccessibleTabs {
  constructor(container) {
    this.container = container;
    this.tablist = container.querySelector('[role="tablist"]');
    this.tabs = Array.from(container.querySelectorAll('[role="tab"]'));
    this.panels = Array.from(container.querySelectorAll('[role="tabpanel"]'));
    this.activeIndex = 0;

    this.init();
  }

  init() {
    this.tabs.forEach((tab, index) => {
      // Click pour activer
      tab.addEventListener('click', () => this.activerOnglet(index));

      // Navigation clavier
      tab.addEventListener('keydown', (e) => this.gererClavier(e, index));
    });
  }

  activerOnglet(index) {
    // Désactiver l'ancien onglet
    this.tabs[this.activeIndex].setAttribute('aria-selected', 'false');
    this.tabs[this.activeIndex].setAttribute('tabindex', '-1');
    this.panels[this.activeIndex].hidden = true;

    // Activer le nouveau
    this.activeIndex = index;
    this.tabs[index].setAttribute('aria-selected', 'true');
    this.tabs[index].setAttribute('tabindex', '0');
    this.tabs[index].focus();
    this.panels[index].hidden = false;
  }

  gererClavier(event, currentIndex) {
    let nouvelIndex = currentIndex;

    switch (event.key) {
      case 'ArrowLeft':
        nouvelIndex = currentIndex > 0
          ? currentIndex - 1
          : this.tabs.length - 1;
        break;

      case 'ArrowRight':
        nouvelIndex = currentIndex < this.tabs.length - 1
          ? currentIndex + 1
          : 0;
        break;

      case 'Home':
        nouvelIndex = 0;
        break;

      case 'End':
        nouvelIndex = this.tabs.length - 1;
        break;

      default:
        return; // Ne rien faire pour les autres touches
    }

    event.preventDefault();
    this.activerOnglet(nouvelIndex);
  }
}

// Initialisation
document.querySelectorAll('.tabs').forEach(container => {
  new AccessibleTabs(container);
});

Onglets verticaux

Pour des onglets disposés verticalement, adaptez la navigation :

CODE
<div role="tablist" aria-orientation="vertical" aria-label="Menu">

Changez les flèches utilisées :

CODE
gererClavier(event, currentIndex) {
  const isVertical = this.tablist.getAttribute('aria-orientation') === 'vertical';

  switch (event.key) {
    case 'ArrowUp':
      if (isVertical) nouvelIndex = currentIndex - 1;
      break;
    case 'ArrowDown':
      if (isVertical) nouvelIndex = currentIndex + 1;
      break;
    case 'ArrowLeft':
      if (!isVertical) nouvelIndex = currentIndex - 1;
      break;
    case 'ArrowRight':
      if (!isVertical) nouvelIndex = currentIndex + 1;
      break;
  }
}

Styles CSS accessibles

Focus visible

Assurez un indicateur de focus clair sur les onglets :

CODE
[role="tab"]:focus {
  outline: none; /* On remplace par un style custom */
}

[role="tab"]:focus-visible {
  outline: 3px solid #005fcc;
  outline-offset: 2px;
}

Indication visuelle de l'onglet actif

L'état actif doit être clairement visible, pas seulement par la couleur :

CODE
[role="tab"][aria-selected="true"] {
  background: #ffffff;
  border-bottom: 3px solid #005fcc;
  font-weight: bold;
}

[role="tab"][aria-selected="false"] {
  background: #f0f0f0;
  border-bottom: 1px solid #cccccc;
}

Contraste suffisant

Respectez les ratios de contraste WCAG :

  • 4.5:1 pour le texte des onglets
  • 3:1 pour les bordures et indicateurs visuels

Utilisez notre vérificateur de contraste.

Erreurs courantes à éviter

Utiliser des liens au lieu de boutons

Les onglets ne naviguent pas vers une nouvelle page, ce sont des boutons :

CODE
<!-- Incorrect -->
<a href="#panel-1" role="tab">Onglet 1</a>

<!-- Correct -->
<button role="tab">Onglet 1</button>

Oublier aria-controls

Chaque onglet doit être lié à son panneau :

CODE
<button role="tab" aria-controls="panel-1">Onglet</button>
<div role="tabpanel" id="panel-1">...</div>

tabindex incorrect

Seul l'onglet actif doit avoir tabindex="0" :

CODE
<!-- Incorrect : tous avec tabindex="0" -->
<button role="tab" tabindex="0">Onglet 1</button>
<button role="tab" tabindex="0">Onglet 2</button>

<!-- Correct : roving tabindex -->
<button role="tab" aria-selected="true" tabindex="0">Onglet 1</button>
<button role="tab" aria-selected="false" tabindex="-1">Onglet 2</button>

Contenu de panneau non accessible

Si le panneau ne contient pas d'éléments focusables, ajoutez tabindex="0" :

CODE
<div role="tabpanel" tabindex="0">
  <p>Texte sans éléments interactifs</p>
</div>

Test d'accessibilité

Validez votre implémentation :

  1. Clavier : Naviguer uniquement au clavier (Tab, flèches)
  2. Lecteur d'écran : Vérifier les annonces (NVDA, VoiceOver)
  3. Focus visible : Indicateur de focus sur chaque onglet
  4. État annoncé : "Onglet 1, sélectionné, onglet, 1 sur 3"
  5. Panneaux : Le contenu est-il annoncé correctement ?

Lancez un audit RGAA complet pour vérifier l'accessibilité de votre site.

Conclusion

Les interfaces à onglets accessibles reposent sur une utilisation correcte des rôles ARIA (tablist, tab, tabpanel) et une navigation clavier conforme aux attentes des utilisateurs. Le pattern du roving tabindex garantit une expérience fluide pour les utilisateurs clavier.

En implémentant ces techniques, vous offrez une navigation efficace à tous vos utilisateurs, qu'ils utilisent une souris, un clavier, ou un lecteur d'écran.

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.