「BEFORE」と「AFTER」の画像比較スライダーを操作する男性キャラクターのイラスト。Reactコンポーネント、アクセシビリティ(A11y)、タッチ・キーボード対応といった、記事で解説する機能が図示されている。

はじめに

正直なところ、最初は画像を並べるだけで十分だと考えていました。

しかし、実際に使ってみるとスライダーの重要性に気づきました。
Web アプリの結果画面で、AI が生成したデザイン画像の Before/After を表示していましたが、従来は2枚の画像を並べて表示するだけでした。

ユーザーが細かい違いを比較しにくいという課題があり、スライダーで重ねて比較できるインタラクティブな UI に改善することになりました。

本記事では、react-compare-slider を使った Before/After 画像比較スライダーの実装方法を、タッチデバイス対応・アクセシビリティ対応まで含めて解説します。

こんな人におすすめ

  • React で Before/After 比較 UI を実装したい方
  • react-compare-slider の使い方を学びたい方
  • タッチデバイス対応・アクセシビリティ対応の実装に関心がある方
  • UI コンポーネントの設計パターンを学びたい方

良かった点

実際にライブラリを導入してみて、一番驚いたのは実装の手軽さでした。

タッチ対応・キーボード操作・disabled 機能が全て react-compare-slider に組み込み済みで、自前で実装する必要がまったくありませんでした。
アクセシビリティ対応(role="slider"aria-disabled)も自動で処理されるため、開発者側の実装コストが大幅に下がりました。

個人的に気に入ったのは、keyboardIncrement="5%" の一行だけでキーボード操作の移動量を設定できる点です。
ゼロ依存で軽量というのも、プロダクトに組み込む際の安心感につながりました。

Before/After 画像比較スライダーとは

Before/After 画像比較スライダーとは、2枚の画像を重ねて表示し、スライダーをドラッグすることで比較できるインタラクティブな UI コンポーネントです。

  • ドラッグ操作で Before/After の境界線を移動
  • 重ねて表示することで細かい違いを比較可能
  • タッチデバイスに対応していればモバイルでも操作可能
  • 写真比較サイトや、リフォーム事例、施術のビフォーアフター表示などで活用されています

React では、react-compare-slider というライブラリを使うことで、この機能を簡単に実装できます。

要件

スライダー機能の要件を整理します。

flowchart TD
    A[Before/After比較機能] --> B[スライダー操作]
    A --> C[デバイス対応]
    A --> D[アクセシビリティ]

    B --> B1[ドラッグで比較]
    B --> B2[初期位置: 50%]

    C --> C1[タッチデバイス対応]
    C --> C2[ハンドルサイズ: 48px以上]

    D --> D1[キーボード操作対応]
    D --> D2[未ログイン時は無効化]

    style A fill:#e0e7ff,stroke:#6366f1,color:#000
    style B fill:#d1fae5,stroke:#10b981,color:#000
    style C fill:#fef3c7,stroke:#f59e0b,color:#000
    style D fill:#fce7f3,stroke:#ec4899,color:#000
  • スライダーをドラッグして Before/After を重ねて比較
  • タッチデバイス対応(モバイルでの操作性確保)
  • スライダー位置の初期値は中央(50%)
  • モバイルでも操作しやすいハンドルサイズ(48px 以上)
  • キーボード操作対応(アクセシビリティ)
  • 未ログイン時はスライダー操作を無効化してログイン促進

実装方針

1. ライブラリ選定: react-compare-slider を採用

  • タッチデバイス対応が組み込み済み
  • キーボードナビゲーション対応(keyboardIncrement prop)
  • ゼロ依存で軽量
  • disabled prop が組み込み済み

2. コンポーネント設計: ImageCompareSlider.tsx を新規作成

  • Props: beforeImage, afterImage, beforeAlt, afterAlt, initialPosition, disabled, showLabels, className
  • ハンドル: green-500 色、48x48px
  • トランジション: 0.2s ease-out

3. 画面統合

  • 2画像レイアウト: スライダーで Before/After 表示
  • 3画像レイアウト: メインビューをスライダー化、サブビューは別枠
  • モーダルボタン: スライダー下部に「Before 拡大」「After 拡大」ボタン配置

4. スタイル実装

  • @layer components で全スタイルをスコープ化
  • ラベル: 左上「Before」、右上「After」(半透明黒背景 + backdrop-blur)
  • disabled 時オーバーレイ: ログインプロンプト表示
  • レスポンシブ対応

react-compare-slider の API 活用

主要な Props の使い方を解説します。

<ReactCompareSlider
  itemOne={<ReactCompareSliderImage src={beforeImage} alt={beforeAlt} />}
  itemTwo={<ReactCompareSliderImage src={afterImage} alt={afterAlt} />}
  position={initialPosition}
  disabled={disabled}
  handle={
    <ReactCompareSliderHandle
      buttonStyle={{
        backgroundColor: '#22c55e',
        width: 48,
        height: 48,
      }}
    />
  }
  keyboardIncrement="5%"
  transition="0.2s ease-out"
/>

主要な Props の一覧です。

Props 説明 デフォルト値
itemOne Before 側の画像(左側) 必須
itemTwo After 側の画像(右側) 必須
position スライダー初期位置(0〜100) 50
disabled 操作を無効化するか false
handle ハンドルのカスタマイズ デフォルトハンドル
keyboardIncrement キーボード操作時の移動量 “1%”
transition アニメーション効果 なし

CSS スコープ化

Tailwind CSS v4 で @layer components を使用してグローバルスタイルの汚染を防止します。

@layer components {
  .compare-slider-container {
    @apply relative w-full max-w-4xl mx-auto rounded-lg overflow-hidden shadow-md;
  }
}

ラベルの表示や disabled 時のオーバーレイも @layer components 内に閉じ込めることで、他のコンポーネントへの影響を排除できます。

アクセシビリティ対応

ライブラリ組み込みのアクセシビリティ機能を活用します。

  • ライブラリ組み込みのキーボード操作(左右矢印キー)
  • role="slider" 自動設定
  • disabled 時は aria-disabled="true" が自動設定
  • ボタン要素はデフォルトでフォーカス可能
sequenceDiagram
    participant U as ユーザー
    participant S as Slider
    participant K as キーボード

    Note over U,S: タッチ操作
    U->>S: ドラッグ
    S->>S: 位置更新

    Note over U,K: キーボード操作
    U->>K: 矢印キー押下
    K->>S: keyboardIncrement分移動
    S->>U: 位置更新

    Note over U,S: disabled時
    U->>S: 操作試行
    S-->>U: aria-disabled="true"

開発者側で追加すべき対応は以下の通りです。

  • 画像の alt 属性を適切に設定する
  • aria-label でスライダーの目的を説明する
  • キーボードフォーカスが見えるようにする

つまづいた点

テストコードのブラケット不整合

実装の中で一番時間がかかったのはテストコードの修正でした。
大規模なテストファイルでは、ブラケットの対応が崩れていると構文エラーになりますが、どこがずれているか一見わかりませんでした。

grep -c '{' file && grep -c '}' file でブラケット数を比較することで原因を特定できました。

button 要素のフォーカス仕様

button 要素のフォーカス動作を検証しようとした際、tabIndex="0" の存在を確認するテストを書いていましたが、ネイティブの <button> 要素はデフォルトでフォーカス可能なため、そのチェック自体が不要でした。

ライブラリのデフォルト動作を過信せずに確認する習慣の大切さを学びました。

よくある質問

Q. react-compare-slider 以外の選択肢は?

いくつかの代替ライブラリがあります。

ライブラリ 特徴 推奨ケース
react-compare-slider タッチ対応・アクセシビリティ対応・軽量 推奨
react-before-after-slider シンプルで使いやすい 基本的な比較のみ
react-compare-image カスタマイズ性が高い 高度なカスタマイズが必要な場合

個人的には、react-compare-slider が最もバランスが良いと感じています。

Q. モバイルでの動作は?

react-compare-slider はタッチデバイス対応が組み込み済みなので、モバイルでもスムーズに動作します。
ハンドルサイズを 48px 以上にすること、touch-action: none CSS プロパティでスクロールとの競合を防ぐことがポイントです。

Q. TypeScript の型定義は?

react-compare-slider は TypeScript の型定義を提供しています。
インポートするだけで型補完が有効になります。

まとめ

React で Before/After 画像比較スライダーを実装する方法を解説しました。

  • react-compare-slider を採用: タッチ対応・キーボード対応・disabled 機能を一括実装
  • Tailwind CSS v4 の @layer components: スタイルをスコープ化
  • アクセシビリティ対応: ライブラリ組み込みで実現

個人的には、このライブラリ選定で実装工数を大幅に削減できたと感じています。
要件に合ったライブラリを選ぶ重要性を教えてくれた実装でした。