Tab

Composant de navigation par onglets. Un clic sur un bouton masque le panneau actif et affiche le panneau cible avec une animation fade in. Le premier onglet actif est déterminé par la classe .active ou par défaut le premier bouton.

Stable JS requis Accessible

Aperçu


LIVE PREVIEW — TAB PAR DÉFAUT

Contenu du panneau 1.

Contenu du panneau 2.

Contenu du panneau 3.

Structure HTML


CODE HTML

Contenu du panneau 1.

Contenu du panneau 2.

Contenu du panneau 3.

💡 Indispensable

Le data-target de chaque bouton doit correspondre exactement à l'id du panneau associé. Le JS gère les deux formats : #id ou id.


⚠️ Chaque bouton doit posséder un id. Sans id sur le bouton, le JS en génère un automatiquement via Math.random() pour assurer le lien aria-labelledby sur le panneau.

👉 Onglet actif par défaut

Le JS cherche le premier bouton portant la classe .active pour l'activer au chargement. Si aucun bouton ne porte cette classe, c'est le premier bouton de la liste qui est activé par défaut.

Classes CSS


CLASSE / ATTRIBUT ÉLÉMENT DESCRIPTION
[data-ui="tab"] <div> Wrapper principal. Marque la racine du composant pour le JS.
[data-ui="tab-panel"] <div> Panneau de contenu. Masqué par défaut via display: none. Visible lorsqu'il porte la classe .show (display: block).
.tab-panel <div> Style du panneau : bordure, border-radius et paddings via variables CSS.
.active <button> Marque le bouton actif. Posée et retirée par le JS à chaque changement d'onglet. Peut être présente au chargement pour définir l'onglet ouvert par défaut.

États


ÉTAT ÉLÉMENT DESCRIPTION
Actif .active + aria-selected="true" + tabindex="0" Bouton de l'onglet actif. Le panneau associé est visible (.show, hidden retiré).
Inactif aria-selected="false" + tabindex="-1" Bouton inactif. Le panneau associé est masqué (hidden posé, .show retiré).
En animation .anim-fade-in + .anim-200 Classes posées temporairement sur le panneau à l'affichage. Retirées automatiquement via animationend. Non appliquées pour le premier onglet actif au chargement.

JavaScript


Le composant est piloté par la classe Tab. Elle s'appuie sur la délégation d'événements (un seul listener sur document).

INITIALISATION

SCRIPT D'INITIALISATION JS
import Tab from './Tab.js';

// Une seule instance suffit pour toute la page.
new Tab();

ℹ️ Singleton

La classe implémente un guard Tab.initialized : instancier Tab plusieurs fois n'entraîne aucun doublon de listeners.

ATTRIBUTS DE DONNÉES REQUIS

ATTRIBUT ÉLÉMENT DESCRIPTION
data-ui="tab" wrapper Marque la racine du composant. Le JS s'appuie sur cet attribut pour trouver les boutons et les panneaux associés.
data-target <button> Sélecteur du panneau cible. Accepte #id ou id.
data-ui="tab-panel" panneau Marque chaque panneau de contenu. Utilisé par le JS pour réinitialiser tous les panneaux avant d'afficher le panneau cible.

MÉTHODES STATIQUES

MÉTHODE DESCRIPTION
Tab.init() Initialise tous les composants [data-ui="tab"] du DOM. Pose les attributs ARIA, masque tous les panneaux, active le premier onglet.
Tab.resetPanel(content) Masque tous les panneaux [data-ui="tab-panel"] du conteneur : retire .show et pose hidden.
Tab.resetButton(nav) Désactive tous les boutons du <nav> : retire .active, pose aria-selected="false" et tabindex="-1".
Tab.show(button, tabPanel, first) Active un bouton et affiche son panneau. Si first = true (chargement initial), aucune animation n'est jouée. Sinon, ajoute .anim-fade-in et .anim-200, retirées via animationend.

CSS Custom Properties


TOKEN RÔLE
--tab-panel-border-color Couleur de la bordure du panneau
--tab-panel-border-style Style de la bordure (solid, dashed…)
--tab-panel-border-width Épaisseur de la bordure
--tab-panel-border-radius Rayon des coins du panneau
--tab-panel-padding-x Padding horizontal du panneau
--tab-panel-padding-y Padding vertical du panneau

Accessibilité

PRATIQUE DÉTAIL
role="tablist" Posé sur le <nav>. Indique aux lecteurs d'écran que l'élément contient une liste d'onglets.
role="tab" Posé sur chaque <button>. Identifie l'élément comme un onglet de navigation.
role="tabpanel" Posé sur chaque panneau. Identifie la zone de contenu associée à un onglet.
aria-selected Mis à jour automatiquement par le JS. true sur le bouton actif, false sur les autres.
aria-controls Lie chaque bouton à l'id de son panneau. Initialisé par Tab.init().
aria-labelledby Lie chaque panneau à l'id de son bouton. Initialisé par Tab.init().
tabindex 0 sur le bouton actif (accessible via Tab), -1 sur les autres (exclus du flux Tab natif). Mis à jour automatiquement par le JS.
hidden Posé sur les panneaux inactifs. Masque le contenu aux technologies d'assistance.

Notes & bonnes pratiques


✅ À faire

Toujours ajouter un id sur chaque bouton onglet. Sans id, le JS en génère un aléatoire — ce qui peut poser des problèmes de cohérence si le DOM est rechargé dynamiquement.

✅ À faire

Utiliser <nav role="tablist"> comme conteneur des boutons. Le JS s'appuie sur button.parentElement pour cibler la navigation et réinitialiser les boutons via Tab.resetButton().

❌ À éviter

Ne pas imbriquer plusieurs composants [data-ui="tab"] l'un dans l'autre. Le JS remonte au premier parent [data-ui="tab"] via closest() — une imbrication créerait des conflits dans la détection des panneaux et des boutons.

❌ À éviter

Ne pas instancier Tab avant le chargement du DOM. Utilisez DOMContentLoaded ou placez le script en bas de page.


Tab · WEBDEV-UI DOCS / V1