



WithCodeMedia-1-pc
WithCodeMedia-2-pc
WithCodeMedia-3-pc
WithCodeMedia-4-pc




WithCodeMedia-1-sp
WithCodeMedia-2-sp
WithCodeMedia-3-sp
WithCodeMedia-4-sp









Webページでボタンをクリックすると画面中央にふわっと現れる「モーダルウィンドウ」は、お知らせ・確認ダイアログ・画像拡大など多くの場面で使われます。多くのチュートリアルではJavaScriptを使った実装が紹介されますが、実はCSSだけでも十分に動作するモーダルを作ることができます。
この記事ではCSSのみで実現できる主要な2つの方法(:target疑似クラスを使う方法と、チェックボックスハックを使う方法)を詳しく解説します。さらに、HTMLの標準要素である<dialog>を使ったより現代的なアプローチも紹介します。それぞれの仕組み・メリット・デメリットを理解して、プロジェクトに合った方法を選べるようになりましょう。
「JavaScriptを覚えるまではCSSだけでやりたい」「軽量な実装にしたい」「標準仕様に沿ったアクセシブルなモーダルを作りたい」——どのニーズにも対応できる知識を、コピーして使えるコード例とともに丁寧に説明します。
この記事で学べることは次の4点です。
:target疑似クラス・チェックボックスハック)の仕組みと実装手順<dialog>要素と最小限のJavaScriptを組み合わせたアクセシブルな第3の選択肢
モーダルウィンドウとは、ページの上に重なって表示される小ウィンドウのことです。背景をクリックしたり「閉じる」ボタンを押したりするまで操作できない「集中させる」UIとして、ログインフォーム・確認ダイアログ・お知らせ表示などで幅広く使われています。ポップアップと呼ばれることもありますが、技術的にはモーダルとポップアップは異なるものの、CSSの実装文脈ではほぼ同義として扱われます。
CSSでモーダルを作る主な方法は次の3つです。それぞれにJavaScriptが必要かどうか、難易度、推奨度が異なります。どれを選ぶかはプロジェクトの要件や自分のスキルによって変わります。
| 方式 | JS要否 | 難易度 | おすすめ度 | 主な特徴 |
|---|---|---|---|---|
:target疑似クラス | 不要 | ★★☆☆☆ | ★★★★☆ | URLのハッシュ(#)を利用して開閉。シンプルで覚えやすい |
| チェックボックスハック | 不要 | ★★★☆☆ | ★★★☆☆ | 非表示のチェックボックスの:checked状態を利用。URLが変わらない |
<dialog>要素 | 最小限必要 | ★★☆☆☆ | ★★★★★ | HTML標準要素。アクセシビリティが高く、ESCキーで閉じる機能も標準搭載 |
:target方式はURLにハッシュが付く(例:page.html#modal)ため、モーダルを開いた状態がブラウザ履歴に残ります。チェックボックスハックはURLが変わらない代わりにHTMLの構造に制約があります。<dialog>要素は現代ブラウザで広くサポートされており、showModal()という1行のJavaScriptで動かせます。
これ以降のセクションで各方法を順番に実装していきます。まずはCSSのみで完結する2つの方法から見ていきましょう。
:target疑似クラスで作るモーダル:targetはCSSの疑似クラスのひとつで、「URLのハッシュ(#以降の文字列)と一致するidを持つ要素」にスタイルを当てることができます。たとえばURLがpage.html#modal-sampleになったとき、id="modal-sample"の要素が:target状態になります。これを利用して、モーダルを「ハッシュが一致したら表示、それ以外は非表示」と切り替えることができます。
HTMLには「モーダルを開くリンク」と「モーダル本体」の2つが必要です。開くリンクのhrefにモーダルのidをハッシュ付きで指定します。ユーザーがリンクをクリックするとURLが変わり、:targetが発動してモーダルが表示されます。閉じるリンクのhrefは#!などのハッシュのない値にすることで、:targetが解除されてモーダルが非表示になります。
まず開くリンクとモーダル本体を記述します。idとhrefの#以降の文字列が完全に一致していることが重要です。
<!-- モーダルを開くリンク -->
<a href="#modal-sample" class="btn-open">モーダルを開く</a>
<!-- モーダル本体(id が href の # と一致する) -->
<div id="modal-sample" class="modal-overlay">
<div class="modal-content">
<h2>モーダルのタイトル</h2>
<p>ここにコンテンツを書きます。</p>
<!-- 閉じるリンク(別の ID またはページ外の # を指す) -->
<a href="#!" class="btn-close">✕ 閉じる</a>
</div>
</div>次にCSSで非表示・表示の切り替えを行います。初期状態はdisplay: noneとし、:targetが付いたときだけdisplay: blockにします。position: fixed; inset: 0で画面全体をオーバーレイし、z-indexで他の要素の上に重ねます。
/* ── モーダルの基本構造(:target を使った表示切り替え) ── */
/* 初期状態:非表示 */
.modal-overlay {
display: none;
}
/* #modal-sample がターゲットになったとき(URLが #modal-sample になったとき)表示 */
.modal-overlay:target {
display: block;
position: fixed;
inset: 0; /* top/right/bottom/left を一括指定 */
background-color: rgba(0, 0, 0, 0.55);
z-index: 1000;
}
/* モーダルの中央ボックス */
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 8px;
padding: 2rem;
width: min(90%, 480px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
}
/* 閉じるボタン */
.btn-close {
display: inline-block;
margin-top: 1rem;
color: #555;
text-decoration: none;
}
.btn-close:hover {
color: #d16176;
}URLの変化が気にならない用途(ランディングページの問い合わせモーダルなど)では:target方式が最もシンプルでおすすめです。URLを変えたくない場合は次のチェックボックスハックを使いましょう。
チェックボックスハックとは、非表示にした<input type="checkbox">と<label>を組み合わせ、チェックボックスの:checked状態をCSSで検知してスタイルを切り替えるテクニックです。URLが変わらないため、:target方式よりも幅広い場面で使えます。
チェックボックスのidと<label>のfor属性を一致させると、ラベルをクリックするだけでチェックボックスのオン・オフが切り替わります。CSSの隣接兄弟結合子(+)を使って「チェックされているときだけモーダルを表示」という指定ができます。閉じるボタンも同じlabelで作ることで、もう一度クリックするとチェックが外れてモーダルが非表示になります。
重要な制約として、チェックボックス(input)とモーダル本体(.modal-overlay)は同じ親要素の直接の子要素である必要があります。隣接兄弟結合子+はDOMの順序に依存するため、HTML構造をきちんと整えることが正常動作の鍵です。
<!-- ① 非表示のチェックボックス(id が label の for と一致) -->
<input type="checkbox" id="modal-toggle" class="modal-input" />
<!-- ② 開くボタン(label で #modal-toggle を操作) -->
<label for="modal-toggle" class="btn-open">モーダルを開く</label>
<!-- ③ モーダル本体(input の直後の兄弟要素にする) -->
<div class="modal-overlay">
<div class="modal-content">
<h2>モーダルのタイトル</h2>
<p>ここにコンテンツを書きます。</p>
<!-- 閉じるボタンも label で同じ input を操作 -->
<label for="modal-toggle" class="btn-close">✕ 閉じる</label>
</div>
</div>.modal-input:checked + .modal-overlayという記法で「チェックされた input の直後の .modal-overlay」を指定します。
/* ── チェックボックスハックによるモーダル切り替え ── */
/* チェックボックス自体は画面に表示しない */
.modal-input {
display: none;
}
/* 初期状態:モーダル非表示 */
.modal-overlay {
display: none;
}
/* チェックボックスが :checked のとき、直後の兄弟(.modal-overlay)を表示 */
/* ※ input と .modal-overlay が同じ親の直接の子である必要がある */
.modal-input:checked + .modal-overlay {
display: block;
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.55);
z-index: 1000;
}
/* モーダルの中央ボックス */
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 8px;
padding: 2rem;
width: min(90%, 480px);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
}
/* 開くボタン(label) */
.btn-open {
display: inline-block;
padding: 0.6rem 1.4rem;
background: #d16176;
color: #fff;
border-radius: 4px;
cursor: pointer;
}
/* 閉じるボタン(label) */
.btn-close {
display: inline-block;
margin-top: 1rem;
color: #555;
cursor: pointer;
}
.btn-close:hover {
color: #d16176;
}:target方式の弱点を補える。CSSのみで実装したい、かつURLを変えたくない場合はチェックボックスハックが有効な選択肢です。ただし複数のモーダルを持つページではHTMLが複雑になりやすいため、その場合は<dialog>要素の利用を検討しましょう。

開閉の仕組みができたら次は見た目を磨きましょう。モーダルに必要なビジュアル要素は大きく4つ:(1)背景を暗くするオーバーレイ、(2)コンテンツボックスの中央寄せ、(3)角丸や影によるカード感、(4)分かりやすい閉じるボタンです。
モーダルコンテンツを画面中央に配置する最もシンプルな方法は、position: absoluteとtransform: translate(-50%, -50%)の組み合わせです。top: 50%; left: 50%でボックスの左上端を画面中央に置いてから、transformで自分のサイズの半分だけ上・左にずらすことで、ボックスの中心が画面中央になります。横幅はwidth: min(90vw, 520px)とすると、小さい画面でも左右に余白が保たれるレスポンシブ対応になります。
/* ── モーダルの見た目を整える共通CSS ── */
/* オーバーレイ(背景の暗幕) */
.modal-overlay {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.55); /* 半透明の黒 */
z-index: 1000;
}
/* モーダル本体ボックス */
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 正確な中央寄せ */
background: #fff;
border-radius: 12px; /* 角丸 */
padding: 2rem 2.5rem;
width: min(90vw, 520px); /* レスポンシブ対応 */
max-height: 85vh; /* 縦に長いコンテンツは内部スクロール */
overflow-y: auto;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.3);
}
/* モーダル内の見出し */
.modal-content h2 {
margin-top: 0;
font-size: 1.25rem;
color: #333;
}
/* 閉じるボタン(右上に絶対配置) */
.btn-close-x {
position: absolute;
top: 0.75rem;
right: 1rem;
font-size: 1.5rem;
line-height: 1;
color: #888;
text-decoration: none;
cursor: pointer;
background: none;
border: none;
padding: 0.25rem;
}
.btn-close-x:hover {
color: #d16176; /* ブランドカラーでホバー変化 */
}
/* 開くボタン */
.btn-open {
display: inline-block;
padding: 0.7rem 1.6rem;
background: #d16176;
color: #fff;
border-radius: 6px;
text-decoration: none;
font-weight: bold;
cursor: pointer;
border: none;
font-size: 1rem;
}
.btn-open:hover {
background: #b84f63;
}閉じるボタンは.modal-contentにposition: relativeを追加し、ボタンをposition: absolute; top: 0.75rem; right: 1remで右上に固定するのが一般的なデザインです。:target方式では<a href="#!">、チェックボックスハックでは<label for="...">、<dialog>では<button>と、方式によって使うHTML要素が変わる点に注意しましょう。
背景オーバーレイ(暗幕部分)をクリックしたときにモーダルを閉じたい場合は、:target方式なら.modal-overlay全体を閉じるリンクにして.modal-contentのクリックが伝播しないようにするか、後述の<dialog>要素のアプローチを検討しましょう。CSSのみではオーバーレイクリックによるクローズは難易度が高くなります。
display: noneからdisplay: blockへの切り替えはアニメーションできない、ということはCSSを学ぶ上でよく遭遇する壁です。displayプロパティは離散値のため、transitionを設定しても補間が行われません。そのため、フェードイン・フェードアウトを実現するにはopacityとvisibilityを組み合わせる方法が定番です。
opacity: 0だけにすると要素は見えなくなりますが、クリックなどのイベントは依然として受け付けてしまいます。そこでvisibility: hiddenも同時に使います。visibilityはtransitionで補間できる(離散的に切り替わる)プロパティなので、opacityのフェードと同期させることで「見えなくなったらクリックも受け付けない」状態を作れます。
また、モーダルコンテンツを少し下からスライドして登場させると、より洗練された印象になります。transform: translate(-50%, -48%)(少し上にずれた位置)→ :target時にtranslate(-50%, -50%)(正位置)へ変化させることでスライドアップのアニメーションを実現します。
/* ── opacityとvisibilityを組み合わせたフェードイン・アウト ── */
/* :target 方式の場合(チェックボックスハックは .modal-input:checked + .modal-overlay に読み替える) */
/* 初期状態:透明 + 非インタラクティブ(display:none より柔軟) */
.modal-overlay {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.55);
z-index: 1000;
opacity: 0;
visibility: hidden; /* クリックなどを受け付けない */
transition: opacity 0.3s ease, visibility 0.3s ease;
}
/* :target 状態:フェードインで表示 */
.modal-overlay:target {
opacity: 1;
visibility: visible;
}
/* モーダルコンテンツも少し下からスライドして登場 */
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -48%); /* 少し上に位置をずらしておく */
background: #fff;
border-radius: 12px;
padding: 2rem 2.5rem;
width: min(90vw, 520px);
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.3);
transition: transform 0.3s ease;
}
/* :target 時にコンテンツを正位置(中央)に戻す */
.modal-overlay:target .modal-content {
transform: translate(-50%, -50%);
}チェックボックスハックに同じアニメーションを適用する場合は、.modal-overlay:targetを.modal-input:checked + .modal-overlayに、.modal-overlay:target .modal-contentを.modal-input:checked + .modal-overlay .modal-contentに書き換えるだけです。CSSのセレクター部分だけ変えれば、アニメーションの仕組みはまったく同じです。
2024年以降、Chrome・Firefox・Safariの最新版では@starting-styleルールとdisplayプロパティのアニメーションをサポートし始めています(「離散的アニメーション」)。ただし、まだ普及途上のため、現状ではopacity+visibilityの組み合わせが最も安全な実装です。
<dialog>要素を使う方法
WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。
公式サイト より
今すぐ
無料カウンセリング
を予約!