サイトでよく見かけるアコーディオンメニュー。クリックすると項目が開閉して、見たい情報だけをスッキリ表示できる便利なUIです。
「JavaScriptが必要なのでは?」と思われがちですが、実はCSSだけでも簡単に作れます。
この記事では、CSSだけで動くシンプルなアコーディオンの作り方を、サンプルコードつきでわかりやすく紹介します。
アコーディオンUIとは?基本を理解しよう
アコーディオンの基本概念
アコーディオンとは
アコーディオンUI クリックやタップで項目が上下にスライドして開いたり閉じたりする仕組みです。楽器のアコーディオンが蛇腹のように伸び縮みすることから、この名前がつけられました。
身近な例で理解する
日常生活での例
- 本の目次:見たい章だけ開く
- 引き出し:必要な時だけ引き出す
- 折りたたみ傘:使うときだけ広げる
Webサイトでの活用場面
- FAQ(よくある質問)
- 商品の詳細説明
- カテゴリー別のメニュー
- モバイル向けナビゲーション
なぜアコーディオンが重要なの?
ユーザビリティの向上
- 画面スペースの有効活用
- 情報の整理と見やすさ
- ユーザーの認知負荷軽減
- モバイルフレンドリーなデザイン
SEOとアクセシビリティ
- コンテンツの構造化
- 階層的な情報提示
- スクリーンリーダー対応
- 検索エンジンでの評価向上
アコーディオンの種類と特徴
基本的なパターン
単一開閉型
- 1つずつしか開けない
- 情報の重複を防ぐ
- 画面をスッキリ保てる
複数開閉型
- 複数項目を同時に開ける
- 比較検討しやすい
- ユーザーの自由度が高い
動作の違い
クリック型
- デスクトップ向け
- 意図的な操作
- 誤操作の防止
ホバー型
- マウス操作が前提
- 素早いプレビュー
- モバイルでは使用困難
detailsとsummaryタグによる基本実装
HTML5の標準機能を活用
基本的な構造
最もシンプルな実装
<details>
<summary>クリックして開く</summary>
<p>ここに開閉する内容を書きます。</p>
</details>
より実践的な構造
<details class="faq-item">
<summary class="faq-question">
Q. アコーディオンとは何ですか?
</summary>
<div class="faq-answer">
<p>A. クリックで開閉できるUI要素のことです。</p>
<p>情報を整理して表示するのに便利です。</p>
</div>
</details>
detailsタグの特徴
ブラウザサポート
- 現代的なブラウザで完全サポート
- Internet Explorer 11以下は未対応
- フォールバック対応が可能
アクセシビリティ
- セマンティックな意味を持つ
- スクリーンリーダー対応
- キーボード操作可能
CSSによるスタイリング
基本的なスタイル設定
見た目の改善
details {
margin-bottom: 1rem;
border: 1px solid #e0e0e0;
border-radius: 8px;
overflow: hidden;
}
summary {
padding: 1rem;
background-color: #f8f9fa;
cursor: pointer;
font-weight: 600;
user-select: none;
transition: background-color 0.2s ease;
}
summary:hover {
background-color: #e9ecef;
}
.faq-answer {
padding: 1rem;
background-color: white;
}
開閉状態のスタイル制御
開いた時のスタイル
details[open] {
border-color: #007bff;
}
details[open] summary {
background-color: #007bff;
color: white;
}
details[open] summary:hover {
background-color: #0056b3;
}
デフォルトマーカーの削除とカスタム
ブラウザデフォルトの三角マークを削除
summary {
list-style: none; /* Safari用 */
}
summary::-webkit-details-marker {
display: none; /* Chrome, Edge用 */
}
/* カスタムアイコンの追加 */
summary::after {
content: '+';
float: right;
font-size: 1.2em;
line-height: 1;
transition: transform 0.2s ease;
}
details[open] summary::after {
transform: rotate(45deg);
}
アニメーション効果の追加
スムーズな開閉アニメーション
CSS Transitions を使用
details {
transition: border-color 0.2s ease;
}
.faq-answer {
animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
より高度なアニメーション
高さアニメーションの実装
details {
overflow: hidden;
}
details:not([open]) .faq-answer {
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-10px);
}
}
inputチェックボックスを使った高度な実装
より柔軟な制御が可能な方法
HTML構造
基本的な構造
<div class="accordion-item">
<input type="checkbox" id="accordion-1" class="accordion-toggle">
<label for="accordion-1" class="accordion-header">
<span class="accordion-title">アコーディオンのタイトル</span>
<span class="accordion-icon">+</span>
</label>
<div class="accordion-content">
<div class="accordion-body">
<p>ここにコンテンツが入ります。</p>
<p>複数の段落や他の要素も配置できます。</p>
</div>
</div>
</div>
CSSによる制御
チェックボックスを隠す
.accordion-toggle {
display: none;
}
.accordion-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background-color: #f8f9fa;
cursor: pointer;
user-select: none;
transition: all 0.2s ease;
}
.accordion-header:hover {
background-color: #e9ecef;
}
開閉の制御
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease-out;
}
.accordion-toggle:checked + .accordion-header + .accordion-content {
max-height: 500px; /* 内容に応じて調整 */
}
.accordion-body {
padding: 1rem;
}
アイコンの回転アニメーション
洗練されたアイコン効果
.accordion-icon {
transition: transform 0.3s ease;
font-size: 1.2em;
}
.accordion-toggle:checked + .accordion-header .accordion-icon {
transform: rotate(135deg);
}
/* より現代的なアイコン */
.accordion-icon::before {
content: '';
display: inline-block;
width: 10px;
height: 10px;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(-45deg);
transition: transform 0.3s ease;
}
.accordion-toggle:checked + .accordion-header .accordion-icon::before {
transform: rotate(45deg);
}
複数アコーディオンの管理
独立した複数項目
複数項目のHTMLテンプレート
<div class="accordion-container">
<div class="accordion-item">
<input type="checkbox" id="accordion-1" class="accordion-toggle">
<label for="accordion-1" class="accordion-header">
<span class="accordion-title">項目 1</span>
<span class="accordion-icon">+</span>
</label>
<div class="accordion-content">
<div class="accordion-body">
<p>項目 1 の内容です。</p>
</div>
</div>
</div>
<div class="accordion-item">
<input type="checkbox" id="accordion-2" class="accordion-toggle">
<label for="accordion-2" class="accordion-header">
<span class="accordion-title">項目 2</span>
<span class="accordion-icon">+</span>
</label>
<div class="accordion-content">
<div class="accordion-body">
<p>項目 2 の内容です。</p>
</div>
</div>
</div>
</div>
radioボタンによる排他制御
1つずつしか開かないアコーディオン
<div class="exclusive-accordion">
<input type="radio" name="accordion" id="acc-1" class="accordion-radio">
<label for="acc-1" class="accordion-header">項目 1</label>
<div class="accordion-content">
<div class="accordion-body">内容 1</div>
</div>
<input type="radio" name="accordion" id="acc-2" class="accordion-radio">
<label for="acc-2" class="accordion-header">項目 2</label>
<div class="accordion-content">
<div class="accordion-body">内容 2</div>
</div>
</div>
.accordion-radio {
display: none;
}
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease;
}
.accordion-radio:checked + .accordion-header + .accordion-content {
max-height: 300px;
}
実用的なデザインパターン
FAQページの実装
完全なFAQシステム
HTML構造
<section class="faq-section">
<h2 class="faq-title">よくある質問</h2>
<div class="faq-list">
<details class="faq-item">
<summary class="faq-question">
<span class="question-text">Q. サービスの利用料金はいくらですか?</span>
<span class="question-icon">
<svg width="16" height="16" viewBox="0 0 16 16">
<path d="M8 12l-4-4h8l-4 4z"/>
</svg>
</span>
</summary>
<div class="faq-answer">
<p>基本プランは月額1,000円からご利用いただけます。</p>
<p>詳細な料金体系については、料金ページをご確認ください。</p>
</div>
</details>
<details class="faq-item">
<summary class="faq-question">
<span class="question-text">Q. 無料トライアルはありますか?</span>
<span class="question-icon">
<svg width="16" height="16" viewBox="0 0 16 16">
<path d="M8 12l-4-4h8l-4 4z"/>
</svg>
</span>
</summary>
<div class="faq-answer">
<p>はい、14日間の無料トライアルをご提供しています。</p>
<p>クレジットカードの登録は不要です。</p>
</div>
</details>
</div>
</section>
CSS スタイリング
.faq-section {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
.faq-title {
text-align: center;
margin-bottom: 2rem;
color: #333;
}
.faq-item {
margin-bottom: 1rem;
border: 1px solid #e0e6ed;
border-radius: 8px;
overflow: hidden;
transition: all 0.2s ease;
}
.faq-item:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.faq-question {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
cursor: pointer;
list-style: none;
user-select: none;
}
.faq-question::-webkit-details-marker {
display: none;
}
.question-text {
font-weight: 600;
font-size: 1.1rem;
}
.question-icon svg {
transition: transform 0.3s ease;
fill: currentColor;
}
.faq-item[open] .question-icon svg {
transform: rotate(180deg);
}
.faq-answer {
padding: 1.5rem;
background-color: #f8f9fa;
border-top: 1px solid #e0e6ed;
animation: fadeInDown 0.3s ease;
}
.faq-answer p {
margin: 0 0 1rem 0;
line-height: 1.6;
color: #555;
}
.faq-answer p:last-child {
margin-bottom: 0;
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
モバイルメニューの実装
レスポンシブナビゲーション
モバイル専用アコーディオンメニュー
<nav class="mobile-nav">
<details class="nav-category">
<summary class="nav-title">商品・サービス</summary>
<ul class="nav-submenu">
<li><a href="/products/web">Webサイト制作</a></li>
<li><a href="/products/app">アプリ開発</a></li>
<li><a href="/products/design">デザイン</a></li>
</ul>
</details>
<details class="nav-category">
<summary class="nav-title">会社情報</summary>
<ul class="nav-submenu">
<li><a href="/about">会社概要</a></li>
<li><a href="/team">チーム</a></li>
<li><a href="/news">ニュース</a></li>
</ul>
</details>
</nav>
モバイル向けCSS
.mobile-nav {
display: none;
}
@media (max-width: 768px) {
.mobile-nav {
display: block;
padding: 1rem;
}
.nav-category {
margin-bottom: 0.5rem;
border-bottom: 1px solid #eee;
}
.nav-title {
padding: 1rem 0;
font-weight: 600;
color: #333;
cursor: pointer;
list-style: none;
user-select: none;
position: relative;
}
.nav-title::-webkit-details-marker {
display: none;
}
.nav-title::after {
content: '▼';
position: absolute;
right: 0;
transition: transform 0.2s ease;
font-size: 0.8em;
}
.nav-category[open] .nav-title::after {
transform: rotate(180deg);
}
.nav-submenu {
list-style: none;
padding: 0;
margin: 0 0 1rem 1rem;
animation: slideDown 0.3s ease;
}
.nav-submenu li {
margin-bottom: 0.5rem;
}
.nav-submenu a {
color: #666;
text-decoration: none;
display: block;
padding: 0.5rem 0;
transition: color 0.2s ease;
}
.nav-submenu a:hover {
color: #007bff;
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
カードレイアウトとの組み合わせ
製品紹介カード
カード型アコーディオン
<div class="product-grid">
<div class="product-card">
<img src="product1.jpg" alt="製品1" class="product-image">
<div class="product-info">
<h3 class="product-name">製品名</h3>
<p class="product-price">¥1,000</p>
<details class="product-details">
<summary class="details-toggle">詳細を見る</summary>
<div class="details-content">
<h4>製品の特徴</h4>
<ul>
<li>高品質な素材を使用</li>
<li>環境に優しい製造工程</li>
<li>1年間の保証付き</li>
</ul>
<button class="add-to-cart">カートに追加</button>
</div>
</details>
</div>
</div>
</div>
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
padding: 2rem;
}
.product-card {
border: 1px solid #e0e0e0;
border-radius: 12px;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.product-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.product-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.product-info {
padding: 1.5rem;
}
.product-name {
margin: 0 0 0.5rem 0;
font-size: 1.25rem;
color: #333;
}
.product-price {
margin: 0 0 1rem 0;
font-size: 1.5rem;
font-weight: bold;
color: #e74c3c;
}
.details-toggle {
background: #007bff;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
list-style: none;
user-select: none;
transition: background-color 0.2s ease;
}
.details-toggle::-webkit-details-marker {
display: none;
}
.details-toggle:hover {
background: #0056b3;
}
.details-content {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #eee;
animation: fadeIn 0.3s ease;
}
.details-content h4 {
margin: 0 0 0.5rem 0;
color: #333;
}
.details-content ul {
margin: 0 0 1rem 0;
padding-left: 1.5rem;
}
.details-content li {
margin-bottom: 0.25rem;
color: #666;
}
.add-to-cart {
background: #28a745;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
transition: background-color 0.2s ease;
}
.add-to-cart:hover {
background: #1e7e34;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
トラブルシューティングとベストプラクティス
よくある問題と解決法
問題1:アニメーションがスムーズでない
症状 max-heightを使ったアニメーションがガクガクする
解決法
/* 悪い例 */
.content {
max-height: 0;
transition: max-height 0.3s ease;
}
.content.open {
max-height: 1000px; /* 大きすぎる値 */
}
/* 良い例 */
.content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.content.open {
max-height: 200px; /* 実際の内容に近い値 */
}
問題2:アクセシビリティの考慮不足
改善すべき点
/* フォーカス時のスタイル */
summary:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
/* 高コントラストモード対応 */
@media (prefers-contrast: high) {
.accordion-item {
border-color: #000;
}
.accordion-header {
background-color: #000;
color: #fff;
}
}
/* アニメーション無効化への対応 */
@media (prefers-reduced-motion: reduce) {
.accordion-content {
transition: none;
}
.accordion-icon {
transition: none;
}
}
問題3:レスポンシブ対応
モバイル最適化
@media (max-width: 480px) {
.accordion-item {
margin-bottom: 0.5rem;
border-radius: 4px;
}
.accordion-header {
padding: 1rem 0.75rem;
font-size: 0.9rem;
}
.accordion-body {
padding: 0.75rem;
font-size: 0.85rem;
line-height: 1.5;
}
/* タッチ操作を考慮したサイズ */
.accordion-header {
min-height: 44px; /* Apple推奨の最小タッチターゲット */
}
}
パフォーマンス最適化
大量コンテンツでの最適化
仮想化の検討
/* スクロール最適化 */
.accordion-container {
contain: layout style;
}
/* GPU加速の活用 */
.accordion-content {
transform: translateZ(0);
transition: transform 0.3s ease, opacity 0.3s ease;
}
.accordion-content.closed {
transform: translateY(-100%);
opacity: 0;
}
.accordion-content.open {
transform: translateY(0);
opacity: 1;
}
読み込み時間の最適化
Critical CSSの分離
/* Critical: Above the fold */
.accordion-item {
border: 1px solid #e0e0e0;
margin-bottom: 1rem;
}
.accordion-header {
padding: 1rem;
cursor: pointer;
}
/* Non-critical: 遅延読み込み可能 */
.accordion-item:hover {
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
まとめ
CSSだけで作るアコーディオンメニューについて、基本から応用まで詳しく解説しました。
2つの主要な実装方法
- detailsとsummary:HTML5標準、シンプルで確実
- inputとlabel:デザインの自由度が高い、複雑な制御が可能
重要なポイント
- セマンティックなHTMLでアクセシビリティを確保
- 適切なアニメーションでユーザー体験を向上
- レスポンシブデザインでマルチデバイス対応
- パフォーマンス最適化で高速表示を実現
実用的な活用例
- FAQ(よくある質問)セクション
- モバイル向けナビゲーションメニュー
- 製品詳細の段階的表示
- コンテンツの階層的整理
ベストプラクティス
- アクセシビリティを最優先に考慮
- アニメーション設定の無効化に対応
- 適切なフォーカス管理
- 高コントラストモードへの配慮
コメント