Cartes, listes et organisation du contenu

Panneau repliable accordéon

Composant FAQ à accordéon avec prise en charge du clavier et déploiement fluide.

HTMLCSSJavaScriptAccessibilité
Après activation de JavaScript, ce cas peut être modifié, exécuté et enregistré. Code source fixe ci-dessous :
<!doctype html>
<html lang="fr" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Panneau repliable accordéon</title>
  <style>
    * { box-sizing: border-box; }
    body {
      min-height: 100vh;
      margin: 0;
      padding: clamp(24px, 7vw, 76px) 20px;
      color: #18302a;
      font-family: "Gill Sans", "Microsoft YaHei", sans-serif;
      background: linear-gradient(135deg, #f4f0d8 0 55%, #d7ead5 55%);
    }
    main { width: min(820px, 100%); margin: auto; }
    header { display: grid; grid-template-columns: 1fr auto; gap: 24px; align-items: end; margin-bottom: 34px; }
    .kicker { color: #d5523f; font-size: 12px; font-weight: 800; letter-spacing: .2em; }
    h1 { margin: 8px 0 0; font: 800 clamp(36px, 8vw, 66px)/1 "STKaiti", "KaiTi", serif; }
    .leaf { width: 80px; height: 80px; border-radius: 80% 10% 80% 10%; background: #2e8169; transform: rotate(10deg); box-shadow: inset 14px 14px rgba(255,255,255,.16); }
    .accordion { border-top: 2px solid #18302a; }
    .item { border-bottom: 1px solid rgba(24, 48, 42, .35); }
    .trigger {
      width: 100%;
      display: grid;
      grid-template-columns: 48px 1fr 34px;
      gap: 12px;
      align-items: center;
      padding: 24px 4px;
      border: 0;
      color: inherit;
      text-align: left;
      font: inherit;
      font-size: 19px;
      font-weight: 750;
      background: transparent;
      cursor: pointer;
    }
    .index { color: #d5523f; font: 700 13px/1 Georgia, serif; }
    .plus { position: relative; width: 28px; height: 28px; border: 1px solid currentColor; border-radius: 50%; transition: transform .25s ease; }
    .plus::before, .plus::after { content: ""; position: absolute; left: 7px; right: 7px; top: 13px; height: 1px; background: currentColor; }
    .plus::after { transform: rotate(90deg); transition: opacity .2s ease; }
    .trigger[aria-expanded="true"] .plus { transform: rotate(90deg); color: #d5523f; }
    .trigger[aria-expanded="true"] .plus::after { opacity: 0; }
    .trigger:focus-visible { outline: 3px solid #d5523f; outline-offset: -3px; }
    .panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .3s ease; }
    .panel.is-open { grid-template-rows: 1fr; }
    .panel-content { overflow: hidden; }
    .panel p { max-width: 650px; margin: 0; padding: 0 48px 24px 64px; color: #4d625c; line-height: 1.8; }
    @media (max-width: 520px) { header { grid-template-columns: 1fr; } .leaf { display: none; } .panel p { padding-left: 4px; } }
    @media (prefers-reduced-motion: reduce) { .panel, .plus { transition: none; } }
  </style>
</head>
<body>
  <main>
    <header>
      <div><span class="kicker">GARDEN NOTES</span><h1>Foire aux questions</h1></div>
      <div class="leaf" aria-hidden="true"></div>
    </header>
    <section class="accordion">
      <div class="item">
        <button id="trigger-1" class="trigger" type="button" aria-expanded="true" aria-controls="panel-1"><span class="index">01</span><span>Comment choisir l’emplacement des plantes d’intérieur ?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-1" class="panel is-open" role="region" aria-labelledby="trigger-1" aria-hidden="false"><div class="panel-content"><p>Observez d’abord l’évolution de la lumière naturelle au cours de la journée. La plupart des plantes à feuillage apprécient une lumière vive mais indirecte, et un emplacement à un ou deux mètres d’une fenêtre orientée au sud constitue souvent un point de départ confortable.</p></div></div>
      </div>
      <div class="item">
        <button id="trigger-2" class="trigger" type="button" aria-expanded="false" aria-controls="panel-2"><span class="index">02</span><span>À quelle fréquence faut-il arroser ?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-2" class="panel" role="region" aria-labelledby="trigger-2" aria-hidden="true"><div class="panel-content"><p>Ne vous fiez pas uniquement au calendrier. Enfoncez un doigt dans la terre sur environ deux centimètres ; arrosez abondamment seulement lorsque la surface est déjà sèche ; en hiver, l’évaporation est plus lente, il faut donc espacer davantage les arrosages.</p></div></div>
      </div>
      <div class="item">
        <button id="trigger-3" class="trigger" type="button" aria-expanded="false" aria-controls="panel-3"><span class="index">03</span><span>Que faire lorsque le bord des feuilles jaunit ?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-3" class="panel" role="region" aria-labelledby="trigger-3" aria-hidden="true"><div class="panel-content"><p>Commencez par vérifier l’eau stagnante, l’air trop sec et l’exposition soudaine au soleil. Coupez les feuilles gravement endommagées et maintenez une bonne aération ; en général, de nouvelles feuilles en meilleure santé apparaissent en une à deux semaines.</p></div></div>
      </div>
      <div class="item">
        <button id="trigger-4" class="trigger" type="button" aria-expanded="false" aria-controls="panel-4"><span class="index">04</span><span>Quand faut-il remplacer le pot de fleurs ?</span><span class="plus" aria-hidden="true"></span></button>
        <div id="panel-4" class="panel" role="region" aria-labelledby="trigger-4" aria-hidden="true"><div class="panel-content"><p>Lorsque les racines sortent par les trous de drainage, que l’eau s’écoule rapidement après l’arrosage, ou que la croissance ralentit nettement, vous pouvez rempoter au printemps dans un nouveau pot de 2 à 4 centimètres de diamètre supérieur.</p></div></div>
      </div>
    </section>
  </main>
  <script>
    const triggers = [...document.querySelectorAll('.trigger')];
    triggers.forEach((trigger, index) => {
      trigger.addEventListener('click', () => {
        const willOpen = trigger.getAttribute('aria-expanded') !== 'true';
        triggers.forEach(item => {
          item.setAttribute('aria-expanded', 'false');
          const panel = document.getElementById(item.getAttribute('aria-controls'));
          panel.classList.remove('is-open');
          panel.setAttribute('aria-hidden', 'true');
        });
        if (willOpen) {
          trigger.setAttribute('aria-expanded', 'true');
          const panel = document.getElementById(trigger.getAttribute('aria-controls'));
          panel.classList.add('is-open');
          panel.setAttribute('aria-hidden', 'false');
        }
      });
      trigger.addEventListener('keydown', event => {
        const direction = event.key === 'ArrowDown' ? 1 : event.key === 'ArrowUp' ? -1 : 0;
        if (direction) {
          event.preventDefault();
          triggers[(index + direction + triggers.length) % triggers.length].focus();
        }
      });
    });
  </script>
</body>
</html>
Commentaires sur la pageVous avez découvert un problème ou avez une suggestion d’amélioration ?
Les fonctions de retour nécessitent l’utilisation de la version en ligne

Veuillez activer JavaScript dans la version en ligne avant de soumettre ; les fonctionnalités locales de l’outil ou du cas ne sont pas affectées.

Aller vers la version en ligne pour envoyer des commentaires