Mon article de blog
Publié le par
Jean DupontLe contenu de l'article va ici. Il est compréhensible seul, même hors contexte.
Toutes les balises sémantiques HTML5, commentées et expliquées pour les intégrateurs débutants.
En-tête d'une page ou d'une section. Contient généralement le logo, le titre principal, la navigation ou une introduction. Peut apparaître plusieurs fois (en-tête de page + en-têtes d'articles). Rôle ARIA implicite : banner (quand enfant direct de body).
Zone de navigation principale ou secondaire du site. Réservée aux liens de navigation importants (menu principal, fil d'Ariane, pagination). Pas pour tous les groupes de liens. Rôle ARIA implicite : navigation.
Contenu principal et unique de la page. Ce qui distingue cette page de toutes les autres. UN SEUL par page. Ne peut pas être enfant de <article>, <aside>, <footer>, <header> ou <nav>. Rôle ARIA implicite : main.
Pied de page d'une page ou d'une section. Contient généralement : copyright, liens légaux, informations de contact, liens secondaires. Comme <header>, peut apparaître dans des articles et sections. Rôle ARIA implicite : contentinfo (quand enfant direct de body).
Contenu autonome et auto-suffisant : compréhensible seul, hors de son contexte. Exemples : article de blog, post de forum, tweet, commentaire, fiche produit, widget météo. Peut contenir son propre <header>, <footer>, <section>. Rôle ARIA implicite : article.
Publié le par
Jean DupontLe contenu de l'article va ici. Il est compréhensible seul, même hors contexte.
Regroupe du contenu thématiquement lié. Toujours accompagnée d'un titre. Apparaît dans le plan du document. Utiliser <div> si c'est juste pour le style CSS sans sens sémantique. Rôle ARIA implicite : region (si aria-labelledby ou aria-label).
Contenu indirectement lié au contenu principal. En rapport mais pas essentiel : barre latérale, encart, publicité, biographie de l'auteur, liens connexes, note de bas de page. Rôle ARIA implicite : complementary.
Conteneur générique block. Aucun sens sémantique. Utilisé quand aucune autre balise sémantique ne convient. Sert uniquement au style CSS ou au ciblage JavaScript. "div soup" = abus de <div> là où des balises sémantiques seraient plus appropriées. Rôle ARIA implicite : generic.
Conteneur générique inline. Version inline du <div>. Aucun sens sémantique. Utilisé pour styliser une portion de texte ou la cibler en JavaScript quand aucune autre balise inline ne convient. Exemple : colorer un mot dans une phrase.
Ce texte a un mot important mis en évidence.
Coordonnées de contact de l'auteur ou du propriétaire du document ou d'un article. Peut contenir : adresse postale, email, téléphone, réseaux sociaux, site web. Ne pas utiliser pour des adresses génériques non liées à l'auteur.
Titres de section, du plus important (h1) au moins important (h6). Créent la hiérarchie du document (plan du document). Les lecteurs d'écran les utilisent pour naviguer. Google les utilise pour comprendre la structure du contenu.
Règles importantes :
Paragraphe de texte. Élément block avec espacement automatique en haut et en bas. La balise fermante </p> est techniquement optionnelle mais toujours recommandée. Ne pas utiliser <p> pour des images ou des liens seuls (utiliser <div>).
Premier paragraphe. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.
Deuxième paragraphe. Les navigateurs ajoutent automatiquement une marge entre les paragraphes.
Saut de ligne dans du texte. Balise auto-fermante (pas de </br>). Utiliser UNIQUEMENT pour des sauts de ligne sémantiquement significatifs (poèmes, adresses, paroles de chanson). NE PAS utiliser <br> pour créer des espacements visuels (utiliser CSS margin/padding à la place).
Ligne 1 Ligne 2 (après un saut de ligne) Ligne 3 — Exemple : adresse postale
Séparation thématique entre des paragraphes ou sections. Balise auto-fermante. Représente un changement de sujet dans le contenu. Affiché comme une ligne horizontale par défaut. Le style peut être modifié avec CSS. Rôle ARIA implicite : separator.
Avant le séparateur thématique.
Après le séparateur — nouveau sujet.
Citation longue provenant d'une autre source, affichée en bloc.
L'attribut cite précise l'URL de la source.
Pour une citation courte inline, utiliser <q>.
Pour nommer la source, utiliser <cite> à l'intérieur.
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."
— Martin Fowler
Citation courte inline (dans une phrase). Le navigateur ajoute automatiquement les guillemets selon la langue du document (lang="fr" → guillemets français « »). Pour une citation longue, utiliser <blockquote>.
Comme disait Shakespeare : To be or not to be.
Nom d'une œuvre : livre, film, chanson, article, tableau, site web... Affiché en italique par défaut. NE PAS utiliser pour nommer une personne (utiliser pour l'œuvre, pas l'auteur).
J'ai lu Le Petit Prince d'Antoine de Saint-Exupéry.
<pre> — Texte préformaté : conserve les espaces,
les tabulations et les sauts de ligne exactement comme dans le HTML.
Police monospace par défaut.
<code> — Fragment de code informatique inline.
Police monospace. Souvent utilisé dans du texte courant.
Combo <pre><code> → Bloc de code multilignes (la combinaison standard pour afficher du code).
Utilisez console.log() pour déboguer.
function saluer(nom) {
return "Bonjour, " + nom + " !";
}
console.log(saluer("Alice"));
Balises techniques liées au code et aux interactions clavier.
kbd — Entrée clavier : Appuyez sur Ctrl + C pour copier.
samp — Sortie d'un programme : Erreur 404 : fichier introuvable
var — Variable mathématique : E = m × c²
Balises pour modifier l'apparence ou le sens d'une portion de texte.
<strong> — Texte important (gras sémantique). Les lecteurs d'écran accentuent la lecture. ≠ <b> (gras visuel pur).
<em> — Texte mis en emphase (italique sémantique). Lecteurs d'écran changent le ton. ≠ <i> (italique visuel).
<b> — Gras sans importance sémantique (mots-clés, noms de produits).
<i> — Italique sans emphase (termes techniques, noms étrangers, pensées).
<u> — Soulignement sémantique (fautes d'orthographe, noms propres chinois). Éviter car ressemble à un lien.
<s> — [début texte barré] Texte barré / obsolète [fin texte barré] (prix réduit, ancienne version).
<del> — Texte supprimé (modification de document, WCAG accessible).
<ins> — Texte inséré (contenu ajouté à un document).
<mark> — Texte surligné (résultat de recherche, passage pertinent).
<small> — Texte de petite taille (mentions légales, copyright, commentaires).
<sub> — H2O — Indice inférieur (formules chimiques, notes de bas de page).
<sup> — E=mc2 — Exposant supérieur (puissances, notes de renvoi).
<abbr> — HTML — Abréviation avec définition au survol (title obligatoire).
<dfn> — HTML — Définition d'un terme (première occurrence).
<time> — Publié le — Date/heure machine-readable.
<wbr> — Longmot
<bdi> — نص عربي — Isolement bidirectionnel (texte RTL dans texte LTR).
<bdo> — Texte inversé — Direction de texte forcée (dir="rtl" ou "ltr").
<ruby> <rt> <rp> — 漢 — Annotations phonétiques (japonais, chinois).
<data> — Produit A — Valeur machine associée à du contenu lisible.
<output> — Résultat : — Résultat d'un calcul ou d'une action.
Liste non ordonnée (unordered list). L'ordre des éléments n'a pas d'importance. Affichée avec des puces par défaut. Ne peut contenir que des <li> comme enfants directs. Utiliser quand les items sont équivalents et interchangeables.
Liste ordonnée (ordered list). L'ordre est important. Affichée avec des numéros par défaut. Exemples : étapes d'une recette, classement, procédure.
Élément de liste (list item). Enfant direct de <ul>, <ol> ou <menu>. Peut contenir n'importe quel contenu (texte, images, d'autres listes...).
Liste de définitions (description list). Paires terme/définition.
<dl> — Conteneur de la liste de définitions.
<dt> — Terme à définir (definition term).
<dd> — Définition du terme (definition description).
Un <dt> peut avoir plusieurs <dd>. Plusieurs <dt> peuvent
partager un <dd>. Utilisé pour glossaires, FAQ, métadonnées.
Liste de commandes ou d'options (sémantiquement un menu d'actions). Similaire à <ul> mais sémantiquement destiné aux interfaces (barres d'outils, menus contextuels). Contient des <li> avec des <button> ou <a> à l'intérieur.
Hyperlien (anchor). Élément fondamental du Web. Peut lier vers : une autre page, une ancre (#id), un email, un téléphone, un fichier, un fragment JavaScript. Rôle ARIA implicite : link. Utiliser <a href> pour naviguer, <button> pour agir.
🔗 — Lien avec aria-label car l'icône seule n'est pas descriptive
Affiche une image. Balise auto-fermante.
L'attribut alt est OBLIGATOIRE pour l'accessibilité
(WCAG 1.1.1). Les lecteurs d'écran lisent le texte alt.
Si l'image est décorative : alt="" (vide, pas absent).
<figure> — Contenu autonome référencé depuis le texte principal :
image, graphique, diagramme, code, poème, tableau...
Peut être déplacé sans affecter le flux principal.
<figcaption> — Légende du <figure> (facultative).
Premier ou dernier enfant de <figure>.
<figcaption> ajoute une légende accessible à la figure.
<picture> — Conteneur d'images adaptatives.
Permet de servir des formats modernes (WebP, AVIF) avec
fallback vers JPEG/PNG. Aussi pour le responsive art direction
(image différente selon la taille d'écran).
<source> — Source alternative testée dans l'ordre.
Le navigateur choisit la première qui correspond.
<img> en dernier = fallback obligatoire.
<map> — Définit une image cliquable avec plusieurs zones.
<area> — Zone cliquable dans la map (auto-fermante).
shape = rect (rectangle), circle (cercle), poly (polygone), default (tout).
Scalable Vector Graphics — Images vectorielles embarquées directement dans le HTML. Infiniment zoomables sans perte de qualité. Accessibles (les éléments peuvent avoir aria-label, title, desc). Manipulables par CSS et JavaScript. Meilleur pour : logos, icônes, graphiques, animations.
Zone de dessin bitmap contrôlée par JavaScript. Utilisé pour : jeux 2D/3D, visualisations de données, effets, retouche photo, animations complexes. Contrairement à SVG : pas accessible, pas scalable, pas de DOM. Le contenu entre les balises = fallback si canvas non supporté.
Lecteur vidéo natif HTML5. Plusieurs <source> = compatibilité navigateurs (différents formats supportés). Le texte entre les balises = fallback pour les navigateurs ne supportant pas la vidéo. WCAG : toujours fournir des sous-titres (track kind="subtitles").
⚠️ Aucune vidéo chargée dans cet exemple — le poster s'affiche.
Lecteur audio natif HTML5. Même principe que <video> mais sans dimensions visuelles. Formats : MP3, OGG, WAV, AAC, FLAC.
Piste de texte pour <video> et <audio> (sous-titres, légendes, chapitres, descriptions). Format WebVTT (.vtt). WCAG 1.2.2 exige des sous-titres pour toutes les vidéos.
Inline Frame — Embarque une autre page web dans la page courante. Utilisé pour : vidéos YouTube/Vimeo, Google Maps, Tweets, formulaires externes, widgets de paiement. Chaque iframe a son propre contexte de navigation (DOM séparé). SÉCURITÉ : sandbox isole le contenu. CSP contrôle les permissions.
Anciens mécanismes d'intégration (Flash, Java, plugins).
Aujourd'hui quasi-obsolètes. Remplacés par <video>, <audio>,
<iframe>, SVG, Canvas.
<embed> — Contenu externe sans fallback possible.
<object> — Contenu externe avec fallback (contenu entre les balises).
<param> — Paramètre pour <object>.
Réservé aux données tabulaires (lignes + colonnes avec relation). NE PAS utiliser pour la mise en page (utiliser CSS Grid/Flexbox). Un tableau mal structuré est incompréhensible pour les lecteurs d'écran.
| Langage | Type | Rôle | Difficulté |
|---|---|---|---|
| HTML | Balisage | Structure | ⭐ |
| CSS | Style | Présentation | ⭐⭐ |
| JavaScript | Programmation | Interactivité | ⭐⭐⭐ |
| Total : 3 langages fondamentaux du Web | |||
Conteneur d'un formulaire interactif. Groupe les champs de saisie et définit comment les données sont envoyées au serveur.
Étiquette d'un champ de formulaire. OBLIGATOIRE
pour l'accessibilité (WCAG 1.3.1, RGAA 11.1).
Cliquer sur le label active/coche le champ associé.
Deux façons de lier : for="id" sur le label + id="id" sur l'input,
ou imbriquer l'input dans le label.
Champ de saisie. Balise auto-fermante. Le type détermine l'interface affichée et la validation. Toujours associé à un <label>.
Zone de texte multiligne. Redimensionnable par l'utilisateur. Pas d'attribut value — le contenu est entre les balises.
Liste déroulante de sélection.
<select> — Conteneur de la liste.
<option> — Élément de la liste.
<optgroup> — Groupe d'options avec label.
Liste de suggestions pour un <input list="id">. Combine la liberté de saisie d'un input avec des suggestions comme un select. L'utilisateur peut taper autre chose que les suggestions.
<fieldset> — Groupe des champs de formulaire liés.
Dessine une bordure autour du groupe par défaut.
<legend> — Titre/description du groupe (toujours premier enfant de fieldset).
OBLIGATOIRE pour l'accessibilité des groupes de radios/checkboxes.
Le lecteur d'écran lit la légende avant chaque option du groupe.
Bouton interactif. Peut contenir du HTML (texte, images, icônes). Plus flexible que <input type="button">. Rôle ARIA implicite : button. Toujours préciser type= pour éviter les comportements inattendus.
Éléments de formulaire pour afficher des valeurs.
Calcul (a + b = résultat) :
Un formulaire d'inscription complet avec toutes les bonnes pratiques.
<details> — Widget d'accordéon natif HTML.
Le contenu est masqué/affiché au clic sans JavaScript.
L'attribut open l'ouvre par défaut.
<summary> — Premier enfant de details, toujours visible,
sert de titre cliquable pour ouvrir/fermer.
HTML (HyperText Markup Language) est le langage de balisage standard pour créer des pages web. Il définit la structure et le sens du contenu web.
CSS (Cascading Style Sheets) est le langage de style utilisé pour décrire la présentation d'un document HTML.
Boîte de dialogue / modale native HTML5. Gère automatiquement :
focus trap, accessibilité ARIA, arrière-plan modal (::backdrop).
Ouverture : dialog.showModal() (modale) ou dialog.show() (non-modale).
Fermeture : bouton avec method="dialog" dans un <form> interne.
L'attribut open l'affiche (préférer la méthode JS).
Rôle ARIA implicite : dialog.
<template> — Contenu HTML inerte (non rendu, non exécuté).
Stocke du HTML réutilisable activable par JavaScript via
template.content.cloneNode(true).
Utilisé pour les Web Components et la génération dynamique.
<slot> — Emplacement de contenu dans un Shadow DOM
(Web Components). Permet d'injecter du contenu depuis l'extérieur
du composant.
<script> — Intègre ou charge du JavaScript.
Dans le body = exécuté quand rencontré.
Toujours en bas du <body> ou avec defer/async
dans le head, pour ne pas bloquer l'affichage de la page.
<noscript> — Contenu de fallback affiché si JavaScript
est désactivé. Dans le body : peut contenir n'importe quel HTML.
Ces attributs peuvent être utilisés sur n'importe quelle balise HTML.
L'accessibilité web (a11y) consiste à concevoir des sites et applications utilisables par toutes les personnes, quelles que soient leurs capacités physiques, cognitives ou technologiques.
Recommandations du W3C pour l'accessibilité des contenus web. Version actuelle : WCAG 2.2 (2023). Organisées autour de 4 principes (POUR) :
Le contenu doit être présentable de façon perceptible par les sens. Textes alternatifs, sous-titres, contrastes suffisants.
Les composants d'interface et la navigation doivent être utilisables. Navigation clavier, pas de clignotement dangereux.
Le contenu et l'interface doivent être compréhensibles. Langue déclarée, formulaires explicites, comportement prévisible.
Le contenu doit être interprétable par les technologies d'assistance. HTML valide, ARIA correct, compatibilité navigateurs.
3 niveaux de conformité :
Déclinaison française des WCAG 2.1 AA, publiée par la DINUM. Version actuelle : RGAA 4.1 (2021). Obligatoire en France pour : services publics, établissements publics, entreprises de +250M€ de CA. Basé sur des tests techniques précis organisés en 13 thématiques.
alt="description" sur <img>. aria-label sur <svg>. alt="" si décoratif.<track kind="captions"><track kind="descriptions">autocomplete avec les valeurs standardisées (name, email, tel...).<a href="#main">Aller au contenu</a><title> descriptif et unique pour chaque page.:focus-visible { outline: 3px solid ... }min-height: 44px; min-width: 44px<html lang="fr"> obligatoire. Permet aux synthèses vocales de choisir la bonne voix.<span lang="en">Hello world</span> pour les passages dans une autre langue.aria-invalid="true" + message d'erreur.role="alert" ou aria-live.ARIA est un ensemble d'attributs HTML supplémentaires qui donnent des informations sémantiques aux technologies d'assistance (lecteurs d'écran, commande vocale) quand le HTML natif ne suffit pas.
<button> est préférable à <div role="button">.role="button" sur un <a> est rarement justifié.role="button", gérer Entrée et Espace en JS.
Définit la nature sémantique d'un élément pour les technologies d'assistance.
Les balises HTML ont déjà des rôles implicites (voir tableau ci-dessous).
N'utiliser role que quand le HTML natif est insuffisant.
Rôles implicites des balises HTML (ne pas les redéfinir)
| Balise HTML | Rôle ARIA implicite | Condition |
|---|---|---|
<header> | banner | Enfant direct de <body> |
<footer> | contentinfo | Enfant direct de <body> |
<main> | main | Toujours |
<nav> | navigation | Toujours |
<aside> | complementary | Toujours |
<section> | region | Si aria-labelledby ou aria-label |
<article> | article | Toujours |
<form> | form | Si aria-label ou aria-labelledby |
<button> | button | Toujours |
<a href> | link | Avec href |
<a> (sans href) | generic | Sans href |
<h1>–<h6> | heading | Toujours |
<ul>, <ol> | list | Toujours |
<li> | listitem | Toujours |
<img> | img | Avec alt non vide |
<img alt=""> | presentation | alt vide = décoratif |
<table> | table | Toujours |
<input type="text"> | textbox | Toujours |
<input type="checkbox"> | checkbox | Toujours |
<input type="radio"> | radio | Toujours |
<select> | combobox / listbox | Selon contexte |
<dialog> | dialog | Toujours |
<details> | group | Toujours |
<summary> | button | Toujours |
<div>, <span> | generic | Aucun rôle sémantique |
Rôles ARIA à utiliser manuellement (sur des <div> ou éléments non natifs)
Les attributs aria-* se divisent en deux catégories :
propriétés (valeurs stables, rarement modifiées) et
états (valeurs changeant dynamiquement via JavaScript).
Premier élément focusable de la page. Invisible jusqu'au focus clavier. Permet de sauter la navigation principale pour aller directement au contenu. Obligatoire pour tout site avec une navigation répétitive.
<!-- En PREMIER dans le body -->
<a href="#contenu-principal" class="skip-link">
Aller au contenu principal
</a>
<!-- CSS -->
.skip-link {
position: absolute;
top: -100px; /* Hors écran par défaut */
left: 16px;
z-index: 9999;
background: #1a1a2e;
color: #fff;
padding: 10px 18px;
text-decoration: none;
transition: top .2s;
}
.skip-link:focus {
top: 0; /* Visible au focus */
}
Un bouton avec seulement une icône doit avoir un nom accessible. Sans texte visible, les lecteurs d'écran ne savent pas à quoi il sert.
<!-- ❌ MAUVAIS : aucun nom accessible -->
<button type="button">✕</button>
<!-- ✅ CORRECT : aria-label donne le nom -->
<button type="button" aria-label="Fermer la fenêtre">
<span aria-hidden="true">✕</span>
</button>
<!-- ✅ AUSSI CORRECT : texte masqué visuellement -->
<button type="button">
<svg aria-hidden="true" focusable="false">...</svg>
<span class="visually-hidden">Fermer la fenêtre</span>
</button>
<!-- CSS visually-hidden (meilleure pratique) -->
.visually-hidden {
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
}
❌ Sans aria-label
✅ Avec aria-label
Un bouton qui s'active/désactive doit communiquer son état via
aria-pressed. Le CSS cible cet attribut directement.
<button type="button"
aria-pressed="false"
id="btn-theme">
Mode sombre
</button>
/* CSS : cibler l'état ARIA directement */
[aria-pressed="true"] {
background: #1a1a2e;
color: white;
}
var btn = document.getElementById('btn-theme');
btn.addEventListener('click', function() {
var etat = this.getAttribute('aria-pressed') === 'true';
this.setAttribute('aria-pressed', String(!etat));
this.setAttribute('aria-label',
!etat ? 'Désactiver le mode sombre'
: 'Activer le mode sombre');
});
Le bouton burger contrôle la visibilité du menu.
aria-expanded + aria-controls communiquent
l'état et la relation aux technologies d'assistance.
<button type="button"
id="burger"
aria-controls="main-nav"
aria-expanded="false"
aria-label="Ouvrir le menu de navigation">
<span aria-hidden="true">☰</span>
</button>
<nav id="main-nav" hidden>
<!-- liens -->
</nav>
var burger = document.getElementById('burger');
var nav = document.getElementById('main-nav');
burger.addEventListener('click', function() {
var ouvert = this.getAttribute('aria-expanded') === 'true';
this.setAttribute('aria-expanded', String(!ouvert));
this.setAttribute('aria-label',
ouvert ? 'Ouvrir le menu' : 'Fermer le menu');
if (ouvert) {
nav.setAttribute('hidden', '');
} else {
nav.removeAttribute('hidden');
}
});
Les messages d'erreur doivent être annoncés par les lecteurs d'écran
et liés aux champs en erreur via aria-describedby + aria-invalid.
<!-- Champ valide -->
<div>
<label for="email">
Email <span aria-hidden="true">*</span>
<span class="visually-hidden">(obligatoire)</span>
</label>
<input type="email"
id="email"
name="email"
required
aria-required="true"
aria-describedby="email-aide email-erreur"
aria-invalid="false">
<small id="email-aide">Format : vous@exemple.com</small>
<span id="email-erreur"
role="alert"
aria-live="assertive"
hidden>
❌ Email invalide. Vérifiez le format.
</span>
</div>
function afficherErreur(inputId, msgId) {
var input = document.getElementById(inputId);
var msg = document.getElementById(msgId);
input.setAttribute('aria-invalid', 'true');
msg.removeAttribute('hidden');
}
function effacerErreur(inputId, msgId) {
var input = document.getElementById(inputId);
var msg = document.getElementById(msgId);
input.setAttribute('aria-invalid', 'false');
msg.setAttribute('hidden', '');
}
Une modale accessible doit : piéger le focus à l'intérieur (focus trap), se fermer avec Échap, remettre le focus sur l'élément déclencheur à la fermeture, et bloquer la navigation dans le reste de la page.
<button id="open-modal" type="button"
aria-haspopup="dialog">
Ouvrir la modale
</button>
<div id="modale"
role="dialog"
aria-modal="true"
aria-labelledby="modal-titre"
aria-describedby="modal-desc"
hidden>
<div class="modal-box">
<h2 id="modal-titre">Titre de la modale</h2>
<p id="modal-desc">Description du contenu.</p>
<button id="close-modal" type="button"
aria-label="Fermer la modale">✕</button>
</div>
</div>
var trigger, modal, closeBtn;
function ouvrir() {
trigger = document.activeElement;
modal.removeAttribute('hidden');
document.body.style.overflow = 'hidden';
closeBtn.focus();
document.addEventListener('keydown', trapFocus);
}
function fermer() {
modal.setAttribute('hidden', '');
document.body.style.overflow = '';
document.removeEventListener('keydown', trapFocus);
trigger.focus();
}
function trapFocus(e) {
if (e.key === 'Escape') { fermer(); return; }
if (e.key !== 'Tab') return;
var focusables = modal.querySelectorAll(
'a[href], button, input, [tabindex]:not([tabindex="-1"])'
);
var first = focusables[0];
var last = focusables[focusables.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
Les contenus qui se mettent à jour dynamiquement (notifications, résultats de recherche, messages de statut) doivent être annoncés par les lecteurs d'écran sans que le focus soit déplacé.
<!-- role="alert" : annonce immédiate (assertive) -->
<div role="alert" aria-live="assertive" aria-atomic="true">
<!-- Vide au départ, rempli par JS en cas d'erreur critique -->
</div>
<!-- role="status" : annonce différée (polite) -->
<div role="status" aria-live="polite" aria-atomic="true">
<!-- Confirmations, informations, résultats -->
</div>
<!-- Exemple : notification de succès -->
<div id="notif"
aria-live="polite"
aria-atomic="true"
class="visually-hidden">
</div>
function notifier(message) {
var zone = document.getElementById('notif');
zone.textContent = ''; // Vider d'abord
setTimeout(function() {
zone.textContent = message;
}, 100);
}
Le pattern tabs utilise role="tablist", role="tab",
role="tabpanel" et les flèches clavier pour naviguer entre onglets.
<div role="tablist" aria-label="Onglets de cours">
<button role="tab"
id="tab-html"
aria-controls="panel-html"
aria-selected="true"
tabindex="0">HTML</button>
<button role="tab"
id="tab-css"
aria-controls="panel-css"
aria-selected="false"
tabindex="-1">CSS</button>
</div>
<div role="tabpanel"
id="panel-html"
aria-labelledby="tab-html">
Contenu du panel HTML
</div>
<div role="tabpanel"
id="panel-css"
aria-labelledby="tab-css"
hidden>
Contenu du panel CSS
</div>
<!-- Navigation clavier : flèche gauche/droite entre tabs
tabindex="-1" sur les tabs non sélectionnés
tabindex="0" sur le tab actif -->
Le ratio de contraste est calculé entre la couleur du texte et son arrière-plan.
✅ PASSE — 16.1:1 (AAA)
✅ PASSE — 5.1:1 (AA)
✅ PASSE — 4.6:1 (AA)
❌ ÉCHOUE — 2.5:1 (insuffisant)
❌ ÉCHOUE — 2.8:1 (insuffisant)
✅ PASSE — 5.2:1 (AA en dark)
Outils de vérification : WebAIM Contrast Checker, Colour Contrast Analyser, extensions navigateur : axe, WAVE, Accessibility Insights.
Tout élément interactif doit avoir un indicateur de focus visible
pour les utilisateurs clavier. Utiliser :focus-visible
(focus clavier uniquement, pas souris) plutôt que :focus.
/* ❌ MAUVAIS — supprime tout focus */
* { outline: none; }
a:focus { outline: none; }
/* ✅ CORRECT — focus clavier visible, souris invisible */
:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
border-radius: 3px;
}
:focus:not(:focus-visible) {
outline: none; /* Retire l'outline pour les clics souris */
}
/* Focus personnalisé avec box-shadow (contourne les overflow:hidden) */
:focus-visible {
outline: none;
box-shadow: 0 0 0 3px #fff, 0 0 0 5px #005fcc;
}