before/after 比較スライダー
透明な range と clip-path で2枚の画像を境界線で切り替えるビフォーアフター比較。加工前後やリフォーム事例の紹介に。
ライブデモ
使用例(お題: カフェ MOON BREW)
この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- MOON BREW:ラテアート ビフォーアフター比較スライダー -->
<section class="mb-ba">
<div class="mb-ba__head">
<span class="mb-ba__tag">BARISTA SCHOOL</span>
<h2 class="mb-ba__title">3日でここまで描ける。</h2>
<p class="mb-ba__lead">体験レッスン受講前と受講後の一杯。<br>スライダーを動かして上達を比べてみて。</p>
</div>
<!-- スライダーで2枚を切り替え -->
<figure class="mb-ba__stage">
<img class="mb-ba__img mb-ba__img--after" src="https://picsum.photos/720/520?random=21" alt="受講後のラテアート">
<div class="mb-ba__before" id="mbClip">
<img class="mb-ba__img" src="https://picsum.photos/720/520?random=22&grayscale" alt="受講前のラテ">
<span class="mb-ba__lbl mb-ba__lbl--before">BEFORE</span>
</div>
<span class="mb-ba__lbl mb-ba__lbl--after">AFTER</span>
<span class="mb-ba__line" id="mbLine"><span class="mb-ba__knob">◀▶</span></span>
<input class="mb-ba__range" id="mbRange" type="range" min="0" max="100" value="50" aria-label="比較スライダー">
</figure>
</section>
CSS
/* MOON BREW:ラテアート ビフォーアフタースライダー */
:root {
--cream: #f5ede1;
--brown: #2b1d12;
--amber: #c98a3b;
}
* { box-sizing: border-box; }
body {
margin: 0;
height: 400px;
display: flex;
align-items: center;
gap: 26px;
padding: 0 28px;
font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
background: var(--cream);
color: var(--brown);
overflow: hidden;
}
.mb-ba__head { flex: 0 0 210px; }
.mb-ba__tag { font-size: 10px; letter-spacing: 0.3em; color: var(--amber); }
.mb-ba__title {
margin: 10px 0 12px;
font-size: 23px;
line-height: 1.45;
font-family: "Hiragino Mincho ProN", "Yu Mincho", serif;
}
.mb-ba__lead { margin: 0; font-size: 12.5px; line-height: 1.9; color: #6d5b49; }
/* 比較ステージ */
.mb-ba__stage {
position: relative;
flex: 1;
height: 300px;
margin: 0;
border-radius: 18px;
overflow: hidden;
box-shadow: 0 16px 36px rgba(43,29,18,0.18);
}
.mb-ba__img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.mb-ba__img--after { filter: saturate(1.1); }
/* before レイヤーを幅でクリップ */
.mb-ba__before {
position: absolute;
inset: 0;
width: 50%;
overflow: hidden;
border-right: 2px solid var(--cream);
}
.mb-ba__before .mb-ba__img { width: 720px; max-width: none; }
.mb-ba__lbl {
position: absolute;
top: 14px;
font-size: 10px;
letter-spacing: 0.18em;
padding: 5px 11px;
border-radius: 999px;
font-weight: 700;
}
.mb-ba__lbl--before { left: 14px; background: rgba(43,29,18,0.6); color: #fff; }
.mb-ba__lbl--after { right: 14px; background: var(--amber); color: #2b1d12; }
/* 仕切り線とノブ */
.mb-ba__line {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 2px;
background: var(--cream);
transform: translateX(-1px);
pointer-events: none;
}
.mb-ba__knob {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 10px;
letter-spacing: -1px;
color: #fff;
background: var(--amber);
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
/* 透明な range を全面に重ねて操作 */
.mb-ba__range {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: ew-resize;
}
JavaScript
// range の値で before レイヤー幅と仕切り線位置を更新
const range = document.getElementById("mbRange");
const clip = document.getElementById("mbClip");
const line = document.getElementById("mbLine");
if (range && clip && line) {
const update = () => {
const v = Number(range.value) || 0; // 0〜100
clip.style.width = `${v}%`;
line.style.left = `${v}%`;
};
range.addEventListener("input", update);
update(); // 初期反映(50%)
}
コード
HTML
<!-- before/after 比較スライダー -->
<div class="stage">
<div class="ba" aria-label="ビフォーアフター比較">
<!-- 下層: After(カラー) -->
<img class="ba__img" src="https://picsum.photos/id/1016/900/600" alt="アフター(加工後)" draggable="false">
<!-- 上層: Before(モノクロ)。幅を変えて見せる量を調整 -->
<div class="ba__before">
<img class="ba__img" src="https://picsum.photos/id/1016/900/600?grayscale" alt="ビフォー(加工前)" draggable="false">
<span class="ba__tag ba__tag--before">BEFORE</span>
</div>
<span class="ba__tag ba__tag--after">AFTER</span>
<!-- ドラッグ用ハンドル -->
<input class="ba__range" type="range" min="0" max="100" value="50"
aria-label="比較位置スライダー">
<div class="ba__handle" aria-hidden="true">
<span class="ba__grip"></span>
</div>
</div>
</div>
CSS
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: radial-gradient(circle at 50% 0%, #1b2230, #0c0e14 70%);
font-family: "Segoe UI", system-ui, sans-serif;
}
.stage { padding: 24px; }
/* 比較枠。--pos(%) で境界位置を共有 */
.ba {
--pos: 50%;
position: relative;
width: min(72vw, 440px);
aspect-ratio: 3 / 2;
border-radius: 14px;
overflow: hidden;
user-select: none;
box-shadow: 0 18px 45px -15px rgba(0, 0, 0, .7);
}
.ba__img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* before レイヤーは右端を clip して幅を可変に */
.ba__before {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
clip-path: inset(0 calc(100% - var(--pos)) 0 0);
}
/* ラベル */
.ba__tag {
position: absolute;
top: 12px;
z-index: 3;
padding: 4px 12px;
font-size: 11px;
letter-spacing: .2em;
font-weight: 800;
color: #fff;
background: rgba(12, 14, 20, .6);
backdrop-filter: blur(4px);
border-radius: 999px;
}
.ba__tag--before { left: 12px; }
.ba__tag--after { right: 12px; }
/* 実体は透明な range。当たり判定として全面に置く */
.ba__range {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: ew-resize;
z-index: 4;
}
/* 見た目のハンドル(CSS のみ、--pos に追従) */
.ba__handle {
position: absolute;
top: 0;
bottom: 0;
left: var(--pos);
width: 2px;
background: #fff;
transform: translateX(-1px);
z-index: 2;
pointer-events: none;
box-shadow: 0 0 12px rgba(0, 0, 0, .5);
}
.ba__grip {
position: absolute;
top: 50%;
left: 50%;
width: 38px;
height: 38px;
transform: translate(-50%, -50%);
border-radius: 50%;
background: #fff;
box-shadow: 0 4px 14px rgba(0, 0, 0, .4);
display: grid;
place-items: center;
}
/* 左右の三角矢印 */
.ba__grip::before,
.ba__grip::after {
content: "";
width: 0;
height: 0;
border-block: 5px solid transparent;
position: absolute;
}
.ba__grip::before { border-right: 7px solid #1b2230; left: 8px; }
.ba__grip::after { border-left: 7px solid #1b2230; right: 8px; }
.ba__range:focus-visible ~ .ba__handle .ba__grip {
outline: 3px solid #8ab4ff;
outline-offset: 2px;
}
JavaScript
// range の値を --pos に反映し、before レイヤーとハンドルを同期させる
const ba = document.querySelector(".ba");
const range = ba && ba.querySelector(".ba__range");
if (ba && range) {
// スライダー値(%)を CSS 変数へ
const update = () => {
ba.style.setProperty("--pos", `${range.value}%`);
};
range.addEventListener("input", update);
// 初期反映
update();
}
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「before/after 比較スライダー」の効果を追加してください。
# 追加してほしい効果
before/after 比較スライダー(画像エフェクト)
透明な range と clip-path で2枚の画像を境界線で切り替えるビフォーアフター比較。加工前後やリフォーム事例の紹介に。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- before/after 比較スライダー -->
<div class="stage">
<div class="ba" aria-label="ビフォーアフター比較">
<!-- 下層: After(カラー) -->
<img class="ba__img" src="https://picsum.photos/id/1016/900/600" alt="アフター(加工後)" draggable="false">
<!-- 上層: Before(モノクロ)。幅を変えて見せる量を調整 -->
<div class="ba__before">
<img class="ba__img" src="https://picsum.photos/id/1016/900/600?grayscale" alt="ビフォー(加工前)" draggable="false">
<span class="ba__tag ba__tag--before">BEFORE</span>
</div>
<span class="ba__tag ba__tag--after">AFTER</span>
<!-- ドラッグ用ハンドル -->
<input class="ba__range" type="range" min="0" max="100" value="50"
aria-label="比較位置スライダー">
<div class="ba__handle" aria-hidden="true">
<span class="ba__grip"></span>
</div>
</div>
</div>
【CSS】
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: radial-gradient(circle at 50% 0%, #1b2230, #0c0e14 70%);
font-family: "Segoe UI", system-ui, sans-serif;
}
.stage { padding: 24px; }
/* 比較枠。--pos(%) で境界位置を共有 */
.ba {
--pos: 50%;
position: relative;
width: min(72vw, 440px);
aspect-ratio: 3 / 2;
border-radius: 14px;
overflow: hidden;
user-select: none;
box-shadow: 0 18px 45px -15px rgba(0, 0, 0, .7);
}
.ba__img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
/* before レイヤーは右端を clip して幅を可変に */
.ba__before {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
clip-path: inset(0 calc(100% - var(--pos)) 0 0);
}
/* ラベル */
.ba__tag {
position: absolute;
top: 12px;
z-index: 3;
padding: 4px 12px;
font-size: 11px;
letter-spacing: .2em;
font-weight: 800;
color: #fff;
background: rgba(12, 14, 20, .6);
backdrop-filter: blur(4px);
border-radius: 999px;
}
.ba__tag--before { left: 12px; }
.ba__tag--after { right: 12px; }
/* 実体は透明な range。当たり判定として全面に置く */
.ba__range {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: ew-resize;
z-index: 4;
}
/* 見た目のハンドル(CSS のみ、--pos に追従) */
.ba__handle {
position: absolute;
top: 0;
bottom: 0;
left: var(--pos);
width: 2px;
background: #fff;
transform: translateX(-1px);
z-index: 2;
pointer-events: none;
box-shadow: 0 0 12px rgba(0, 0, 0, .5);
}
.ba__grip {
position: absolute;
top: 50%;
left: 50%;
width: 38px;
height: 38px;
transform: translate(-50%, -50%);
border-radius: 50%;
background: #fff;
box-shadow: 0 4px 14px rgba(0, 0, 0, .4);
display: grid;
place-items: center;
}
/* 左右の三角矢印 */
.ba__grip::before,
.ba__grip::after {
content: "";
width: 0;
height: 0;
border-block: 5px solid transparent;
position: absolute;
}
.ba__grip::before { border-right: 7px solid #1b2230; left: 8px; }
.ba__grip::after { border-left: 7px solid #1b2230; right: 8px; }
.ba__range:focus-visible ~ .ba__handle .ba__grip {
outline: 3px solid #8ab4ff;
outline-offset: 2px;
}
【JavaScript】
// range の値を --pos に反映し、before レイヤーとハンドルを同期させる
const ba = document.querySelector(".ba");
const range = ba && ba.querySelector(".ba__range");
if (ba && range) {
// スライダー値(%)を CSS 変数へ
const update = () => {
ba.style.setProperty("--pos", `${range.value}%`);
};
range.addEventListener("input", update);
// 初期反映
update();
}
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。