Webパーツ辞典

ビフォーアフタースライダー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>

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 ドラッグ動作)。範囲入力を画像全体に重ねると、クリックした位置へ境目が移る
  • 範囲入力を透明にするときは、フォーカスの枠を見えているつまみ側に出す

作るときに使えるツール

関連するパーツ