HTML sémantique — Guide pratique
Structurer une page avec les bons éléments HTML améliore l’accessibilité, le référencement et la maintenabilité du code. Cette page couvre les éléments sémantiques, les structures typiques et les pièges courants.
Cette page est un complément à Frontend (vue d’ensemble HTML) et à Motifs de mise en page (mise en page CSS). Elle se concentre sur le choix et l’agencement des balises sémantiques.
Les éléments sémantiques — quand utiliser quoi
Structure de page
| Élément | Rôle | Contraintes |
|---|---|---|
<header> | En-tête d’une section ou de la page | Peut contenir logo, titre, navigation. Possiblement plusieurs sur une page (un par section). |
<nav> | Groupe de liens de navigation | Pas nécessaire pour tous les liens — réserver aux navigations principales (menu, pagination, fil d’Ariane). |
<main> | Contenu principal unique de la page | Un seul par page. Ne doit pas contenir d’en-tête, de navigation ou de footer global. |
<section> | Regroupement thématique de contenu | Doit avoir un titre (<h1>–<h6>). Pas un simple conteneur de style. |
<article> | Contenu autonome, redistribuable | Un billet de blog, un commentaire, une carte produit — peut être partagé indépendamment. |
<aside> | Contenu secondaire, lié au principal | Sidebar, publicités, glossaire — peut être lu indépendamment du contexte principal. |
<footer> | Pied de page ou de section | Copyright, liens légaux, meta-liens. Possiblement plusieurs (un par section). |
Règle simple : si un groupe d’éléments a un titre et un sujet unique, c’est une
<section>. Si le contenu a un sens propre sans le reste de la page, c’est un<article>.
Autres éléments utiles
| Élément | Rôle |
|---|---|
<figure> + <figcaption> | Illustration autonome avec légende. Peut être déplacée sans perdre de sens. |
<details> + <summary> | Accordion native, sans JavaScript. Idéal pour FAQ, spoilers, expansion. |
<time> | Date/heure machine-lisible. Attributs : datetime="YYYY-MM-DD". |
<mark> | Texte mis en évidence (recherche, annotation). |
<address> | Informations de contact. Non pas une adresse postale dans du texte, mais le bloc contact. |
<blockquote> | Citation longue. L’<aside> pour citation hors contexte, le <blockquote> pour citation dans le texte. |
<dialog> | Fenêtre modale native (attribut open pour afficher). |
Structures typiques
1. Page complète
<body>
<header>
<nav>…</nav>
</header>
<main>
<section>…</section>
<article>…</article>
</main>
<aside>…</aside>
<footer>…</footer>
</body>Piège : utiliser
<div id="main">à la place de<main>. Les lecteurs d’écran utilisent<main>pour le raccourci « sauter au contenu principal ». Une<div>ne donne pas cet accès.
2. Article + sidebar (blog, article)
<main>
<article>
<header>
<h1>Titre de l'article</h1>
<time datetime="2026-07-05">5 juillet 2026</time>
</header>
<section>
<p>Corps de l'article…</p>
</section>
<footer>
<p>Par <address>Jean Dupont</address></p>
</footer>
</article>
<aside>
<section>
<h2>Articles récents</h2>
<ul>…</ul>
</section>
</aside>
</main>Piège : mettre le
<aside>à l’intérieur du<article>. L’aside doit être un frère de l’article, pas un enfant. Le mettre à l’intérieur signifierait que la sidebar fait partie du contenu de l’article.
3. Navigation avec fil d’Ariane et menu secondaire
<body>
<header>
<nav aria-label="Fil d'Ariane">
<ol>
<li><a href="/">Accueil</a></li>
<li><a href="/docs">Documentation</a></li>
<li><span aria-current="page">Guide</span></li>
</ol>
</nav>
<nav aria-label="Navigation principale">
<ul>…</ul>
</nav>
</header>
</body>Piège : avoir plusieurs
<nav>sansaria-label. Un lecteur d’écran ne peut pas distinguer le menu principal de la pagination. Toujours ajouter unaria-labeldistinct si la page contient plusieurs navigations.
4. Footer multi-sections
<footer>
<nav aria-label="Liens légaux">
<ul>
<li><a href="/mentions-legales">Mentions légales</a></li>
<li><a href="/confidentialite">Confidentialité</a></li>
</ul>
</nav>
<p>© 2026 Entreprise</p>
<address>Contact : <a href="mailto:contact@example.com">contact@example.com</a></address>
</footer>5. Section avec figure et légende
<section>
<h2>Installation</h2>
<p>Exécution de la commande :</p>
<figure>
<pre><code>npm install mon-paquet</code></pre>
<figcaption>Figure 1. Commande d'installation du paquet.</figcaption>
</figure>
</section>6. Accordion natif avec <details>
<details>
<summary>Qu'est-ce que le HTML sémantique ?</summary>
<p>C'est utiliser les balises HTML pour exprimer le sens du contenu…</p>
</details>
<details>
<summary>Avantages pour le SEO ?</summary>
<p>Les moteurs de recherche comprennent mieux la structure…</p>
</details>Piège : utiliser
<details>pour du contenu qui doit être visible par défaut. L’élément est conçu pour être replié par défaut. Retirer l’attributopenpour cacher, ou utiliser un pattern CSS/JS si le comportement doit être différent.
Accessibilité (a11y) — les 5 points essentiels
1. Hiérarchie des titres
<h1> Titre de la page (UN SEUL par page) </h1>
<h2> Section principale </h2>
<h3> Sous-section </h3>
<h2> Section suivante </h2>
<h3> Sous-section </h3>Piège : sauter des niveaux (
<h1>→<h3>sans<h2>). Les lecteurs d’écran permettent de naviguer par titre. Un saut de niveau casse le parcours.
Piège : utiliser
<h1>pour le titre du site dans le header ET un autre<h1>pour le titre de la page. Résultat : deux<h1>dans la page. Utiliser<h2>pour le titre du site dans le header,<h1>uniquement pour le titre de la page.
2. aria-label et aria-labelledby
<!-- Libeller un nav sans texte visible -->
<nav aria-label="Réseaux sociaux">…</nav>
<!-- Raccourcis clavier -->
<button aria-label="Fermer">✕</button>
<!-- Lier une légende à sa figure -->
<figure aria-labelledby="fig-1">
<img src="graphique.png" alt="">
<figcaption id="fig-1">Graphique des ventes 2026</figcaption>
</figure>Piège : utiliser
aria-labelsur un<a>dont le texte visible dit déjà la même chose. Double lecture redondante. Garderaria-labeluniquement quand le texte visible ne transmet pas suffisamment d’information.
3. role — quand et pourquoi
Les rôles ARIA sont le plus souvent inutiles car les éléments sémantiques les portent déjà :
| Élément | Rôle implicite |
|---|---|
<nav> | role="navigation" |
<main> | role="main" |
<article> | role="article" |
<aside> | role="complementary" |
<header> | role="banner" |
<footer> | role="contentinfo" |
<button> | role="button" |
<input type="text"> | role="textbox" |
Règle ARIA n°1 : ne jamais utiliser
rolesi un élément sémantique standard fait le travail. L’exception est l’utilisation de<div>ou<span>pour un composant interactif (ex : un div qui imite un bouton → ajouterrole="button"+ gestion clavier).
4. Skip links (liens d’évitement)
Un lien caché en premier dans le <body> permet de passer directement au contenu principal :
<body>
<a href="#main-content" class="skip-link">Aller au contenu principal</a>
<header>…</header>
<main id="main-content">…</main>
</body>.skip-link {
position: absolute;
top: -100px;
left: 0;
z-index: 9999;
padding: 8px 16px;
background: #2563eb;
color: white;
}
.skip-link:focus {
top: 0;
}5. Textes alternatifs et alt
<!-- Image informative : décrire le contenu -->
<img src="diagramme-archi.png" alt="Architecture microservices avec 3 services communiquant via un bus événementiel">
<!-- Image décorative : alt vide -->
<img src="separator.png" alt="">
<!-- Image ignorée du lecteur d'écran (pure décoration) -->
<img src="decor.png" role="presentation" alt="">Piège : mettre une description longue dans
altpour les images décoratives. Le lecteur d’écran lit tout haut à chaque chargement de page. Les images purement esthétiques doivent avoiralt="".
Pièges récurrents
| Piège | Symptôme | Correction |
|---|---|---|
<div class="nav"> à la place de <nav> | Pas de raccourci lecteur d’écran | Remplacer par <nav> |
Plusieurs <main> sur la page | Confusion pour les lecteurs d’écran | Un seul <main> par page ; utiliser <section> pour les autres |
<div> cliquable sans role | Focus invisible, inaccessible au clavier | Utiliser <button> ou ajouter role="button" tabindex="0" + gestion Enter/Space |
alt manquant sur <img> | Erreur WCAG 1.1.1 | Toujours ajouter alt, même vide pour décorative |
<section> sans titre | Non significatif, confond avec un <div> | Ajouter un <h2>–<h6> ou utiliser <div> |
| Bouton sans texte accessible | Clic impossible via lecteur d’écran | Ajouter un texte, ou aria-label |
<audio> / <video> sans sous-titres | Non accessible aux malentendants | Ajouter <track kind="captions"> |
tabindex positif sur des <div> | Ordre de tabulation cassé | Utiliser tabindex="0" au maximum ; corriger l’ordre si besoin |
<span> pour styliser du texte | Pas de sémantique ajoutée | Utiliser <strong> (important), <em> (emphase), <mark> (surligné) |
<h1> dans le header + <h1> dans le main | Deux titres principaux | Header : <h2> pour le nom du site, main : <h1> pour le titre de page |
Checklist avant livraison
- Un seul
<h1>par page - Au moins un
<main>contenant le contenu principal - Chaque
<nav>a unaria-labeldistinct - Tous les
<img>ont un attributalt - Pas de saut de niveau dans la hiérarchie des titres
- Les éléments interactifs (
div/spancliquables) ontrole,tabindexet gestion clavier - Un
<details>n’a pas l’attributopenpar défaut (sauf intention) - Un skip link existe en premier dans le
<body> -
<audio>/<video>ont des sous-titres si pertinents
Arbre de décision rapide
Nouvelle page HTML à structurer ?
│
├─ Quelle est la structure globale ?
│ ├─ Header + nav + contenu + footer → éléments ① (voir § Structures typiques)
│ └─ Une seule région principale → <main> seul, pas d'aside
│
├─ Le contenu est-il autonome ?
│ ├─ Oui (article, carte, post) → <article>
│ └─ Non (regroupement thématique) → <section>
│
├─ Besoin d'une navigation ?
│ ├─ Menu principal → <nav aria-label="Navigation principale">
│ ├─ Pagination → <nav aria-label="Pagination">
│ ├─ Fil d'Ariane → <nav aria-label="Fil d'Ariane">
│ └─ Liens sociaux (inline) → <div> suffisant (pas une navigation)
│
├─ Besoin d'un contenu secondaire ?
│ ├─ Sidebar, publicités → <aside>
│ └─ Citation dans le texte → <blockquote>
│
└─ Besoin de cacher/déplier du contenu ?
└─ Oui → <details> + <summary> (pas de JS)Référence : MDN HTML sémantique .