1. HOME
  2. HTML CSS
  3. CSS Gridで作るスムーズなアコーディオンの実装方法
HTML CSS - 2025-08-19

CSS Gridで作るスムーズなアコーディオンの実装方法

この記事では、CSS GridとJavaScriptを使って、スムーズなアコーディオンを実装する方法を解説します。

完成イメージ

  • 初期状態では中身が非表示
  • ボタンをクリックするとスムーズに展開
  • +/− アイコンが切り替わる
  • 閉じたときは元の位置にスクロール

See the Pen Untitled by watanabe (@web-sourcecode) on CodePen.

コードの全体

HTML

<div class="c-accordion js-accordion">
  <div class="c-accordion__content">
    <div class="c-accordion__content__inner">
      <p>dammy text</p>
      <p>dammy text</p>
      <p>dammy text</p>
    </div>
  </div>
  <button class="c-accordion__trigger js-accordion-trigger" type="button">
    button<i class="c-accordion__trigger__icon"></i>
  </button>
</div>

SCSS

.c-accordion {
  &__content {
    display: grid;
    grid-template-rows: 0fr; // 高さゼロで非表示
    opacity: 0;
    transition: .36s grid-template-rows ease;
    &__inner {
      overflow: hidden;
    }
    .is-opened & {
      grid-template-rows: 1fr; // 高さを自動に
      opacity: 1;
    }
  }

  &__trigger {
    position: relative;
    display: block;
    cursor: pointer;
    text-align: center;
    font-weight: 600;
    border-radius: 100vmax;
    color: black;
    border: 2px solid black;
    background-color: #fff;
    width: 300px;
    margin: 20px auto 0;
    font-size: 16px;
    padding: 1em 0;

    &__icon {
      position: absolute;
      top: 0;
      right: 12px;
      width: 44px;
      height: 100%;
      &::before,
      &::after {
        content: '';
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: black;
      }
      &::before {
        width: 18px;
        height: 2px; // 横線
      }
      &::after {
        width: 2px;
        height: 18px; // 縦線
      }
      .is-opened &::after {
        opacity: 0; // 縦線を消して−にする
      }
    }
  }
}

JavaScript

const items = document.querySelectorAll('.js-accordion');

items.forEach(item => {
  const trigger = item.querySelector('.js-accordion-trigger');
  if (trigger) {
    trigger.addEventListener('click', function (e) {
      e.stopPropagation();
      const isActive = item.classList.contains('is-opened');
      item.classList.toggle('is-opened');
      if (isActive) {
        item.scrollIntoView({ behavior: 'instant', block: 'start' });
      }
    });
  }
});

ポイント

  • grid-template-rowsを使った高さアニメーション
  • max-heightより滑らかで、中身の高さが可変でも対応可能。
  • アイコンは::before::afterでCSSだけで描画。
  • 縦線を消すだけで+→−に切り替え。
  • .is-opened クラスの付け外しでCSSの状態を切り替え
  • 閉じるときはscrollIntoViewで元の位置までスクロール

応用例

1つだけ開くアコーディオンにする場合は以下を追加します。

items.forEach(item => {
  const trigger = item.querySelector('.js-accordion-trigger');
  if (trigger) {
    trigger.addEventListener('click', e => {
      e.stopPropagation();
      items.forEach(i => i.classList.remove('is-opened')); // 全部閉じる
      item.classList.add('is-opened'); // クリックしたものだけ開く
    });
  }
});

まとめ

アコーディオンはFAQやメニューに限らず、コンテンツの折りたたみにも応用できます。
CSS Gridを使えば、よりモダンで柔らかな動きを簡単に実現できます。