Créez des accordéons et FAQ accessibles avec details et summary : attribut name pour exclusivité, animations CSS, SEO et 8 patterns réels production.
Pourquoi details et summary ?
Pendant des années, créer un accordéon nécessitait Bootstrap, jQuery, Alpine ou React, des classes .collapse, des attributs data-bs-toggle, des listeners JavaScript et des managers d'état. Tout ça pour un simple bouton qui montre ou cache du contenu.
Les balises HTML <details> et <summary> remplacent ce stack entier par 4 lignes de HTML pur. Pas de JS, pas de framework, accessibilité gratuite, SEO impeccable, performance maximale. En 2026, ces balises sont supportées par 100% des navigateurs modernes et ont gagné de nouvelles capacités majeures (attribut name pour accordéon exclusif, animations CSS natives).
Comparaison rapide
<!-- ❌ Bootstrap 5 — 8+ lignes, 1 dépendance JS -->
<div class="accordion" id="myAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button"
data-bs-toggle="collapse" data-bs-target="#item1">
Question 1
</button>
</h2>
<div id="item1" class="accordion-collapse collapse">
<div class="accordion-body">Réponse 1</div>
</div>
</div>
</div>
<!-- ✅ HTML natif — 4 lignes, 0 JS -->
<details>
<summary>Question 1</summary>
<p>Réponse 1</p>
</details>
Syntaxe et anatomie
Un widget <details> contient toujours un <summary> en premier enfant (l'en-tête cliquable), suivi d'un contenu libre (paragraphes, listes, tableaux, vidéos, autres composants).
Structure de base
<details>
<summary>Cliquez pour développer</summary>
<p>Contenu masqué par défaut. Visible après clic sur summary.</p>
<ul>
<li>Liste autorisée</li>
<li>Tableaux, images, vidéos aussi</li>
</ul>
</details>
Attributs disponibles
| Attribut | Description | Exemple |
|---|---|---|
open |
Ouvre le widget par défaut | <details open> |
name |
Crée un groupe exclusif (radio-like) | <details name="faq"> |
Événement toggle
Lorsqu'un <details> s'ouvre ou se ferme, il déclenche l'événement toggle. Utile pour analytics, lazy-loading de contenu, ou synchronisation d'état.
<details id="terms">
<summary>Conditions générales d'utilisation</summary>
<p>Texte des CGU…</p>
</details>
<script>
document.getElementById('terms').addEventListener('toggle', (e) => {
if (e.target.open) {
console.log('CGU consultées par l\'utilisateur');
// Tracker analytics, débloquer le bouton « accepter », etc.
}
});
</script>
Accordéon exclusif avec name
Depuis fin 2023 (Chrome 120, Safari 17.2, Firefox 117), l'attribut name permet de regrouper plusieurs <details> en accordéon exclusif : ouvrir l'un ferme automatiquement les autres du même groupe. C'est le comportement « FAQ » classique, désormais natif.
Exemple FAQ exclusive
<details name="faq" open>
<summary>Quels sont les délais de livraison ?</summary>
<p>Livraison standard sous 3 à 5 jours ouvrés en France métropolitaine.</p>
</details>
<details name="faq">
<summary>Comment retourner un article ?</summary>
<p>Vous disposez de 30 jours pour retourner votre commande.</p>
</details>
<details name="faq">
<summary>Acceptez-vous les paiements en plusieurs fois ?</summary>
<p>Oui, paiement en 3x ou 4x sans frais via Klarna ou Alma.</p>
</details>
Plusieurs groupes indépendants sur la même page
<!-- Groupe FAQ Livraison -->
<details name="livraison">
<summary>Délais</summary>
<p>…</p>
</details>
<details name="livraison">
<summary>Suivi</summary>
<p>…</p>
</details>
<!-- Groupe FAQ Paiement (indépendant) -->
<details name="paiement">
<summary>Modes acceptés</summary>
<p>…</p>
</details>
<details name="paiement">
<summary>Sécurité</summary>
<p>…</p>
</details>
toggle. L'attribut name rend ce code obsolète.
Styling moderne avec CSS
Par défaut, summary affiche un petit triangle (▸) qui pivote à l'ouverture. Vous pouvez le personnaliser entièrement avec ::marker, ::before/::after et le sélecteur [open].
Reset minimal
/* Supprime le triangle natif */
summary {
list-style: none;
cursor: pointer;
}
summary::-webkit-details-marker { display: none; } /* Safari */
Personnaliser le marqueur avec ::marker
summary::marker {
content: '▸ ';
color: #0d6efd;
font-size: 1.2em;
}
details[open] summary::marker {
content: '▾ ';
}
Icône custom avec ::after — chevron Bootstrap
details {
border: 1px solid #dee2e6;
border-radius: 0.5rem;
margin-bottom: 0.5rem;
background: #fff;
overflow: hidden;
}
summary {
list-style: none;
cursor: pointer;
padding: 1rem 1.25rem;
font-weight: 600;
display: flex;
justify-content: space-between;
align-items: center;
transition: background 0.2s ease;
}
summary:hover { background: #f8f9fa; }
summary::after {
content: '+';
font-size: 1.5rem;
font-weight: 300;
transition: transform 0.3s ease;
}
details[open] summary {
background: #e7f1ff;
color: #084298;
border-bottom: 1px solid #dee2e6;
}
details[open] summary::after {
transform: rotate(45deg); /* + devient × */
}
details > *:not(summary) {
padding: 1rem 1.25rem;
}
Style par état avec :has()
/* Container highlight quand un details est ouvert */
.faq-section:has(details[open]) {
background: #fff8e7;
}
/* Style spécial pour le premier details ouvert */
details[open]:first-of-type {
box-shadow: 0 4px 12px rgba(13, 110, 253, 0.15);
}
Animations natives
Animer l'ouverture d'un <details> a longtemps été le talon d'Achille de cette balise : la transition height: auto n'était pas animable. Trois solutions modernes existent en 2026.
Solution 1 : interpolate-size (Chrome 129+)
:root {
interpolate-size: allow-keywords;
}
details > *:not(summary) {
height: 0;
overflow: hidden;
transition: height 0.3s ease;
}
details[open] > *:not(summary) {
height: auto; /* maintenant animable */
}
Solution 2 : ::details-content (Chrome 131+)
details::details-content {
opacity: 0;
height: 0;
overflow: hidden;
transition: height 0.3s ease, opacity 0.3s ease;
transition-behavior: allow-discrete;
}
details[open]::details-content {
opacity: 1;
height: auto;
}
Solution 3 : grid-template-rows (compatible large)
details {
display: grid;
grid-template-rows: auto 0fr;
transition: grid-template-rows 0.3s ease;
}
details[open] {
grid-template-rows: auto 1fr;
}
details > *:not(summary) {
overflow: hidden;
}
Animation rotation du chevron
summary .chevron {
display: inline-block;
transition: transform 0.3s ease;
}
details[open] summary .chevron {
transform: rotate(180deg);
}
<details>
<summary>
Plus de détails
<span class="chevron">▼</span>
</summary>
<p>Contenu</p>
</details>
prefers-reduced-motion doit être respecté. Englobez vos transitions dans une media query : @media (prefers-reduced-motion: no-preference) { ... }.
Accessibilité ARIA
<details> et <summary> apportent l'accessibilité gratuitement : focus clavier, rôle group implicite sur details, état aria-expanded géré par le navigateur, annonce vocale par les lecteurs d'écran (NVDA, JAWS, VoiceOver).
Améliorer le contexte avec ARIA
<section aria-labelledby="faq-title">
<h2 id="faq-title">Questions fréquentes</h2>
<details name="faq">
<summary>Comment créer un compte ?</summary>
<p>Cliquez sur « Inscription » en haut à droite…</p>
</details>
<details name="faq">
<summary>Comment réinitialiser mon mot de passe ?</summary>
<p>Sur la page de connexion, cliquez sur « Mot de passe oublié »…</p>
</details>
</section>
Indicateur de focus visible
summary:focus-visible {
outline: 3px solid #0d6efd;
outline-offset: 2px;
border-radius: 0.25rem;
}
Test au lecteur d'écran
- NVDA : « Question, bouton, réduit » → après clic → « Question, bouton, développé »
- VoiceOver : « Bouton de divulgation, fermé » → « Bouton de divulgation, ouvert »
- Focus clavier : Tab arrive sur summary, Espace ou Entrée bascule l'état
FAQ, SEO et Schema.org
Google et Bing indexent intégralement le contenu replié de <details> depuis 2020. Combinez avec un balisage Schema.org FAQPage pour obtenir des rich snippets dans les résultats de recherche.
FAQ avec Schema.org JSON-LD
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "Quels sont les délais de livraison ?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Livraison standard sous 3 à 5 jours ouvrés en France métropolitaine."
}
},
{
"@type": "Question",
"name": "Comment retourner un article ?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Vous disposez de 30 jours pour retourner votre commande."
}
}
]
}
</script>
<section>
<h2>FAQ Livraison</h2>
<details name="faq-livraison">
<summary>Quels sont les délais de livraison ?</summary>
<p>Livraison standard sous 3 à 5 jours ouvrés en France métropolitaine.</p>
</details>
<details name="faq-livraison">
<summary>Comment retourner un article ?</summary>
<p>Vous disposez de 30 jours pour retourner votre commande.</p>
</details>
</section>
Microdata inline (alternative)
<div itemscope itemtype="https://schema.org/FAQPage">
<details itemprop="mainEntity" itemscope itemtype="https://schema.org/Question">
<summary itemprop="name">Quels sont les délais de livraison ?</summary>
<div itemprop="acceptedAnswer" itemscope itemtype="https://schema.org/Answer">
<div itemprop="text">Livraison sous 3 à 5 jours ouvrés.</div>
</div>
</details>
</div>
8 patterns réels
1. Notice cookies repliable
<details class="alert alert-info">
<summary>Pourquoi nous utilisons des cookies</summary>
<p>Les cookies nous aident à mesurer l'audience et personnaliser votre expérience.</p>
</details>
2. Métadonnées d'un article
<details>
<summary>Métadonnées de cet article</summary>
<ul>
<li>Auteur : Saïd Mezgani</li>
<li>Publié le : 7 mai 2026</li>
<li>Temps de lecture : 8 min</li>
</ul>
</details>
3. Code source replié
<details>
<summary>Voir le code complet</summary>
<pre><code>function hello() {
console.log('Hello world');
}</code></pre>
</details>
4. Filtre de recherche avancée
<details>
<summary>Filtres avancés</summary>
<div class="row g-2">
<div class="col-md-4">
<label>Prix min <input type="number" class="form-control"></label>
</div>
<div class="col-md-4">
<label>Prix max <input type="number" class="form-control"></label>
</div>
<div class="col-md-4">
<label>Catégorie <select class="form-select">...</select></label>
</div>
</div>
</details>
5. Spoiler tag pour blog
<details class="spoiler">
<summary>⚠️ Spoiler — cliquez pour révéler</summary>
<p>Le héros meurt à la fin du chapitre 12.</p>
</details>
6. Menu mobile sans JS
<details class="mobile-menu d-md-none">
<summary>☰ Menu</summary>
<nav>
<a href="/">Accueil</a>
<a href="/blog">Blog</a>
<a href="/contact">Contact</a>
</nav>
</details>
7. Section help contextuelle
<label for="iban">
IBAN
<details class="d-inline help-tip">
<summary>?</summary>
<p class="small text-muted">Format : 27 caractères, ex. FR76 1234…</p>
</details>
</label>
<input id="iban" type="text" class="form-control">
8. Liste imbriquée pour documentation
<details name="docs" open>
<summary>Installation</summary>
<details name="docs-install">
<summary>Sur Linux</summary>
<pre><code>apt install nodejs</code></pre>
</details>
<details name="docs-install">
<summary>Sur macOS</summary>
<pre><code>brew install node</code></pre>
</details>
</details>
<details name="docs">
<summary>Configuration</summary>
<p>…</p>
</details>
Conclusion
<details> et <summary> sont parmi les balises HTML les plus sous-utilisées. Elles remplacent des centaines de lignes de JavaScript par 4 lignes de HTML, gèrent l'accessibilité automatiquement, indexent parfaitement le contenu pour le SEO et bénéficient en 2026 de nouveautés majeures : attribut name pour accordéons exclusifs, animations natives via ::details-content et interpolate-size.
Adoptez-les dès maintenant pour vos FAQ, vos sections optionnelles, vos menus mobiles, vos help-tips, vos spoilers blog. Combinez avec Schema.org FAQPage pour gagner des rich snippets Google. Vous éliminez Bootstrap accordion, jQuery slideToggle, et un pan entier de votre dépendance JavaScript.
<details>+<summary>= accordéon natif sans JSopenouvre par défaut,namecrée un groupe exclusif- Événement
togglepour analytics ou lazy-load - Personnaliser avec
::markerou::after - Animer avec
::details-contentouinterpolate-size - Accessibilité gratuite (focus, ARIA, lecteurs d'écran)
- Indexé par Google même replié — combiner avec FAQPage Schema.org
- Cas réels : FAQ, menus mobile, help-tips, spoilers, métadonnées