RYX

Blog

Web制作 — 2026.02.05

【コピペOK】モダンなアコーディオンメニュー4選!

← ブログ一覧に戻る

はじめに

多くのWebサイトで使用されているアコーディオンメニューはシンプルそうに見えて、実は適切な実装方法を知らないと、思わぬバグやスマートフォンでの表示崩れに悩まされることになります。

この記事では、モダンなアコーディオンメニューの実装方法を、5つのパターンで詳しく解説します。

全てのコードはコピー&ペーストですぐに使え、さらにカスタマイズのポイントも押さえているので、ポートフォリオやクライアントワークですぐに活用できますので、ぜひ参考にしてみてください。

1. シンプルなフェードインアコーディオン

この実装の特徴は以下の通りです。

  • CSSアニメーションによるスムーズな動き
  • JavaScriptでの適切なアクセシビリティ対応
  • レスポンシブデザインへの対応
  • 軽量で高パフォーマンス

HTML

<div class="accordion">
  <button class="accordion-trigger" aria-expanded="false">
    <span>アコーディオンの見出し</span>
    <svg class="accordion-icon" width="24" height="24" viewBox="0 0 24 24">
      <path d="M6 9l6 6 6-6"></path>
    </svg>
  </button>
  <div class="accordion-content">
    <div class="accordion-body">
      アコーディオンの内容がここに表示されます。
    </div>
  </div>
</div>

CSS

.accordion {
  width: 100%;
  max-width: 600px;
  margin: 0 auto;
  border: 1px solid #e2e8f0;
  border-radius: 0.5rem;
  overflow: hidden;
}

.accordion-trigger { width: 100%; padding: 1rem; display: flex; justify-content: space-between; align-items: center; background: white; border: none; cursor: pointer; transition: background-color 0.2s; }

.accordion-trigger:hover { background-color: #f8fafc; }

.accordion-icon { transition: transform 0.2s; }

.accordion-trigger[aria-expanded="true"] .accordion-icon { transform: rotate(180deg); }

.accordion-content { max-height: 0; overflow: hidden; transition: max-height 0.2s ease-in-out; }

.accordion-content.open { max-height: 200px; }

.accordion-body { padding: 1rem; }

JavaScript

document.querySelectorAll('.accordion-trigger').forEach(button => {
  button.addEventListener('click', () => {
    const content = button.nextElementSibling;
    const isExpanded = button.getAttribute('aria-expanded') === 'true';

button.setAttribute(&apos;aria-expanded&apos;, !isExpanded);
content.classList.toggle(&apos;open&apos;);

}); });

コードの解説

  1. HTML構造
  • button要素を使用することで、キーボード操作に対応
  • aria-expanded属性でアクセシビリティを確保
  1. CSSの特徴
  • max-heightを使用したスムーズなアニメーション
  • transitionプロパティによる自然な動き
  • レスポンシブ対応のためのmax-width設定
  1. JavaScriptのポイント
  • 純粋なJavaScript(バニラJS)での実装
  • WAI-ARIAに準拠したアクセシビリティ対応
  • イベントデリゲーションによる効率的な処理

2.複数パネル対応のアコーディオングループ

HTML

<div class="faq-accordion">
  <div class="panel">
    <button class="panel-header">
      Q1. アコーディオンメニューとは何ですか?
      <span class="icon">+</span>
    </button>
    <div class="panel-content">
      <div class="panel-body">
        <p>
          アコーディオンメニューは、クリックすると内容が展開・収納される
          UIコンポーネントです。限られたスペースで多くの情報を
          整理して表示できる便利な機能です。
        </p>
      </div>
    </div>
  </div>

<div class="panel"> <button class="panel-header"> Q2. メリットを教えてください <span class="icon">+</span> </button> <div class="panel-content"> <div class="panel-body"> <p> 主なメリットは以下の3つです:<br> ・画面スペースを効率的に使える<br> ・情報を整理して表示できる<br> ・ユーザーが必要な情報を選んで閲覧できる </p> </div> </div> </div> </div>

CSS

.faq-accordion {
  max-width: 800px;
  margin: 0 auto;
}

.panel { border: 1px solid #ddd; border-radius: 8px; margin-bottom: 10px; }

.panel-header { width: 100%; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; background: #fff; border: none; cursor: pointer; font-size: 16px; text-align: left; }

.panel-header:hover { background: #f8f9fa; }

.icon { font-size: 20px; transition: transform 0.3s; }

.panel-header.active .icon { transform: rotate(45deg); }

.panel-content { height: 0; overflow: hidden; transition: height 0.3s ease-out; }

.panel-body { padding: 20px; border-top: 1px solid #eee; }

JavaScript

document.querySelectorAll('.panel-header').forEach(trigger => {
  trigger.addEventListener('click', function() {
    const content = this.nextElementSibling;
    const panel = this.closest('.panel');

// 他のパネルを閉じる
document.querySelectorAll(&apos;.panel-header&apos;).forEach(otherTrigger =&gt; {
  if (otherTrigger !== this) {
    otherTrigger.classList.remove(&apos;active&apos;);
    const otherContent = otherTrigger.nextElementSibling;
    otherContent.style.height = &apos;0&apos;;
  }
});

// クリックしたパネルの処理
this.classList.toggle(&apos;active&apos;);

if (this.classList.contains(&apos;active&apos;)) {
  // 開く時は中身の高さを計算
  const body = content.querySelector(&apos;.panel-body&apos;);
  content.style.height = body.offsetHeight + &apos;px&apos;;
} else {
  // 閉じる時は0
  content.style.height = &apos;0&apos;;
}

}); });

実装のポイント

  1. 構造のポイント
  • 内容を包む要素を2重にして、パディングの変化を防ぐ
  • アイコンは疑似要素ではなくspanで実装し、回転を管理しやすく
  • ボタン要素を使用してキーボード操作に対応
  1. CSSのポイント
  • heightプロパティでアニメーション(max-heightより自然な動き)
  • パディングは固定値で持ち、高さだけを変化
  • 余計なアニメーションを入れすぎない
  1. JavaScriptのポイント
  • 実際の高さを計算して設定
  • パネルごとに適切な高さでアニメーション
  • 他のパネルを確実に閉じる制御

3.モダンなアニメーション付きアコーディオン

HTML

<div class="modern-accordion">
  <div class="accordion-item">
    <button class="accordion-header">
      <div class="header-content">
        <span class="label">Section 1</span>
        <p class="title">基本的な使い方</p>
      </div>
      <div class="icon-wrap">
        <span class="line"></span>
        <span class="line"></span>
      </div>
    </button>
    <div class="accordion-body">
      <div class="body-content">
        <p>基本的な使い方について説明します。まずは基礎から始めて、徐々に応用的な使い方を学んでいきましょう。</p>
        <ul>
          <li>手順1: インストール方法</li>
          <li>手順2: 初期設定</li>
          <li>手順3: 基本操作</li>
        </ul>
      </div>
    </div>
  </div>

<div class="accordion-item"> <button class="accordion-header"> <div class="header-content"> <span class="label">Section 2</span> <p class="title">カスタマイズ</p> </div> <div class="icon-wrap"> <span class="line"></span> <span class="line"></span> </div> </button> <div class="accordion-body"> <div class="body-content"> <p>見た目や動きをカスタマイズする方法を解説します。CSSとJavaScriptを使って様々なアレンジが可能です。</p> </div> </div> </div> </div>

CSS

.modern-accordion {
  max-width: 800px;
  margin: 0 auto;
  --primary-color: #2563eb;
  --text-color: #1f2937;
  --border-color: #e5e7eb;
}

.accordion-item { border: 1px solid var(--border-color); border-radius: 12px; margin-bottom: 16px; overflow: hidden; background: white; }

.accordion-header { width: 100%; padding: 24px; display: flex; justify-content: space-between; align-items: center; background: none; border: none; cursor: pointer; transition: background-color 0.2s; }

.accordion-header:hover { background-color: #f8fafc; }

.header-content { text-align: left; }

.label { font-size: 14px; color: var(--primary-color); margin-bottom: 4px; display: block; }

.title { font-size: 18px; color: var(--text-color); margin: 0; font-weight: 500; }

.icon-wrap { position: relative; width: 24px; height: 24px; }

.line { position: absolute; top: 50%; left: 0; width: 100%; height: 2px; background-color: var(--text-color); transition: transform 0.3s ease; }

.line:first-child { transform: translateY(-50%); }

.line:last-child { transform: translateY(-50%) rotate(90deg); }

.accordion-header.active .line:last-child { transform: translateY(-50%) rotate(0); }

.accordion-body { height: 0; overflow: hidden; transition: height 0.3s ease-out; }

.body-content { padding: 0 24px 24px; }

/* アニメーション用の状態 */ .accordion-body.entering { height: var(--content-height); }

.accordion-body.leaving { height: 0; }

JavaScript

document.querySelectorAll('.accordion-header').forEach(header => {
  header.addEventListener('click', function() {
    const body = this.nextElementSibling;
    const content = body.querySelector('.body-content');
    const isExpanding = !this.classList.contains('active');

// 他のアコーディオンを閉じる
document.querySelectorAll(&apos;.accordion-header&apos;).forEach(otherHeader =&gt; {
  if (otherHeader !== this &amp;&amp; otherHeader.classList.contains(&apos;active&apos;)) {
    otherHeader.classList.remove(&apos;active&apos;);
    const otherBody = otherHeader.nextElementSibling;
    closeAccordion(otherBody);
  }
});

// クリックされたアコーディオンの処理
this.classList.toggle(&apos;active&apos;);

if (isExpanding) {
  // 開く処理
  const height = content.offsetHeight;
  body.style.height = `${height}px`;
} else {
  // 閉じる処理
  closeAccordion(body);
}

}); });

function closeAccordion(element) { // 高さを0にして閉じる element.style.height = '0'; }

// ウィンドウリサイズ時の高さ調整 window.addEventListener('resize', () => { document.querySelectorAll('.accordion-header.active').forEach(header => { const body = header.nextElementSibling; const content = body.querySelector('.body-content'); body.style.height = ${content.offsetHeight}px; }); });

ポイント解説

  1. デザインの工夫
  • CSS変数で色を管理し、カスタマイズを容易に
  • 十分な余白でコンテンツの可読性を向上
  • ホバー時の視覚的フィードバック
  1. アニメーションの特徴
  • プラス/マイナスアイコンの滑らかな変形
  • コンテンツの高さを正確に計算
  • リサイズ時の高さ自動調整
  1. 保守性の向上
  • アニメーションのタイミングを変数で管理
  • リサイズ対応で様々な画面サイズに対応

4.タブ切り替え連動アコーディオン

この実装の特徴は以下の通りです。

  • タブ切り替えと連動した表示制御
  • スムーズな開閉アニメーション
  • コンテンツの高さに応じた自動調整

HTML

<div class="tab-accordion">
  <!-- タブメニュー -->
  <div class="tab-nav">
    <button class="tab-button active" data-target="tab1">タブ1</button>
    <button class="tab-button" data-target="tab2">タブ2</button>
    <button class="tab-button" data-target="tab3">タブ3</button>
  </div>

<!-- アコーディオン --> <div class="tab-content active" id="tab1"> <button class="tab-trigger" aria-expanded="false"> <span>アコーディオン1</span> <svg class="tab-icon" width="24" height="24" viewBox="0 0 24 24"> <path d="M6 9l6 6 6-6"></path> </svg> </button> <div class="accordion-content"> <div class="accordion-body"> タブ1のコンテンツがここに入ります。 アコーディオンの開閉と連動して表示されます。 </div> </div> </div>

<div class="tab-content" id="tab2"> <button class="tab-trigger" aria-expanded="false"> <span>アコーディオン2</span> <svg class="tab-icon" width="24" height="24" viewBox="0 0 24 24"> <path d="M6 9l6 6 6-6"></path> </svg> </button> <div class="accordion-content"> <div class="accordion-body"> タブ2のコンテンツです。 別のタブに切り替えると自動的に閉じます。 </div> </div> </div>

<div class="tab-content" id="tab3"> <button class="tab-trigger" aria-expanded="false"> <span>アコーディオン3</span> <svg class="tab-icon" width="24" height="24" viewBox="0 0 24 24"> <path d="M6 9l6 6 6-6"></path> </svg> </button> <div class="accordion-content"> <div class="accordion-body"> タブ3のコンテンツです。 各タブで独立してアコーディオンが動作します。 </div> </div> </div> </div>

CSS

.tab-accordion {
  width: 100%;
  max-width: 800px;
  margin: 0 auto;
}

.tab-nav { display: flex; gap: 1rem; margin-bottom: 1rem; }

.tab-button { padding: 0.75rem 1.5rem; background: #f1f5f9; border: none; border-radius: 0.5rem; cursor: pointer; transition: background-color 0.2s; }

.tab-button:hover { background: #e2e8f0; }

.tab-button.active { background: #3b82f6; color: white; }

.tab-content { display: none; border: 1px solid #e2e8f0; border-radius: 0.5rem; overflow: hidden; }

.tab-content.active { display: block; }

.tab-trigger { width: 100%; padding: 1rem; display: flex; justify-content: space-between; align-items: center; background: white; border: none; cursor: pointer; }

.tab-icon { transition: transform 0.2s; }

.tab-trigger[aria-expanded="true"] .tab-icon { transform: rotate(180deg); }

.accordion-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-out; }

.accordion-content.open { max-height: 200px; }

.accordion-body { padding: 1rem; border-top: 1px solid #e2e8f0; }

JavaScript

// タブ切り替えの処理
document.querySelectorAll('.tab-button').forEach(button => {
  button.addEventListener('click', () => {
    // アクティブなタブを切り替え
    document.querySelectorAll('.tab-button').forEach(btn => {
      btn.classList.remove('active');
    });
    button.classList.add('active');

// タブコンテンツを切り替え
document.querySelectorAll(&apos;.tab-content&apos;).forEach(content =&gt; {
  content.classList.remove(&apos;active&apos;);
});
const targetId = button.dataset.target;
document.getElementById(targetId).classList.add(&apos;active&apos;);

// アコーディオンを全て閉じる
document.querySelectorAll(&apos;.tab-trigger&apos;).forEach(trigger =&gt; {
  trigger.setAttribute(&apos;aria-expanded&apos;, &apos;false&apos;);
  trigger.nextElementSibling.classList.remove(&apos;open&apos;);
});

}); });

// アコーディオンの処理 document.querySelectorAll('.tab-trigger').forEach(trigger => { trigger.addEventListener('click', () => { const content = trigger.nextElementSibling; const isExpanded = trigger.getAttribute('aria-expanded') === 'true';

trigger.setAttribute(&apos;aria-expanded&apos;, !isExpanded);
content.classList.toggle(&apos;open&apos;);

}); });

コードの解説

  1. HTML構造
  • タブメニューとアコーディオンを組み合わせた階層構造
  • data-target属性でタブとコンテンツを紐付け
  • アクセシビリティを考慮した属性設定
  1. CSSの特徴
  • flexboxを使用したタブレイアウト
  • 状態に応じた表示/非表示の切り替え
  • スムーズな開閉アニメーション
  1. JavaScriptのポイント
  • タブ切り替え時のアコーディオンリセット
  • 独立した状態管理による安定した動作
  • 複数の要素を連動させた制御

さいごに

この記事では、実践的なアコーディオンメニューの実装について4つのパターンを解説しました。

それぞれのコードはコピー&ペーストですぐに使え、カスタマイズも容易な設計になっています。実際のプロジェクトでもすぐに活用できます。

また、アクセシビリティにも配慮した実装を心がけていますので、より多くのユーザーに使いやすいUIを提供できます。

ぜひ参考にしてみてください。

Contact

Shopifyのご相談はこちら

構築・移行・改修・運用改善まで。状況を伺って最短ルートをご提案します。

相談・見積もり無料 / 24時間以内に返信

相談する