ライトボックス
サムネイルをクリックすると暗いオーバーレイと拡大表示が現れる。背景か × ボタンで閉じる。
プレビュー
操作クリック/タップで動きます。
仕様書
- 向く場面
- 作品ギャラリー・商品写真の1枚を拡大
- ページ遷移せずその場で拡大したいとき
- サムネイル一覧からの拡大閲覧
- 避ける場面
- 拡大側に長文やフォームを載せる(ダイアログに)
- サムネイルが1〜2枚(最初から大きく出す)
- ヘッダーより低い z-index で fixed 化する
- 構成要素
- サムネイル一覧
- 暗幕オーバーレイ
- 拡大画像
- キャプション
- 閉じるボタン
- 持っている状態
- 通常
- ホバー
- フォーカス
- キーボード
Tab でサムネイル(button)に到達・Enter/Space で開く。開くと閉じるボタンへフォーカスが移り Tab は中に留まる。Esc か × で閉じ、元のサムネイルへ戻る
- 支援技術
オーバーレイは role=dialog aria-modal=true・aria-labelledby でキャプションを名前にする。サムネは aria-label「作品 A を拡大」。閉じたら開いたサムネへフォーカスを戻す。reduce で飛び出しを止める
- 動き
暗幕 250ms ease フェード・画像 300ms cubic-bezier(.34,1.56,.64,1) で 0.6→1 に飛び出す。退場は即時。reduce では即表示
デザインの要点
- 拡大枠は1つだけ用意して中身を差し替える。開くたびに DOM を作ると閉じ判定やフォーカス復帰の対象がずれる
- 背景クリックの閉じ判定は e.target === overlay に限る。closest で緩めると画像やキャプションのクリックでも閉じる
- 暗幕はフェード、画像だけオーバーシュート付きの scale で「ポンと出る」質感にする。両方を弾ませると重たい
- 開いたら閉じるボタンへ、閉じたら開いたサムネへフォーカスを戻す。戻さないと「どこにいるか」が消える
使いどころ
作品ギャラリーや商品写真のように、一覧のサムネイルから『この1枚をよく見たい』ときに使う。ページ遷移せずその場で完結するので、閲覧の流れを切らずに拡大できるのが利点。逆に、拡大した画像に長い説明文やフォームなど読み込む・操作する要素が乗る場合はモーダルダイアログとして設計し直した方がよい(スクロールや複数の対話要素はライトボックスの想定外)。今回のデモは .lb-stage 内に absolute で収まる『枠内完結』のオーバーレイだが、実ページに組み込むときは position: fixed; inset: 0 に変えて画面全体を覆う必要がある。サムネイルが1〜2枚しかないなら最初から大きく表示すればよく、ライトボックスにする意味は薄い。
仕組み
サムネイルをクリックすると、拡大用の要素(.lb-big)の背景と見出し(.lb-title)を書き換えたうえで overlay.hidden を false にするだけの仕組み。画像を毎回ロードし直すのではなく、あらかじめ用意した1つの拡大枠に内容を差し替える設計なので、DOMの生成・破棄コストが無い。開閉のアニメーションは2つに分かれていて、背景の暗幕は lb-in で単純にフェードイン、中身の画像は lb-scale で cubic-bezier(.34,1.56,.64,1) のオーバーシュートを使い『ポンと飛び出す』質感を出している。閉じる経路は3つ(×ボタン・背景クリック・Escapeキー)あり、背景クリックは e.target === overlay の判定で、画像自体をクリックしたときは反応しないようにしている(伝播で誤って閉じない設計)。
実装の注意
背景クリックで閉じる判定を e.target === overlay ではなく closest() 系で緩く書くと、画像や見出しをクリックしたときも閉じてしまう。逆に .lb-box 側で stopPropagation() に頼る設計にすると、要素を追加したときに閉じ判定漏れが起きやすいので、この実装のように『overlay 自身がクリックされたときだけ閉じる』方が壊れにくい。Escape キーのリスナーは常時 document に貼ってあるが、!overlay.hidden のガードを忘れると、開いていない状態でも他のEscape処理(別のモーダル等)と衝突する。実ページで fixed に変えたときは z-index をヘッダーより高くしないと拡大画像がヘッダーの下に隠れる。開いている間 body のスクロールを止める処理もこのデモには無いので実装時に足す必要がある。
アクセシビリティ
.lb-overlay には role="dialog" aria-modal="true" と、キャプション(.lb-title)を指す aria-labelledby を付けてあり、開いた瞬間に閉じるボタンへフォーカスを移す。オーバーレイ内で操作できる要素は閉じるボタン1つなので、keydown で Tab を止めてフォーカスが背後のサムネイルへ漏れないようにしている(拡大内に前へ/次へを足すなら、最初と最後の要素で折り返すトラップに広げる)。閉じたときは拡大を開いたサムネイルへフォーカスを戻す。これを忘れると視覚に頼らない操作では『どこに戻ったか分からない』状態になる。サムネイルは button 要素で aria-label「作品 A を拡大」を持ち、Tab で到達して Enter/Space で開ける。prefers-reduced-motion: reduce では lb-scale のバウンドと暗幕のフェードを止め、即座に表示する。
調整して使う
開閉アニメーションの質感(バウンドの強さ、フェードのみへの簡略化)、画像の最大サイズ、キャプションの有無が主な調整点。作品数が多いギャラリーでは、拡大表示の中に前へ/次へボタンを足して連続して見せる構成もよく使われる(その場合は現在の閉じるボタンに加え、矢印キーでの送りも実装するとキーボード操作が自然になる)。背景の暗さ(rgba(0,0,0,0.72))は写真のコントラストによって0.6〜0.85程度で調整し、暗すぎると拡大画像自体も沈んで見えにくくなる。サムネイルのホバー時拡大(scale)はタッチ端末では効果が無いので、PC専用の演出と割り切ってよい。拡大画像に隣接するサムネイル一覧を小さく添えると、開いたまま次の作品へ移動できてページ遷移の往復が減る。
コード(コピーして使えます)
<div class="lb-stage">
<p class="lb-label">クリックで拡大</p>
<div class="lb-thumbs">
<button class="lb-thumb" type="button" aria-label="作品 A を拡大" data-label="作品 A" data-bg="linear-gradient(135deg,#2b50c7,#5a7bff)"></button>
<button class="lb-thumb" type="button" aria-label="作品 B を拡大" data-label="作品 B" data-bg="linear-gradient(135deg,#e86c4d,#f5a623)"></button>
<button class="lb-thumb" type="button" aria-label="作品 C を拡大" data-label="作品 C" data-bg="linear-gradient(135deg,#2dc793,#2b50c7)"></button>
</div>
<!-- オーバーレイ(absolute で枠内完結) -->
<div class="lb-overlay" role="dialog" aria-modal="true" aria-labelledby="lbTitle" hidden>
<div class="lb-box">
<div class="lb-big" role="img" aria-label="拡大画像"></div>
<p class="lb-title" id="lbTitle"></p>
</div>
<button class="lb-close" type="button" aria-label="閉じる">×</button>
</div>
</div>