Cards, Lists, and Content Organization

Accordion Panel

Supports keyboard operation and smooth expanding FAQ accordion component.

HTMLCSSJavaScriptAccessibility
With JavaScript enabled, you can edit, run, and save this example. The fixed source is as follows:
<!doctype html>
<html lang="en" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Accordion Panel</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>FAQ</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>How do you choose a spot for indoor plants?</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>First observe how natural light changes throughout the day. Most foliage plants prefer bright, indirect light, and a spot one to two meters from a south-facing window is usually a comfortable starting point.</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>How often should it be watered?</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>Don’t just look at the calendar. Insert your finger about two centimeters into the soil; water thoroughly only after the surface has dried. In winter, evaporation is slower, so extend the interval appropriately.</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>What should I do if the leaf edges turn yellow?</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>First check for standing water, overly dry air, and sudden exposure to direct sun. Trim severely damaged leaves and keep the plant well ventilated; new leaves usually recover within one to two weeks.</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>When do you need to repot the plant?</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>When roots emerge from the drainage holes, water runs through too quickly, or growth slows markedly, repot in spring into a new pot 2 to 4 cm larger in diameter.</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>
Page feedbackFound an issue or have suggestions for improvement?
Feedback features need to be used in the online version

Please enable JavaScript in the online version before submitting; local features of the current tool or example are not affected.

Go to the online version for feedback