卡片、列表与内容组织

手风琴折叠面板

支持键盘操作与平滑展开的常见问题折叠组件。

HTMLCSSJavaScript无障碍
启用 JavaScript 后可编辑、运行和保存此案例。固定源码如下:
<!doctype html>
<html lang="zh-Hans" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>手风琴折叠面板</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>常见问题</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>如何给室内植物选择位置?</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>先观察一天中的自然光变化。大多数观叶植物喜欢明亮但不直射的散射光,距离朝南窗户一到两米通常是舒服的起点。</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>多久浇一次水比较合适?</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>不要只看日历。将手指伸入土壤约两厘米,表层已经干燥再浇透;冬季蒸发较慢,应适当延长间隔。</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>叶片边缘变黄怎么办?</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>先排查积水、空气过干和突然暴晒。剪去严重受损的叶片,并保持通风,通常一至两周就能看到新叶恢复健康。</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>什么时候需要更换花盆?</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>根系从排水孔伸出、浇水后很快流走,或生长明显停滞时,可以在春季换到直径大两到四厘米的新盆。</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>
页面反馈发现问题或有改进建议?
反馈功能需要在在线版使用

请在在线版启用 JavaScript 后提交;当前工具或案例的本地功能不受影响。

前往在线版反馈