DEMO
CODE
<div class="demo-stage rsb-stage">
<div class="rsb-box">
<label class="rsb-label" for="rsb-range">明るさ</label>
<div class="rsb-wrap" id="rsb-wrap">
<output class="rsb-bubble" id="rsb-bubble" for="rsb-range" aria-hidden="true">60%</output>
<input class="rsb-range" id="rsb-range" type="range" min="0" max="100" step="1" value="60">
</div>
<div class="rsb-scale" aria-hidden="true"><span>0</span><span>100</span></div>
</div>
</div>
.rsb-stage { display:flex; align-items:center; justify-content:center; padding:2rem 1.5rem; background:#0B0B10; min-height:260px; }
.rsb-box { width:100%; max-width:320px; }
.rsb-label { display:block; margin-bottom:2.6rem; color:#EDEDED; font-size:.88rem; font-weight:600; }
.rsb-wrap { --p:60; --thumb:22px; position:relative; }
.rsb-range { -webkit-appearance:none; appearance:none; display:block; width:100%; height:44px; margin:0; background:transparent; cursor:pointer; }
.rsb-range:focus { outline:none; }
.rsb-range::-webkit-slider-runnable-track { height:6px; border-radius:3px; background:linear-gradient(90deg, #E91B89 0 calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--p) / 100), rgba(255,255,255,.16) 0); }
.rsb-range::-webkit-slider-thumb { -webkit-appearance:none; width:var(--thumb); height:var(--thumb); margin-top:-8px; box-sizing:border-box; border-radius:50%; background:#fff; border:3px solid #E91B89; box-shadow:0 2px 8px rgba(0,0,0,.4); transition: transform .15s ease; }
.rsb-range:focus-visible::-webkit-slider-thumb { box-shadow:0 0 0 3px #fff; }
.rsb-range:active::-webkit-slider-thumb { transform:scale(1.15); }
.rsb-range::-moz-range-track { height:6px; border-radius:3px; background:rgba(255,255,255,.16); }
.rsb-range::-moz-range-progress { height:6px; border-radius:3px; background:#E91B89; }
.rsb-range::-moz-range-thumb { width:var(--thumb); height:var(--thumb); box-sizing:border-box; border-radius:50%; background:#fff; border:3px solid #E91B89; box-shadow:0 2px 8px rgba(0,0,0,.4); transition: transform .15s ease; }
.rsb-range:focus-visible::-moz-range-thumb { box-shadow:0 0 0 3px #fff; }
.rsb-range:active::-moz-range-thumb { transform:scale(1.15); }
.rsb-bubble { position:absolute; bottom:100%; left:calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--p) / 100); margin-bottom:2px; padding:.25rem .55rem; border-radius:8px; background:#D1177A; color:#fff; font-size:.78rem; font-weight:700; font-variant-numeric:tabular-nums; white-space:nowrap; pointer-events:none;
transform:translateX(-50%); transform-origin:50% 100%; transition: transform .2s cubic-bezier(.34,1.56,.64,1); }
.rsb-bubble::after { content:''; position:absolute; top:100%; left:50%; margin-left:-5px; border:5px solid transparent; border-top-color:#D1177A; }
.rsb-wrap.is-active .rsb-bubble { transform:translateX(-50%) scale(1.12); }
.rsb-scale { display:flex; justify-content:space-between; color:#A1A1AA; font-size:.72rem; font-variant-numeric:tabular-nums; }
@media (prefers-reduced-motion: reduce) {
.rsb-bubble { transition-duration:.01ms; }
.rsb-range::-webkit-slider-thumb { transition-duration:.01ms; }
.rsb-range::-moz-range-thumb { transition-duration:.01ms; }
}
(function(){
var wrap = document.getElementById('rsb-wrap');
var range = document.getElementById('rsb-range');
var bubble = document.getElementById('rsb-bubble');
if (!wrap || !range || !bubble) return;
function update(){
var min = Number(range.min), max = Number(range.max), v = Number(range.value);
wrap.style.setProperty('--p', (v - min) / (max - min) * 100);
bubble.textContent = v + '%';
range.setAttribute('aria-valuetext', v + '%');
}
range.addEventListener('input', update);
function on(){ wrap.classList.add('is-active'); }
function off(){ wrap.classList.remove('is-active'); }
var pressed = false;
function release(){ if (pressed) { pressed = false; off(); } }
range.addEventListener('pointerdown', function(){ pressed = true; on(); });
window.addEventListener('pointerup', release);
window.addEventListener('pointercancel', release);
range.addEventListener('keydown', on);
range.addEventListener('blur', off);
update();
})();
AI PROMPT
スライダー(input type="range")のつまみの真上に、今の値を示す吹き出しが付いてくる入力部品を作って。label と for で名前を付け、input と吹き出し(output 要素・aria-hidden="true")を position:relative の要素で囲む。囲んだ要素にカスタムプロパティ --p(0〜100の割合)と --thumb(つまみの幅22px)を持たせ、吹き出しの left を calc(var(--thumb) / 2 + (100% − var(--thumb)) × var(--p) / 100) にして、transform: translateX(-50%) で中心を合わせる(つまみの中心は、つまみの幅の半分から「全体の幅 − つまみの幅」の範囲を動くので、単純な % だと端でずれる)。吹き出しの下には ::after の三角を付ける。スライダーは appearance:none にして、::-webkit-slider-runnable-track と ::-webkit-slider-thumb、::-moz-range-track・::-moz-range-progress・::-moz-range-thumb をそれぞれ別のルールで書く(1つのセレクタにまとめると、分からない側のブラウザでルールごと無視されるため)。WebKit 系は線の塗りを linear-gradient の区切り位置(吹き出しと同じ計算)で作る。input は高さ44pxにして押しやすくし、フォーカス時はつまみの周りに白い輪を出す(背景とのコントラスト3:1以上)。input イベントで --p と吹き出しの文字(60%)を更新し、aria-valuetext にも「60%」を入れる。押している間とキーボードで操作した後(フォーカスが外れるまで)は吹き出しを scale(1.12) に少し大きくする。スライダーの外でマウスを離しても元に戻るように、pointerdown で押している印を付け、window で受けた pointerup・pointercancel で印が付いている時だけ元の大きさに戻す。@media (prefers-reduced-motion: reduce) では吹き出しとつまみの transition を無効化する。 Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
CREATOR TOOLS · スクール(PR)
※本サイトはアフィリエイトリンクを含みます。リンク経由のご購入・お申し込みで運営者が紹介料を受け取る場合があります。
スライダーを動かすと、つまみの真上に今の値を示す吹き出しが付いてくる入力部品です。音量・明るさ・予算の上限のように、細かい数値を目で確かめながら決めたい場面で使われます。
当サイトの Toggle Switch はスライドで切り替えるスイッチ、Password Strength Meter は入力に合わせて4段のメーターが伸びる強度表示、Anchor Positioning Tooltip は CSS の anchor-name で要素に紐づけて配置するツールチップです。本ページはブラウザ標準のスライダーを使ったまま、見た目と値の見せ方だけを整える別テーマです。
標準の input type="range" を使うと、矢印キーでの操作や読み上げへの値の通知はブラウザが用意してくれます。見た目を変える時も input 要素そのものは残し、aria-valuetext で単位付きの値を伝えると、誰にとっても分かりやすいスライダーになります。
吹き出しの位置が端でずれないのはなぜですか?
スライダーのつまみは、線の左端から右端まで動くのではなく、つまみの幅の半分だけ内側を動きます。値が0の時のつまみの中心は左端からつまみの幅の半分の位置、100の時は右端から同じだけ内側です。本実装は「つまみの幅の半分 +(全体の幅 − つまみの幅)× 割合」で中心を求めているため、0でも100でも吹き出しがつまみの真上に来ます。
つまみのCSSを1つのルールにまとめないのはなぜですか?
::-webkit-slider-thumb と ::-moz-range-thumb は、それぞれ対応するブラウザでしか分からない書き方です。CSS では、カンマで並べたセレクタの中に分からないものが1つでもあると、そのルール全体が無視されます。そのため、同じ見た目でも WebKit 系と Firefox 向けを別々のルールに分けて書いています。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。