đ HTML5 Ultimate â SĂ©mantique, APIs & Performance
Guide complet : Structure sémantique, Formulaires 2.0, Multimédia, Accessibilité et nouvelles APIs.
Sémantique & Structure
header, nav, main, article vs section.
SEO & Meta Tags
Viewport, Open Graph (OG), Twitter Cards, Favicons modernes.
<head> SocialAccessibilité (ARIA)
Attributs aria-*, role et bonnes pratiques clavier.
Inputs Modernes
date, color, range, search, tel.
Validation Native
Constraint Validation API. pattern, required, setCustomValidity.
Dialog & Popups
L'élément <dialog> natif. Plus besoin de libs JS lourdes.
Audio & Vidéo
Codecs, sous-titres (track), contrĂŽles et fallback.
Images Responsives
MaĂźtriser srcset, sizes et la balise <picture>.
SVG vs Canvas
Vecteurs (DOM) vs Pixels (JS). Quand utiliser quoi ?
Vector 2D ContextWeb Components
Custom Elements, Shadow DOM et <template>. Intro.
Optimisation Chargement
defer vs async, preload, preconnect, Lazy loading.
Cheat-sheet & Boilerplate
Le template HTML5 parfait 2025.
Snippet Copy-PasteLe Squelette HTML5 Parfait
Oubliez la "Div Soup". Chaque zone a son rÎle précis.
<body>
<!-- 1. HEADER GLOBAL -->
<header>
<!-- Logo, Titre site -->
<nav>
<!-- Liens majeurs UNIQUEMENT -->
<ul><li><a href="/">Accueil</a></li></ul>
</nav>
</header>
<!-- 2. CONTENU PRINCIPAL (Unique par page) -->
<main>
<h1>Titre de la page</h1> <!-- Le H1 est souvent ici -->
<article>
<!-- Contenu autonome -->
</article>
<aside>
<!-- Sidebar : Lié au contenu mais non critique -->
<div class="ad">Pub</div>
</aside>
</main>
<!-- 3. FOOTER GLOBAL -->
<footer>
<small>© 2025</small>
</footer>
</body>RĂšgles d'Or
<main>: Une seule fois par page. Ne doit pas contenir la navigation globale, le footer global ou la sidebar de site.<nav>: RĂ©servĂ© aux blocs de navigation majeurs (Menu principal, Table des matiĂšres, Pagination). Ne pas utiliser pour une petite liste de liens en footer.<aside>: Contenu "tangentiel". Si on le supprime, l'article principal doit rester comprĂ©hensible. (Ex: Bio auteur, Pubs, Articles similaires).<header>&<footer>: Peuvent ĂȘtre utilisĂ©s plusieurs fois ! (Ex: Un header de site, et un header dans chaque<article>).
C'est la confusion n°1 des développeurs. Voici comment trancher.
| Balise | Définition | Le test "RSS / Syndication" |
|---|---|---|
<article> | Contenu indépendant et autonome. | OUI. Si je prends ce bloc et que je le colle dans un flux RSS ou sur un autre site, a-t-il du sens tout seul ? (Ex: Post de blog, Commentaire produit, Widget Météo). |
<section> | Regroupement thématique de contenu. | NON. C'est juste un chapitre d'un ensemble plus grand. Doit généralement avoir un titre (h2-h6). (Ex: Chapitre "Intro", Bloc "Nos Tarifs"). |
<div> | Conteneur stylistique générique. | NON. Utilisé uniquement pour le CSS (Flexbox wrapper, Grid container, couleur de fond). Aucune valeur sémantique. |
Exemple imbriqué complexe
<article> <!-- Le post de blog entier -->
<h1>L'histoire du Web</h1>
<section> <!-- Chapitre 1 -->
<h2>Les débuts</h2>
<p>...</p>
</section>
<section id="comments"> <!-- Zone des commentaires -->
<h2>Commentaires</h2>
<article> <!-- Commentaire individuel (Autonome !) -->
<h3>Jean a dit :</h3>
<p>Super article !</p>
</article>
</section>
</article>Pourquoi Google aime la sémantique ?
Les robots d'indexation ne "voient" pas votre site, ils lisent le code.
- Pondération : Les mots-clés dans un
<main>ou un<h1>ont plus de poids que dans un<div>ou un footer. - Structure : Google comprend mieux la hiérarchie de l'information (Titre > Intro > Chapitre).
- Rich Snippets : Une structure claire aide à générer des "Sitelinks" ou des extraits enrichis dans les résultats de recherche.
Accessibilité (A11y) & Landmarks
Pour les utilisateurs aveugles utilisant des lecteurs d'écran (NVDA, VoiceOver) :
- Navigation rapide : L'utilisateur peut appuyer sur une touche pour sauter directement au
<main>(raccourci "D" ou "M"), ignorant ainsi le menu répétitif. - Div Soup = Enfer : Si tout est une
<div>, l'utilisateur doit écouter tout le code séquentiellement sans pouvoir "scanner" la page.
Les Indispensables
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Titre : 50-60 caractÚres max (mot-clé au début) -->
<title>Formation HTML5 Avancée | Ideo-Lab</title>
<!-- Description : 150-160 chars. C'est votre "Pub" dans Google -->
<meta name="description" content="Maßtrisez le HTML5 moderne : Sémantique, SEO technique et APIs...">
<!-- Canonical : Ăvite le 'Duplicate Content' -->
<link rel="canonical" href="https://site.com/url-propre">
</head>ContrĂŽle des Robots
Dites Ă Google quoi faire de cette page.
<!-- Standard (Indexable + Suivre les liens) -->
<meta name="robots" content="index, follow">
<!-- Page Privée / Admin (Ne pas indexer) -->
<meta name="robots" content="noindex, nofollow">
<!-- Indexer mais ne pas afficher le cache -->
<meta name="robots" content="noarchive"> Astuce : La balise canonical est cruciale si votre site est accessible via plusieurs URLs (ex: avec ou sans paramĂštres `?ref=...`).
Transformez vos liens partagés sur WhatsApp, Slack, LinkedIn et Twitter en cartes riches.
Open Graph (Facebook, LinkedIn, Discord)
<!-- Type de contenu -->
<meta property="og:type" content="website">
<!-- URL Canonique du partage -->
<meta property="og:url" content="https://monsite.com/">
<!-- Titre (Peut ĂȘtre diffĂ©rent du <title>) -->
<meta property="og:title" content="Guide Ultime HTML5">
<!-- Description -->
<meta property="og:description" content="Découvrez...">
<!-- IMAGE : Le plus important (1200x630 px recommandé) -->
<meta property="og:image" content="https://monsite.com/og.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">Twitter Cards (X)
<!-- Grande image (impact visuel max) -->
<meta name="twitter:card" content="summary_large_image">
<!-- Compte du site -->
<meta name="twitter:site" content="@ideo_lab">
<!-- Compte de l'auteur -->
<meta name="twitter:creator" content="@dev_name">
<!-- Titre (Fallback sur OG si absent) -->
<meta name="twitter:title" content="Guide HTML5">Testez vos cartes :
Usez du Twitter Card Validator ou du LinkedIn Post Inspector.
Favicons 2025 & UI Mobile
Ne chargez plus 15 fichiers `.png`. Le format SVG est supporté et permet le Dark Mode.
| Balise | Usage |
|---|---|
icon (svg) | Navigateurs modernes. Léger, vectoriel. |
apple-touch-icon | IcÎne sur l'écran d'accueil iOS (iPhone/iPad). 180x180px. |
theme-color | Colore la barre d'adresse du navigateur mobile (Chrome/Safari). |
<!-- 1. Favicon SVG (Moderne) -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<!-- 2. Favicon ICO (Fallback Legacy) -->
<link rel="alternate icon" href="/favicon.ico">
<!-- 3. iOS Home Screen (Pas de transparence !) -->
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<!-- 4. Couleur du navigateur (Barre d'adresse) -->
<meta name="theme-color" content="#234A26">
<!-- On peut mĂȘme changer la couleur selon le Dark Mode -->
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#0f172a" media="(prefers-color-scheme: dark)">
<!-- 5. Web App Manifest (PWA) -->
<link rel="manifest" href="/site.webmanifest">Gestion du Focus (Tabindex)
Tout Ă©lĂ©ment interactif DOIT ĂȘtre atteignable via la touche Tab.
| Valeur | Comportement | Usage |
|---|---|---|
0 | IntÚgre l'élément dans le flux naturel de tabulation. | Rendre une div ou un span focusable. |
-1 | Focusable via JS (el.focus()) mais pas via Tab. | Modales, messages d'erreur, widgets complexes. |
1+ | Anti-pattern. Force un ordre illogique. | A bannir. Suivez l'ordre du DOM. |
Styles de Focus (Outline)
Ne supprimez jamais l'outline sans le remplacer. C'est le seul repĂšre pour la navigation clavier.
/* â A NE JAMAIS FAIRE SEUL */
*:focus { outline: none; }
/* â
La bonne méthode : :focus-visible */
/* S'active seulement au clavier, pas au clic souris */
button:focus-visible {
outline: 2px solid #38bdf8;
outline-offset: 2px;
}L'ordre logique (DOM)
L'ordre visuel (CSS Flex/Grid order) doit correspondre à l'ordre du DOM HTML, sinon le focus saute de maniÚre imprévisible.
Nommer les éléments (Labels)
Indispensable pour les boutons icĂŽnes (sans texte) ou les inputs sans label visible.
<!-- Label invisible (Lu par le Screen Reader) -->
<button aria-label="Fermer le menu">
<i class="fa fa-times"></i>
</button>
<!-- Référence un autre élément (TrÚs puissant) -->
<h2 id="modal-title">Supprimer ?</h2>
<div role="dialog" aria-labelledby="modal-title">
...
</div>Ătats Dynamiques (Pour le JS)
HTML5 ne suffit pas pour les widgets JS (Accordéons, Modales, Menus).
aria-expanded="true/false": Pour les menus déroulants ou accordéons. Indique si la zone est ouverte.aria-hidden="true": Cache un élément aux lecteurs d'écran (ex: icÎne décorative) tout en le laissant visible.aria-live="polite": Pour les notifications (Toasts). Le lecteur d'écran lira le message dÚs qu'il apparaßt, sans couper l'utilisateur.
RÚgle N°1 de l'ARIA
Mauvais Pattern (Div Button)
<!-- â Mauvais -->
<!-- Pas de focus, pas d'activation "Entrée/Espace" -->
<div onclick="save()">Sauver</div>
<!-- â ïž "RĂ©parĂ©" avec ARIA (Trop lourd) -->
<div role="button" tabindex="0"
onkeydown="if(event.key==='Enter') save()"
onclick="save()">
Sauver
</div>Bon Pattern (Natif)
<!-- â
Parfait -->
<!-- Focus, Click, Entrée et Espace gérés nativement -->
<button onclick="save()">Sauver</button>Quand utiliser role="..." ?
role="alert": Message d'erreur critique.role="dialog": FenĂȘtre modale (empĂȘche de sortir).role="tablist" / "tab": SystĂšme d'onglets complexe.
Types & Claviers Virtuels (iOS/Android)
Choisir le bon type ne sert pas qu'Ă la validation, cela adapte le clavier du smartphone.
| Type | Clavier Affiché | Usage |
|---|---|---|
email | Touche @ et . accessibles. | Emails. |
tel | Pavé numérique (0-9, *, #). | Téléphones, codes PIN. |
url | Touche .com ou /. | Sites web. |
search | Touche "Rechercher" (Go) + Croix "Clear". | Barres de recherche. |
L'attribut inputmode (Le Secret)
Parfois, type="number" est mauvais (flÚches de scroll, validation stricte). Utilisez inputmode pour forcer le pavé numérique tout en gardant un champ texte libre.
<!-- Code Carte Bleue (Pas de flĂšches up/down) -->
<input type="text" inputmode="numeric" pattern="[0-9]*" placeholder="0000 0000...">
<!-- Montant avec décimales -->
<input type="text" inputmode="decimal" placeholder="10.50">
<!-- Code 2FA (One Time Password) -->
<input type="text" autocomplete="one-time-code" inputmode="numeric">UX Clavier (EnterKeyHint)
<!-- Change le label de la touche "Entrée" sur mobile -->
<input type="text" enterkeyhint="send"> <!-- "Envoyer" -->
<input type="search" enterkeyhint="search"> <!-- "Rechercher" -->Date & Heure (Natif)
Plus besoin de lourdes bibliothÚques JS (Datepicker). L'interface dépend du navigateur/OS.
<!-- Date seule (JJ/MM/AAAA) -->
<input type="date" min="2025-01-01" max="2025-12-31">
<!-- Date et Heure -->
<input type="datetime-local">
<!-- Mois seulement -->
<input type="month">
<!-- Heure -->
<input type="time">Couleurs
<label>Couleur du produit</label>
<input type="color" value="#ff0000">Fichiers & Caméra (Mobile)
Astuce pour ouvrir la caméra directement sur mobile.
<!-- Classique (Multiples) -->
<input type="file" multiple accept=".pdf,.docx">
<!-- Images uniquement -->
<input type="file" accept="image/*">
<!-- Ouvrir Caméra (Selfie) -->
<input type="file" accept="image/*" capture="user">
<!-- Ouvrir Caméra (ArriÚre / Environnement) -->
<input type="file" accept="image/*" capture="environment">Sliders (Range)
<input type="range" min="0" max="10" step="0.5"
oninput="this.nextElementSibling.value = this.value">
<output>5</output>Autocomplete (Vital pour l'UX)
Aide le navigateur (Chrome/Safari) à pré-remplir les champs correctement.
<!-- Login -->
<input type="text" autocomplete="username">
<input type="password" autocomplete="current-password">
<!-- Inscription / Change Password -->
<input type="password" autocomplete="new-password">
<!-- Carte Bancaire -->
<input type="text" autocomplete="cc-number">
<input type="text" autocomplete="cc-exp">
<input type="text" autocomplete="cc-csc">Datalist (L'auto-complétion native)
Un hybride entre un <select> et un <input>. Permet de choisir une valeur prédéfinie OU d'en écrire une nouvelle.
<label>Votre navigateur préféré ?</label>
<input list="browsers" name="browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
<option value="Opera">
<option value="Edge">
</datalist>Hidden (Sécurité)
<!-- Souvent utilisé pour les tokens CSRF ou ID -->
<input type="hidden" name="user_id" value="42">Attributs de base
Le navigateur bloque la soumission et affiche une bulle d'erreur native si ces conditions ne sont pas remplies.
<form>
<!-- Présence & Longueur -->
<input type="text" required minlength="3" maxlength="20">
<!-- Plage numérique -->
<input type="number" min="18" max="99" step="1">
<!-- Format Email ou URL (Validation auto) -->
<input type="email" required>
<button>Envoyer</button>
</form>Puissance du Regex (pattern)
L'attribut title est affiché dans la bulle d'erreur si le pattern échoue.
<!-- Code Postal (5 chiffres exacts) -->
<input type="text" pattern="[0-9]{5}"
title="Veuillez entrer 5 chiffres">
<!-- Username (Lettres, chiffres, tiret, 3-15 chars) -->
<input type="text" pattern="[a-zA-Z0-9-]{3,15}">
<!-- Mot de passe complexe -->
<!-- (?=.*\d) = au moins un chiffre -->
<!-- (?=.*[a-z]) = au moins une minuscule -->
<input type="password"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"
title="8 chars, 1 maj, 1 min, 1 chiffre">Le ProblĂšme de l'UX
Si vous utilisez juste :invalid, le champ sera rouge dÚs le chargement de la page (car il est vide et requis). C'est une mauvaise expérience utilisateur.
Solution Moderne : :user-invalid
Ne cible l'erreur que si l'utilisateur a déjà interagi avec le champ.
/* 1. Ătat par dĂ©faut (Pas d'erreur visible) */
input { border: 1px solid #ccc; }
/* 2. SuccĂšs (Optionnel) */
input:valid { border-color: green; }
/* 3. Erreur (Seulement aprĂšs interaction) */
/* Support: Firefox, Chrome/Edge 119+ */
input:user-invalid {
border-color: red;
background-color: #fff0f0;
animation: shake 0.3s;
}Solution Compatibilité (Le "Hack" Placeholder)
Si vous devez supporter de vieux navigateurs.
/* Cible les inputs invalides qui ne montrent PAS
leur placeholder (donc l'user a tapé qqchose) */
input:not(:placeholder-shown):invalid {
border-color: red;
}
/* Requis : il faut mettre un placeholder,
mĂȘme vide (espace) */
<input required placeholder=" " ...>Quand le HTML ne suffit plus (Custom Logic)
Exemple classique : "Confirmer le mot de passe". HTML ne peut pas comparer deux champs. Il faut utiliser l'API JS.
<input type="password" id="pwd">
<input type="password" id="confirm">
<script>
const pwd = document.getElementById("pwd");
const confirm = document.getElementById("confirm");
function validatePassword() {
if (pwd.value !== confirm.value) {
// 1. Marque le champ comme invalide
// 2. Définit le message de la bulle native
confirm.setCustomValidity("Les mots de passe ne correspondent pas !");
} else {
// IMPORTANT : Remettre Ă vide pour valider le champ
confirm.setCustomValidity("");
}
}
pwd.onchange = validatePassword;
confirm.onkeyup = validatePassword;
</script>Méthodes Clés
checkValidity(): Retournetrue/falseet déclenche l'événement `invalid` si faux.reportValidity(): Affiche la bulle d'erreur native (utile si vous faites un formulaire AJAX).setCustomValidity('msg'): Bloque l'envoi du form tant que le message n'est pas vide.
ArrĂȘtez d'utiliser des div avec z-index: 9999. HTML5 a maintenant une balise native pour les modales, qui gĂšre le focus trap et la touche Echap nativement.
HTML
<dialog id="myModal">
<h2>Confirmation</h2>
<p>Voulez-vous supprimer cet élément ?</p>
<form method="dialog">
<!-- Ce bouton ferme la modal auto sans JS -->
<button value="cancel">Annuler</button>
<button value="confirm">Oui</button>
</form>
</dialog>JavaScript & CSS
const modal = document.getElementById('myModal');
// Ouvre en mode "Modal" (bloque le reste de la page)
modal.showModal();
// Ouvre en mode "Non-modal" (popup flottante)
// modal.show();
modal.close(); // Fermeture manuelle/* Fond sombre derriĂšre la modal */
::backdrop {
background: rgba(0, 0, 0, 0.8);
}Attribut srcset (Résolution)
Laisse le navigateur choisir la meilleure image selon la largeur d'écran (w) ou la densité de pixels (1x, 2x).
<img src="small.jpg"
srcset="small.jpg 500w,
medium.jpg 1000w,
large.jpg 2000w"
sizes="(max-width: 600px) 100vw,
50vw"
alt="Description">Traduction : "Si l'écran fait moins de 600px, l'image occupe 100% de largeur. Sinon 50%. Choisis dans `srcset` le fichier le plus léger pour cette taille."
Balise <picture> (Direction Artistique)
Force le changement d'image (ex: recadrage différent sur mobile) ou de format (WebP).
<picture>
<!-- Format AVIF (Le plus léger) -->
<source srcset="img.avif" type="image/avif">
<!-- Format WebP (Standard moderne) -->
<source srcset="img.webp" type="image/webp">
<!-- Fallback JPG -->
<img src="img.jpg" alt="..." loading="lazy">
</picture>Créer vos propres balises HTML encapsulées (comme React/Vue, mais natif navigateur).
Définition (JS)
class UserCard extends HTMLElement {
constructor() {
super();
// Shadow DOM (CSS isolé)
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = `
<style>h3 { color: blue; }</style>
<div class="card">
<h3><slot name="username">Inconnu</slot></h3>
<p>Role: Admin</p>
</div>
`;
}
}
customElements.define('user-card', UserCard);Utilisation (HTML)
<user-card>
<span slot="username">Alice</span>
</user-card>
<user-card>
<span slot="username">Bob</span>
</user-card>Le CSS défini dans le Shadow DOM n'affecte pas le reste de la page, et le CSS de la page n'affecte pas le composant.
| Méthode | Comportement | Usage |
|---|---|---|
<script src="..."> | Bloque le rendu HTML pendant le tĂ©lĂ©chargement ET l'exĂ©cution. | đ Ă Ă©viter (sauf bas de page). |
async | TĂ©lĂ©charge en parallĂšle, exĂ©cute dĂšs que prĂȘt (pause le HTML). Ordre non garanti. | Analytics, Pubs (scripts indĂ©pendants). |
defer | TĂ©lĂ©charge en parallĂšle, exĂ©cute aprĂšs le chargement du HTML (DOMContentLoaded). | đ Standard moderne pour vos JS app. |
Resource Hints
<!-- Charger une ressource critique (ex: police) en priorité -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- Pré-résoudre un DNS (ex: CDN externe) -->
<link rel="preconnect" href="https://api.monsite.com">Lazy Loading Natif
Ne charge l'image/iframe que quand l'utilisateur scrolle dessus.
<img src="lourd.jpg" loading="lazy" alt="..." width="800" height="600">
<iframe src="..." loading="lazy"></iframe>Attention : Ne jamais mettre loading="lazy" sur l'image tout en haut de la page (LCP), sinon le site paraĂźt lent.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Titre Page</title>
<meta name="description" content="Description SEO">
<!-- Favicons -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<!-- Styles -->
<link rel="stylesheet" href="style.css">
<!-- Scripts (Non-bloquant) -->
<script src="app.js" defer></script>
</head>
<body>
<header>
<nav>
<ul><li><a href="/">Accueil</a></li></ul>
</nav>
</header>
<main>
<h1>Titre Principal (H1)</h1>
<section>
<h2>Section Thématique</h2>
<p>Contenu...</p>
<img src="img.jpg" alt="Description" loading="lazy">
</section>
</main>
<footer>
<p>© 2025 Mon Site</p>
</footer>
</body>
</html>