FAQセクションによく使われるアコーディオンUI。
実は「アコーディオン」といっても実装次第でいろんな種類・デザインがありますよね。
いろんなサイトを見ていても「こんなデザインもあるんだなぁ」と思うことが多いです。
- 常に1つだけ開けるタイプ(他は閉じる)
- 複数同時に開けるタイプ
- アイコンを+/−で切り替える
- 矢印が回転するデザイン
- 画像を使う/使わない
など、本当にさまざまです。
私もまだ実案件というよりはコーディング学習でよく作ってきたのですが、
よく指定される条件として多いのは
「最初の1つは開いた状態でスタート」「1つ開いたら他は閉じる」「複数同時に開ける」など。
さらに、開閉ボタンのアイコンも
「画像で実装する方法」「CSSの擬似要素で実装する方法」など色々あります。
今回はそんな中でも、FAQページでよく見かけるパターンの一つ「複数同時に開けるタイプ」を、
HTML・CSS・JavaScriptを使ってシンプルに実装する方法を紹介します。
- 最初の1つは開いた状態
- 複数の質問を同時に開ける
- +/−をCSSだけで切り替え(擬似要素で実装)
- コピペOKで自由に色やサイズをカスタマイズ可能
「FAQを自作で実装したいけど、画像なしで軽量に、デザインも柔軟に変えたい」
そんな方にぴったりのサンプルコードを用意しました!
今回は一番シンプルで、よくある形を紹介します。
完成イメージ
まずは今回この記事で作るFAQアコーディオンの完成イメージを紹介します。
FAQセクションでよく使われる、複数の質問を同時に開けるパターンをシンプルなHTML/CSS/JSで実装します。
画像なしでCSSだけで+/−を切り替えるアイコンを作成し、最初の1つは開いた状態からスタートします。
デザインや色はコード内で自由にカスタマイズ可能です。

複数同時に開けるFAQアコーディオン(+/−切り替えをCSSで実装)
動作サンプルを確認する
実際の動作は以下のCodePenでも確認できます。
※ QとAの内容はサンプルテキストを入れています。実装時は自由に変更してください。
See the Pen Untitled by Ariii (@ariii-cdp) on CodePen.
この記事で実装するFAQアコーディオンのポイント
今回紹介するコードでは、以下のような仕様をシンプルに実装しています。
- 最初の1つは開いた状態
- 複数の質問を同時に開ける
- +/−をCSSだけで切り替え(擬似要素で実装)
- 画像なしで軽量
- コピペOKで自由に色やサイズをカスタマイズ可能
FAQページやサービスサイトなどでよく使われる「複数同時に開けるパターン」を、
HTML・CSS・JavaScriptでシンプルに実装します。
コード紹介
ここからは実際のHTML、CSS、JavaScriptのコードを紹介します。
すべてコピペして使えるようにコメントを入れているので、自分のサイトに合わせて色やフォントを自由にカスタマイズしてみてください。
HTML
<!-- FAQのリスト全体 -->
<div class="faq">
<div class="faq__list">
<!-- 1つ目のFAQアイテム -->
<div class="faq__item">
<button class="faq__question js-accordion-trigger">
<span class="faq__question-content">
<span class="faq__label">Q</span>
<span class="faq__text">サービスの利用に料金はかかりますか?</span>
</span>
<span class="faq__icon"></span>
</button>
<div class="faq__answer">
<div class="faq__answer-inner">
<span class="faq__label">A</span>
<p class="faq__answer-text">
基本的なご利用は無料です。有料プランもご用意していますので、詳細は料金プランページをご確認ください。
</p>
</div>
</div>
</div>
<!-- 2つ目のFAQアイテム -->
<div class="faq__item">
<button class="faq__question js-accordion-trigger">
<span class="faq__question-content">
<span class="faq__label">Q</span>
<span class="faq__text">登録した情報はどのように管理されていますか?</span>
</span>
<span class="faq__icon"></span>
</button>
<div class="faq__answer">
<div class="faq__answer-inner">
<span class="faq__label">A</span>
<p class="faq__answer-text">
お客様の情報は厳重に管理し、プライバシーポリシーに基づき第三者に提供することはありません。
</p>
</div>
</div>
</div>
<!-- 3つ目のFAQアイテム -->
<div class="faq__item">
<button class="faq__question js-accordion-trigger">
<span class="faq__question-content">
<span class="faq__label">Q</span>
<span class="faq__text">退会したい場合はどうすればいいですか?</span>
</span>
<span class="faq__icon"></span>
</button>
<div class="faq__answer">
<div class="faq__answer-inner">
<span class="faq__label">A</span>
<p class="faq__answer-text">
マイページからいつでも退会手続きを行えます。ご不明な点があればお問い合わせフォームからご連絡ください。
</p>
</div>
</div>
</div>
<!-- 4つ目のFAQアイテム -->
<div class="faq__item">
<button class="faq__question js-accordion-trigger">
<span class="faq__question-content">
<span class="faq__label">Q</span>
<span class="faq__text">複数のアカウントを作成できますか?</span>
</span>
<span class="faq__icon"></span>
</button>
<div class="faq__answer">
<div class="faq__answer-inner">
<span class="faq__label">A</span>
<p class="faq__answer-text">
1人につき1アカウントをお願いしております。複数アカウントの作成は利用規約で禁止されています。
</p>
</div>
</div>
</div>
</div>
</div>
CSS
/* ======== 基本の設定 ======== */
/* ページ全体のフォントと背景色 */
body {
font-family: "Noto Sans JP", sans-serif; /* ← 日本語用フォントを変えたいときはここ */
background: #fff;
margin: 0;
padding: 20px;
}
/* ======== FAQ全体のラッパー ======== */
.faq {
padding: 40px 20px;
background: #fff;
}
/* ======== FAQリストエリア ======== */
/* 中央寄せ&幅制限 */
.faq__list {
display: flex;
flex-direction: column;
gap: 16px;
max-width: 800px;
margin: 0 auto;
}
/* ======== FAQアイテム(Q&Aの1セット) ======== */
.faq__item {
border: 2px solid #cccccc; /* ← ボックス枠線の色を変えたいときはここ */
border-radius: 12px;
overflow: hidden;
background: #fff;
}
/* ======== 質問ボタンエリア ======== */
.faq__question {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
width: 100%;
text-align: left;
cursor: pointer;
background: #fff;
border: none;
}
/* Qと質問文を横並びにする */
.faq__question-content {
display: flex;
align-items: center;
gap: 12px;
}
/* Qラベルのデザイン */
.faq__label {
font-family: "Montserrat", sans-serif; /* ← Q/Aラベルのフォントを変えたいときはここ */
color: #8ccad7; /* ← Q/Aラベルの文字色を変えたいときはここ */
font-size: 20px;
flex-shrink: 0;
}
/* 質問文テキスト */
.faq__text {
font-size: 16px;
font-weight: 700;
line-height: 1.6;
}
/* ======== +/−アイコンの丸背景部分 ======== */
.faq__icon {
position: relative;
flex-shrink: 0;
width: 32px;
height: 32px;
background-color: #8ccad7; /* ← アイコン丸背景の色を変えたいときはここ */
border-radius: 50%;
transition: background-color 0.3s ease;
}
/* +/−アイコンの線(縦横) */
.faq__icon::before,
.faq__icon::after {
content: "";
position: absolute;
background-color: #ffffff; /* ← プラスマイナスの線の色を変えたいときはここ */
transition: transform 0.3s ease;
}
/* 横線(常に表示) */
.faq__icon::before {
width: 16px;
height: 2px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* 縦線(開閉で消える) */
.faq__icon::after {
width: 2px;
height: 16px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scaleY(1) rotate(0deg);
}
/* 開いた状態では縦線を縮めて消す */
.faq__item.is-open .faq__icon::after {
transform: translate(-50%, -50%) scaleY(0) rotate(90deg);
}
/* ======== 答え部分のデザイン ======== */
/* 閉じたときは高さ0で隠す */
.faq__answer {
height: 0;
overflow: hidden;
background: #f5f5f5; /* ← A部分の背景色を変えたいときはここ */
transition: height 0.4s ease;
}
/* 開いたときに高さ自動展開 */
.faq__item.is-open .faq__answer {
height: auto;
}
/* 答え内のインナー要素(Aラベルとテキストを横並びに) */
.faq__answer-inner {
display: flex;
gap: 12px;
align-items: center;
padding: 16px;
}
/* 答えテキスト部分 */
.faq__answer-text {
flex: 1;
font-size: 16px;
line-height: 1.6;
}
JavaSctipt
// ページのHTMLが全部読み込まれたら実行
document.addEventListener('DOMContentLoaded', () => {
// ======== ① 最初の1つを開いた状態にする ========
const firstItem = document.querySelector('.faq__item');
if (firstItem) {
firstItem.classList.add('is-open');
const answer = firstItem.querySelector('.faq__answer');
if (answer) {
answer.style.height = 'auto'; // 高さをautoにして開いた状態に
}
}
// ======== ② アコーディオンの開閉動作を設定 ========
document.querySelectorAll('.js-accordion-trigger').forEach(button => {
button.addEventListener('click', () => {
const item = button.closest('.faq__item'); // クリックされたボタンの親アイテム
const answer = item.querySelector('.faq__answer'); // 対応する答え部分
if (item.classList.contains('is-open')) {
// ======== 閉じる処理 ========
// 一度現在の高さを固定
answer.style.height = answer.scrollHeight + 'px';
requestAnimationFrame(() => {
requestAnimationFrame(() => {
answer.style.height = '0px'; // 高さを0にしてアニメーション
});
});
item.classList.remove('is-open');
} else {
// ======== 開く処理 ========
answer.style.height = '0px'; // 初期値0
item.classList.add('is-open');
requestAnimationFrame(() => {
requestAnimationFrame(() => {
answer.style.height = answer.scrollHeight + 'px'; // コンテンツの高さまで広げる
});
});
// ======== トランジションが終わったらautoに戻す ========
const onTransitionEnd = (e) => {
if (e.propertyName === 'height') {
answer.style.height = 'auto'; // autoにして高さを自由に
answer.removeEventListener('transitionend', onTransitionEnd);
}
};
answer.addEventListener('transitionend', onTransitionEnd);
}
});
});
});
コードのポイント解説
ここまで紹介したHTML、CSS、JavaScriptのコードは、そのままコピペして使えるようにコメントを入れています。
ここでは、特にカスタマイズするときに役立つポイントをまとめます。
よくあるカスタマイズ例
コード内にも「ここを変えられる」というコメントを入れていますので、ぜひ自分のサイトに合わせて自由にカスタマイズしてみてください。
まとめ
今回はFAQページによく使われる「複数同時に開けるアコーディオン」を、HTML/CSS/JSでシンプルに実装する方法を紹介しました。
画像を使わず、CSSだけで+/−アイコンを切り替える軽量なコードなので、ぜひコピペして自分のサイトに合わせてカスタマイズしてみてください。
FAQはユーザーの疑問を解消し、サイトの信頼性を高める大事な要素ですので、
ぜひコードストックとして活用し、自分のサイトに合わせて自由にカスタマイズしてみてください!