Comment créer un accordéon sans JavaScript : la méthode moderne avec summary et details

On cherche souvent la même chose : réduire le bruit visuel, garder la page lisible et laisser le lecteur déplier ce qui l’intéresse. En 2026, on obtient ce résultat proprement avec <details> et <summary>. Le navigateur fournit un widget natif. Ainsi, on évite les dépendances JavaScript, on simplifie le DOM et on améliore la robustesse.

Pourquoi ce pattern est la meilleure base en 2026

On gagne immédiatement sur quatre axes.

  1. Performance réelle
    On supprime l’initialisation JS, les gestionnaires d’évènements et les recalculs inutiles. Le widget se comporte nativement.
  2. Sémantique HTML correcte
    On décrit une “divulgation” de contenu, pas une mise en scène. La spec définit clairement open et le comportement attendu.
  3. Accessibilité native
    Le navigateur gère l’état ouvert/fermé et il le communique aux technologies d’assistance dans la plupart des cas. Cela réduit les erreurs d’implémentation « maison ».
  4. Maintenance facile
    On lit le HTML en une seconde. Ensuite, on itère avec CSS. Enfin, on n’ajoute du JS que si la valeur est claire.

Le modèle minimal, propre et “copier-coller”

On commence par la version la plus stable.

<details>
  <summary>Lire la suite</summary>
  <p>
    Ce contenu reste masqué par défaut. On l’affiche en cliquant sur le résumé.
  </p>
</details>
Code language: HTML, XML (xml)
  • Quand open est absent, on n’affiche que le <summary>.
  • Quand open est présent, on affiche aussi le contenu.
<details open>
  <summary>Replier</summary>
  <p>Le contenu s’affiche dès le chargement.</p>
</details>
Code language: HTML, XML (xml)

Le composant “production” : style moderne, focus visible, sans hack

On veut généralement trois choses : un résumé cliquable clair, un indicateur d’état et un focus clavier impeccable. On garde le HTML simple.

HTML

<details class="disclosure">
  <summary class="disclosure__summary">
    <span class="disclosure__title">Lire la suite</span>
    <span class="disclosure__chevron" aria-hidden="true"></span>
  </summary>

  <div class="disclosure__body">
    <p>
      On place ici un complément d’explications, une FAQ, ou une section “méthode”.
      Le navigateur gère l’ouverture et la fermeture.
    </p>
  </div>
</details>
Code language: HTML, XML (xml)

CSS

.disclosure {
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 14px;
  padding: 0.9rem 1rem;
}

.disclosure__summary {
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

/* On neutralise le marker natif, puis on crée un chevron. */
.disclosure__summary::-webkit-details-marker {
  display: none;
}

.disclosure__summary::marker {
  content: "";
}

.disclosure__title {
  font-weight: 600;
  line-height: 1.2;
}

.disclosure__chevron {
  inline-size: 0.75rem;
  block-size: 0.75rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform 180ms ease;
}

.disclosure[open] .disclosure__chevron {
  transform: rotate(-135deg);
}

.disclosure__summary:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

.disclosure__body {
  margin-top: 0.85rem;
}
Code language: CSS (css)

On peut styler le triangle via ::marker car <summary> se comporte comme un item de liste, comme le détaille web.dev.

Accordéon exclusif sans JavaScript

Quand on publie une FAQ, on veut souvent “un seul ouvert à la fois”. On peut le faire nativement avec l’attribut name sur <details>. Chrome for Developers et MDN décrivent ce comportement d’accordéon exclusif :

<details class="disclosure" name="faq">
  <summary class="disclosure__summary">
    <span class="disclosure__title">Question A</span>
    <span class="disclosure__chevron" aria-hidden="true"></span>
  </summary>
  <div class="disclosure__body">
    <p>Réponse A.</p>
  </div>
</details>

<details class="disclosure" name="faq">
  <summary class="disclosure__summary">
    <span class="disclosure__title">Question B</span>
    <span class="disclosure__chevron" aria-hidden="true"></span>
  </summary>
  <div class="disclosure__body">
    <p>Réponse B.</p>
  </div>
</details>
Code language: HTML, XML (xml)

Ensuite, quand on ouvre “Question B”, le navigateur replie “Question A” automatiquement.

Pour la compatibilité, on vérifie l’état réel via Can I use (notamment Safari et Firefox selon versions)

Animations modernes, sans wrapper inutile, avec ::details-content

Longtemps, animer proprement le contenu demandait un conteneur. Désormais, ::details-content cible la partie extensible sans toucher au <summary>. MDN Web Docs documente le pseudo-élément et propose un exemple de transition.

Objectif : une ouverture “douce” sans casser la performance

On évite les animations “height: auto” instables. Ensuite, on privilégie l’opacité et un léger déplacement. Enfin, on respecte prefers-reduced-motion.

@supports selector(details::details-content) {
  details::details-content {
    opacity: 0;
    transform: translateY(-0.25rem);
    content-visibility: hidden;
  }

  details[open]::details-content {
    opacity: 1;
    transform: translateY(0);
    content-visibility: visible;
  }

  @supports (transition-behavior: allow-discrete) {
    details::details-content {
      transition-property: opacity, transform, content-visibility;
      transition-duration: 180ms;
      transition-timing-function: ease;
      transition-behavior: allow-discrete;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    details::details-content {
      transition: none;
      transform: none;
    }
  }
}
Code language: CSS (css)

transition-behavior encadre le démarrage des transitions sur des propriétés discrètes.

Améliorations JavaScript optionnelles et mesurées

On n’ajoute du JS que si l’expérience le justifie. Deux cas reviennent souvent.

Cas 1 : fermer au clic extérieur

C’est un choix UX, pas une obligation.

<script>
document.addEventListener('click', (event) => {
  const openDetails = document.querySelectorAll('details[open]');
  for (const details of openDetails) {
    if (!details.contains(event.target)) {
      details.removeAttribute('open');
    }
  }
});
</script>
Code language: HTML, XML (xml)

Cette version reste légère. Elle évite jQuery. Elle coûte un unique listener.

Cas 2 : analytics d’ouverture

On écoute toggle. On envoie un évènement de suivi.

<script>
document.addEventListener('toggle', (event) => {
  const details = event.target;
  if (!(details instanceof HTMLDetailsElement)) return;

  // Exemple minimal : on remplace par l’outil analytics réel.
  const isOpen = details.open;
  const label = details.querySelector('summary')?.textContent?.trim() ?? 'details';
  console.debug('details_toggle', { isOpen, label });
}, true);
</script>
Code language: HTML, XML (xml)

L’API HTMLDetailsElement.open reflète l’attribut open.

SEO et intentions de recherche : faire mieux qu’un “Lire la suite”

On obtient un vrai gain SEO quand on structure le contenu selon l’intention du lecteur.

Ce qui fonctionne bien

  • On transforme le <summary> en mini-question.
  • Ensuite, on place une réponse directe en première phrase.
  • Puis, on développe avec des exemples, des critères, des pièges.

Ce format matche une intention “comment faire” et une intention “comparaison”. De plus, le contenu reste indexable, car il existe dans le HTML. Plusieurs ressources comme CSS Tricks soulignent l’usage fréquent des FAQ.

Exemples de <summary> orientés requêtes :

  • “Comment créer un accordéon sans JavaScript ?”
  • “Quelle différence entre <details> et un toggle JS ?”
  • “Comment styliser l’icône de <summary> ?”

Pièges courants et solutions

1. Le résumé trop vague

“Cliquez ici” n’aide ni le lecteur ni le moteur. On préfère une phrase descriptive.

2. Le marker stylé de manière fragile

On cible ::marker quand possible. Ensuite, on masque ::-webkit-details-marker pour WebKit. web.dev et CSS-Tricks couvrent ces approches.

3. Les animations agressives

On évite les animations de hauteur complexes. Ensuite, on privilégie opacity et translation. Enfin, on respecte le “reduced motion”. MDN Web Docs fournit une base avec ::details-content.

Intégration WordPress : sans dette technique

On peut intégrer ce pattern sans plugin.

  • On colle le HTML dans un bloc “HTML personnalisé”.
  • Ensuite, on place le CSS dans “CSS additionnel”vou un fichier de thème.
  • Enfin, on ajoute le JS uniquement si un besoin existe.

On gagne une implémentation plus propre qu’un toggle jQuery. On réduit aussi le risque de conflit avec d’autres scripts.

Conclusion : la référence pratique

En 2026, on construit un “show/hide” durable avec trois couches.

  1. HTML natif (<details> / <summary>) pour la structure et le comportement. (MDN Web Docs)
  2. CSS moderne pour l’apparence, le focus et le marker. (web.dev)
  3. JS optionnel uniquement pour des besoins produit mesurables.

Ce choix maximise la performance, réduit la complexité et améliore la maintenabilité.

Votre site mérite performance et fiabilité. Grâce à mon expérience, je vous aide à optimiser WordPress/WooCommerce pour des résultats visibles.

Voyons comment améliorer votre site »

Matt

Développeur certifié WordPress & WooCommerce chez Codeable, administrateur système et enseignant-chercheur, je mets mon expertise au service de vos projets web.

Ma priorité : des sites performants, fiables et sécurisés, pensés pour offrir la meilleure expérience utilisateur. J’accompagne chaque client avec écoute et pédagogie, pour transformer vos idées en solutions concrètes et durables.

Profitez de solutions WordPress et WooCommerce sur-mesure, pensées pour optimiser durablement votre site.
Explorez les leviers pour booster l’impact de votre site web.

Opinions