ビフォーアフタースライダーBefore/after slider
加工前と加工後の2枚の画像をぴったり重ね、縦の境目のつまみを左右に動かすと、片側が前・もう片側が後の画像に切り替わって見比べられる部品。
表示例
アイコン: Font Awesome 7 Free(fa-solid / fa-regular)
別名・英語表記
- 画像比較スライダー
- 比較スライダー
- before/after スライダー
- ビフォーアフター画像
- 見比べスライダー
- 画像の比較表示
- Before/after slider
実装例
<div class="x-before-after">
<div class="x-before-after__stage">
<img class="x-before-after__img" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='190'%3E%3Crect width='320' height='190' fill='%23c9ccd1'/%3E%3Ccircle cx='250' cy='52' r='22' fill='%23e5e6e8'/%3E%3Cpath d='M0 150 L90 70 L160 130 L230 80 L320 150 V190 H0Z' fill='%23868b93'/%3E%3C/svg%3E" alt="加工前: 色あせて灰色がかった山の写真" width="320" height="190">
<!-- 上に重ねた「後」の画像を、境目より左だけ切り取って隠す -->
<img class="x-before-after__img x-before-after__after" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='190'%3E%3Crect width='320' height='190' fill='%238ec5f0'/%3E%3Ccircle cx='250' cy='52' r='22' fill='%23ffd23f'/%3E%3Cpath d='M0 150 L90 70 L160 130 L230 80 L320 150 V190 H0Z' fill='%232f8a4c'/%3E%3C/svg%3E" alt="加工後: 空が青く山が緑に色づいた同じ写真" width="320" height="190">
<!-- 操作の本体は範囲入力。見えない状態で画像全体に重ね、クリックした位置へ境目を動かす -->
<input class="x-before-after__r" type="range" min="0" max="100" value="50" aria-label="加工前と加工後の境目の位置(左が加工前)">
<span class="x-before-after__tag" aria-hidden="true">前</span>
<span class="x-before-after__tag x-before-after__tag--after" aria-hidden="true">後</span>
<span class="x-before-after__line" aria-hidden="true">
<span class="x-before-after__knob"><i class="fa-solid fa-arrows-left-right"></i></span>
</span>
</div>
</div>
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-before-after,
.x-before-after *:not([class*="fa-"]),
.x-before-after *:not([class*="fa-"])::before,
.x-before-after *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
/* 境目の位置は変数で持ち、範囲入力の値に合わせて JavaScript が書き換える */
.x-before-after {
--x-before-after-pos: 50%;
width: 320px;
max-width: 100%; /* 狭い画面では親の幅に合わせて縮む */
}
.x-before-after__stage {
position: relative;
height: 190px;
border-radius: 8px;
overflow: hidden;
user-select: none;
}
.x-before-after__img {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
/* 後の画像は境目より右だけを見せる */
.x-before-after__after {
clip-path: inset(0 0 0 var(--x-before-after-pos));
}
.x-before-after__tag {
position: absolute;
top: 8px;
left: 8px;
padding: 2px 8px;
border-radius: 4px;
background: #1b2028;
color: #fff;
font-size: 11px;
font-weight: 700;
pointer-events: none;
}
.x-before-after__tag--after {
left: auto;
right: 8px;
}
.x-before-after__line {
position: absolute;
top: 0;
bottom: 0;
left: var(--x-before-after-pos);
width: 2px;
margin-left: -1px;
background: #fff;
box-shadow: 0 0 0 1px rgba(27, 32, 40, 0.5);
pointer-events: none;
}
.x-before-after__knob {
position: absolute;
top: 50%;
left: 50%;
display: grid;
place-items: center;
width: 34px;
height: 34px;
border: 2px solid #1b2028;
border-radius: 50%;
background: #fff;
color: #1b2028;
font-size: 13px;
transform: translate(-50%, -50%);
}
/* 範囲入力は透明にして全面に重ねる。見た目はつまみ(knob)が受け持つ */
.x-before-after__r {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
z-index: 2;
cursor: ew-resize;
}
/* 入力が透明なので、フォーカスの枠はつまみに出す */
.x-before-after__r:focus-visible ~ .x-before-after__line .x-before-after__knob {
outline: 3px solid #1d4ed8;
outline-offset: 2px;
}
AIへの指示文
「ビフォーアフタースライダー(Before/after slider)」を実装してください。
## これは何か
加工前と加工後の2枚の画像をぴったり重ね、縦の境目のつまみを左右に動かすと、片側が前・もう片側が後の画像に切り替わって見比べられる部品。
## 使いどころ
- 写真の補正、リフォーム、清掃、化粧品のように、同じ構図の前と後の違いで効果を見せたいとき
- 2枚を横に並べると細部の違いが分かりにくく、同じ位置で重ねて見比べさせたいとき
## 実装の要件
- 2枚の img を position: absolute; inset: 0; で重ね、上の画像に clip-path: inset(0 0 0 var(--pos)) を掛けて境目より右だけを見せる
- input type="range"(0〜100)を opacity: 0 で画像全体に重ね、input イベントで変数 --pos を書き換える
- 境目の線とつまみと「前」「後」の文字は pointer-events: none にして、操作が下の範囲入力に届くようにする
- 2枚の画像には width と height を書き、読み込み中に枠の高さがずれないようにする
- 2枚の画像は同じ大きさ・同じ構図にそろえる。ずれていると、境目を動かしたときに違いではなく位置のずれが目立つ
- どちらが前でどちらが後かを、画像の上の文字(「前」「後」)で示す。境目の位置だけでは分からない
- 画像の上を横にドラッグする操作は、スマートフォンで横スワイプやカルーセルの操作とぶつかる。つまみの周りだけで受け取るか、touch-action: pan-y で縦のスクロールは残す
- 違いが小さいものは、この部品にせず2枚を並べるか、違う箇所を囲んだ画像を別に出す
## アクセシビリティの要件
- 操作の本体は input type="range" にする。矢印キーで動かせ、読み上げに値が伝わる。独自に作るなら role="slider" と aria-valuenow / aria-valuemin / aria-valuemax とキー操作を全部実装する
- 範囲入力には「加工前と加工後の境目の位置(左が加工前)」のように、何を動かすのかが分かる名前を付ける
- 2枚の画像それぞれに、違いが分かる代替テキストを付ける(「加工前: 色あせた写真」「加工後: 空が青く色づいた写真」)。見比べられない人にも何が変わったかが伝わる
- つまみはドラッグしなくても動かせるようにする(WCAG 2.2 の 2.5.7 ドラッグ動作)。範囲入力を画像全体に重ねると、クリックした位置へ境目が移る
- 範囲入力を透明にするときは、フォーカスの枠を見えているつまみ側に出す
## 参考にする実装
この形を出発点にして、プロジェクトの命名規則・色・余白に合わせて調整してください。
```html
<div class="x-before-after">
<div class="x-before-after__stage">
<img class="x-before-after__img" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='190'%3E%3Crect width='320' height='190' fill='%23c9ccd1'/%3E%3Ccircle cx='250' cy='52' r='22' fill='%23e5e6e8'/%3E%3Cpath d='M0 150 L90 70 L160 130 L230 80 L320 150 V190 H0Z' fill='%23868b93'/%3E%3C/svg%3E" alt="加工前: 色あせて灰色がかった山の写真" width="320" height="190">
<!-- 上に重ねた「後」の画像を、境目より左だけ切り取って隠す -->
<img class="x-before-after__img x-before-after__after" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='320' height='190'%3E%3Crect width='320' height='190' fill='%238ec5f0'/%3E%3Ccircle cx='250' cy='52' r='22' fill='%23ffd23f'/%3E%3Cpath d='M0 150 L90 70 L160 130 L230 80 L320 150 V190 H0Z' fill='%232f8a4c'/%3E%3C/svg%3E" alt="加工後: 空が青く山が緑に色づいた同じ写真" width="320" height="190">
<!-- 操作の本体は範囲入力。見えない状態で画像全体に重ね、クリックした位置へ境目を動かす -->
<input class="x-before-after__r" type="range" min="0" max="100" value="50" aria-label="加工前と加工後の境目の位置(左が加工前)">
<span class="x-before-after__tag" aria-hidden="true">前</span>
<span class="x-before-after__tag x-before-after__tag--after" aria-hidden="true">後</span>
<span class="x-before-after__line" aria-hidden="true">
<span class="x-before-after__knob"><i class="fa-solid fa-arrows-left-right"></i></span>
</span>
</div>
</div>
```
```css
/* 貼った先のリセットに頼らないよう、この部品の中だけ border-box にする */
.x-before-after,
.x-before-after *:not([class*="fa-"]),
.x-before-after *:not([class*="fa-"])::before,
.x-before-after *:not([class*="fa-"])::after {
box-sizing: border-box;
/* 貼った先の既定の書体に引きずられないようにする。書体が変わると
文字の幅と高さが変わり、枠のサイズまでずれる */
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
}
/* 境目の位置は変数で持ち、範囲入力の値に合わせて JavaScript が書き換える */
.x-before-after {
--x-before-after-pos: 50%;
width: 320px;
max-width: 100%; /* 狭い画面では親の幅に合わせて縮む */
}
.x-before-after__stage {
position: relative;
height: 190px;
border-radius: 8px;
overflow: hidden;
user-select: none;
}
.x-before-after__img {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
/* 後の画像は境目より右だけを見せる */
.x-before-after__after {
clip-path: inset(0 0 0 var(--x-before-after-pos));
}
.x-before-after__tag {
position: absolute;
top: 8px;
left: 8px;
padding: 2px 8px;
border-radius: 4px;
background: #1b2028;
color: #fff;
font-size: 11px;
font-weight: 700;
pointer-events: none;
}
.x-before-after__tag--after {
left: auto;
right: 8px;
}
.x-before-after__line {
position: absolute;
top: 0;
bottom: 0;
left: var(--x-before-after-pos);
width: 2px;
margin-left: -1px;
background: #fff;
box-shadow: 0 0 0 1px rgba(27, 32, 40, 0.5);
pointer-events: none;
}
.x-before-after__knob {
position: absolute;
top: 50%;
left: 50%;
display: grid;
place-items: center;
width: 34px;
height: 34px;
border: 2px solid #1b2028;
border-radius: 50%;
background: #fff;
color: #1b2028;
font-size: 13px;
transform: translate(-50%, -50%);
}
/* 範囲入力は透明にして全面に重ねる。見た目はつまみ(knob)が受け持つ */
.x-before-after__r {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
z-index: 2;
cursor: ew-resize;
}
/* 入力が透明なので、フォーカスの枠はつまみに出す */
.x-before-after__r:focus-visible ~ .x-before-after__line .x-before-after__knob {
outline: 3px solid #1d4ed8;
outline-offset: 2px;
}
```
## 出力の約束
- HTML と CSS を分けて出す。JavaScript が要るときは素の JavaScript で書き、UIフレームワークは使わない。
- 参考実装がアイコンに Font Awesome のクラスを使っている場合は、プロジェクトで使っているアイコンかインライン SVG に置き換えてよい。
- 上のアクセシビリティ要件を満たす。満たせない箇所があるときは、理由を添えて先に知らせる。
- 色・角丸・余白は既存のデザイントークンがあればそれを使う。無ければ CSS 変数として切り出す。
使いどころ
- 写真の補正、リフォーム、清掃、化粧品のように、同じ構図の前と後の違いで効果を見せたいとき
- 2枚を横に並べると細部の違いが分かりにくく、同じ位置で重ねて見比べさせたいとき
注意点
- 2枚の画像は同じ大きさ・同じ構図にそろえる。ずれていると、境目を動かしたときに違いではなく位置のずれが目立つ
- どちらが前でどちらが後かを、画像の上の文字(「前」「後」)で示す。境目の位置だけでは分からない
- 画像の上を横にドラッグする操作は、スマートフォンで横スワイプやカルーセルの操作とぶつかる。つまみの周りだけで受け取るか、touch-action: pan-y で縦のスクロールは残す
- 違いが小さいものは、この部品にせず2枚を並べるか、違う箇所を囲んだ画像を別に出す
アクセシビリティの要点
- 操作の本体は input type="range" にする。矢印キーで動かせ、読み上げに値が伝わる。独自に作るなら role="slider" と aria-valuenow / aria-valuemin / aria-valuemax とキー操作を全部実装する
- 範囲入力には「加工前と加工後の境目の位置(左が加工前)」のように、何を動かすのかが分かる名前を付ける
- 2枚の画像それぞれに、違いが分かる代替テキストを付ける(「加工前: 色あせた写真」「加工後: 空が青く色づいた写真」)。見比べられない人にも何が変わったかが伝わる
- つまみはドラッグしなくても動かせるようにする(WCAG 2.2 の 2.5.7 ドラッグ動作)。範囲入力を画像全体に重ねると、クリックした位置へ境目が移る
- 範囲入力を透明にするときは、フォーカスの枠を見えているつまみ側に出す