戻る↑ボタン(進捗リング付き)
一定量スクロールすると現れる「トップへ戻る」ボタン。周囲のリングが読み進み度を示し、押すと最上部へ滑らかに戻ります。長いページの定番ユーティリティをひとつにまとめました。
ライブデモ
使用例(お題: カフェ MOON BREW)
この技法を「カフェ MOON BREW」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- MOON BREW:焙煎日記の読了補助(戻る↑+進捗リング) -->
<div class="sbt-frame">
<div class="sbt-scroll" id="sbtScroll">
<article class="sbt-body">
<h1>夜更けの焙煎日記 — 6月のエチオピア</h1>
<p>今月のシングルオリジンは、エチオピア・イルガチェフェの浅煎り。ベリーのような明るい酸味が特徴です。</p>
<p>少し下へ進むと、右下に「トップへ戻る」ボタンが現れます。周囲のリングが読み進み度を表します。</p>
<p>焙煎は深夜の静かな時間に。豆のはぜる音に耳をすませ、止めどきを見極めます。</p>
<p>抽出は92℃、30秒の蒸らしから。冷めていく過程で表情が変わるのも、浅煎りの楽しみです。</p>
<p>このサンプルは自動で上下に往復し、ボタンの出現とリングの充填を実演します。</p>
<p>来月はコロンビアの中煎りを予定。続きは焙煎日記でお楽しみください。</p>
</article>
</div>
<button class="sbt-btn" id="sbtBtn" type="button" aria-label="トップへ戻る">
<svg class="sbt-ring" viewBox="0 0 48 48" aria-hidden="true">
<circle class="sbt-ring__bg" cx="24" cy="24" r="21"></circle>
<circle class="sbt-ring__fg" id="sbtRing" cx="24" cy="24" r="21"></circle>
</svg>
<span class="sbt-arrow">↑</span>
</button>
</div>
CSS
/* MOON BREW(カフェ):戻る↑ボタン(進捗リング付き)の再スキン */
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }
.sbt-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: #faf5ec; }
.sbt-scroll { height: 100%; overflow-y: auto; scrollbar-width: thin; }
.sbt-body { padding: 26px 28px 90px; color: #3c2f22; line-height: 1.85; max-width: 600px; }
.sbt-body h1 { font-size: 23px; font-weight: 800; margin: 0 0 14px; font-family: "Hiragino Mincho ProN", serif; }
.sbt-body p { margin: 0 0 15px; font-size: 14px; color: #5b4630; }
.sbt-btn {
position: absolute; right: 18px; bottom: 18px; z-index: 10;
width: 54px; height: 54px; padding: 0; cursor: pointer; border: none; border-radius: 50%;
background: #fffdf8; box-shadow: 0 10px 26px rgba(90,60,30,.2); display: grid; place-items: center;
opacity: 0; transform: translateY(14px) scale(.85); pointer-events: none;
transition: opacity .3s ease, transform .3s ease;
}
.sbt-btn.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.sbt-btn:hover { transform: translateY(-3px) scale(1.04); }
.sbt-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.sbt-ring circle { fill: none; stroke-width: 3; stroke-linecap: round; }
.sbt-ring__bg { stroke: #ece1cd; }
.sbt-ring__fg { stroke: #b07a3f; stroke-dasharray: 131.95; stroke-dashoffset: 131.95; transition: stroke-dashoffset .1s linear; }
.sbt-arrow { font-size: 20px; font-weight: 700; color: #7a4f2a; line-height: 1; }
@media (prefers-reduced-motion: reduce) { .sbt-btn { transition: none; } .sbt-ring__fg { transition: none; } }
JavaScript
// (デモと同じフックを流用)進捗リング更新+しきい値で出現、クリックでトップへ
(() => {
const sc = document.getElementById('sbtScroll');
const btn = document.getElementById('sbtBtn');
const ring = document.getElementById('sbtRing');
if (!sc || !btn || !ring) return;
const CIRC = 131.95;
function update() {
const max = sc.scrollHeight - sc.clientHeight;
const ratio = max > 0 ? Math.min(sc.scrollTop / max, 1) : 0;
ring.style.strokeDashoffset = String(CIRC * (1 - ratio));
btn.classList.toggle('is-shown', sc.scrollTop > 70);
}
let ticking = false;
sc.addEventListener('scroll', () => { if (ticking) return; ticking = true; requestAnimationFrame(() => { update(); ticking = false; }); }, { passive: true });
update();
btn.addEventListener('click', () => { auto = false; sc.scrollTo({ top: 0, behavior: 'smooth' }); });
let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches, dir = 1;
['wheel', 'touchstart'].forEach(ev => sc.addEventListener(ev, () => { auto = false; }, { passive: true }));
if (auto) {
setTimeout(function step() {
if (!auto) return;
const max = sc.scrollHeight - sc.clientHeight;
sc.scrollTop += dir * 1.5;
if (sc.scrollTop >= max - 1) dir = -1; else if (sc.scrollTop <= 0) dir = 1;
requestAnimationFrame(step);
}, 1100);
}
})();
実装ガイド
使いどころ
長いページ全般のユーティリティとして。一定量スクロールすると現れる「トップへ戻る」ボタンで、周囲のリングが読み進み度を示し、押すと最上部へ滑らかに戻ります。
実装時の注意点
scrollTop ÷(全体高 − ビューポート高)の割合を SVG リングの stroke-dashoffset に反映し、しきい値超えで is-shown を付与。出現は opacity+scale の控えめなフェードです。クリックで smooth スクロールしてトップへ戻ります。
対応ブラウザ
SVG stroke アニメーション・smooth scroll・transform は全モダンブラウザで対応します。リングの円周は 2πr から算出しています。
よくある失敗
リングの dasharray は半径に一致させないと満タンにならない(r=21 → 約131.95)。出現しきい値が低すぎると常時表示になり鬱陶しいので、1画面分は過ぎてから出すのが目安。固定ボタンは他のUI(チャット等)と重ならない位置に。
応用例
リング無しのシンプル版、章ごとのジャンプ、押下で目次を開く、進捗100%で色を変えるなどのアレンジが可能です。
コード
HTML
<!-- 進捗リング付き「トップへ戻る」ボタン -->
<div class="sbt-frame">
<div class="sbt-scroll" id="sbtScroll">
<article class="sbt-body">
<h1>戻る↑ボタン(進捗リング付き)</h1>
<p>少し下へ進むと、右下に「トップへ戻る」ボタンが現れます。周囲のリングが読み進み度を表します。</p>
<p>長いページで上部のナビへ素早く戻れる、定番のユーティリティです。出現は控えめなフェード&スケールで。</p>
<p>リングは scrollTop ÷(全体高 − ビューポート高)で算出した割合を stroke-dashoffset に反映しています。</p>
<p>ボタンを押すと最上部へ滑らかに戻ります。このデモは自動で上下に往復します。</p>
<p>最下部まで来るとリングがほぼ満タンに。実装コストの低い割に効果的な仕上げです。</p>
<p>以上、追従ユーティリティの王道でした。</p>
</article>
</div>
<button class="sbt-btn" id="sbtBtn" type="button" aria-label="トップへ戻る">
<svg class="sbt-ring" viewBox="0 0 48 48" aria-hidden="true">
<circle class="sbt-ring__bg" cx="24" cy="24" r="21"></circle>
<circle class="sbt-ring__fg" id="sbtRing" cx="24" cy="24" r="21"></circle>
</svg>
<span class="sbt-arrow">↑</span>
</button>
</div>
CSS
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }
.sbt-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: #fbfbfd; }
.sbt-scroll { height: 100%; overflow-y: auto; scrollbar-width: thin; }
.sbt-body { padding: 26px 28px 90px; color: #2b2d38; line-height: 1.85; max-width: 600px; }
.sbt-body h1 { font-size: 23px; font-weight: 800; margin: 0 0 14px; }
.sbt-body p { margin: 0 0 15px; font-size: 14px; color: #4b4e5b; }
.sbt-btn {
position: absolute; right: 18px; bottom: 18px; z-index: 10;
width: 54px; height: 54px; padding: 0; cursor: pointer;
border: none; border-radius: 50%;
background: #fff; box-shadow: 0 10px 26px rgba(20,24,40,.18);
display: grid; place-items: center;
opacity: 0; transform: translateY(14px) scale(.85); pointer-events: none;
transition: opacity .3s ease, transform .3s ease;
}
.sbt-btn.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.sbt-btn:hover { transform: translateY(-3px) scale(1.04); }
.sbt-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.sbt-ring circle { fill: none; stroke-width: 3; stroke-linecap: round; }
.sbt-ring__bg { stroke: #eceef5; }
.sbt-ring__fg { stroke: #6366f1; stroke-dasharray: 131.95; stroke-dashoffset: 131.95; transition: stroke-dashoffset .1s linear; }
.sbt-arrow { font-size: 20px; font-weight: 700; color: #4f46e5; line-height: 1; }
@media (prefers-reduced-motion: reduce) { .sbt-btn { transition: none; } .sbt-ring__fg { transition: none; } }
JavaScript
// 進捗リング更新+しきい値で出現、クリックでトップへ
(() => {
const sc = document.getElementById('sbtScroll');
const btn = document.getElementById('sbtBtn');
const ring = document.getElementById('sbtRing');
if (!sc || !btn || !ring) return;
const CIRC = 131.95; // 2π * 21
function update() {
const max = sc.scrollHeight - sc.clientHeight;
const ratio = max > 0 ? Math.min(sc.scrollTop / max, 1) : 0;
ring.style.strokeDashoffset = String(CIRC * (1 - ratio));
btn.classList.toggle('is-shown', sc.scrollTop > 70);
}
let ticking = false;
sc.addEventListener('scroll', () => {
if (ticking) return; ticking = true;
requestAnimationFrame(() => { update(); ticking = false; });
}, { passive: true });
update();
btn.addEventListener('click', () => { auto = false; sc.scrollTo({ top: 0, behavior: 'smooth' }); });
// 自動で往復し、出現とリングの充填を実演
let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches, dir = 1;
['wheel', 'touchstart'].forEach(ev => sc.addEventListener(ev, () => { auto = false; }, { passive: true }));
if (auto) {
setTimeout(function step() {
if (!auto) return;
const max = sc.scrollHeight - sc.clientHeight;
sc.scrollTop += dir * 1.5;
if (sc.scrollTop >= max - 1) dir = -1; else if (sc.scrollTop <= 0) dir = 1;
requestAnimationFrame(step);
}, 1100);
}
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「戻る↑ボタン(進捗リング付き)」の効果を追加してください。
# 追加してほしい効果
戻る↑ボタン(進捗リング付き)(追従ウィジェット)
一定量スクロールすると現れる「トップへ戻る」ボタン。周囲のリングが読み進み度を示し、押すと最上部へ滑らかに戻ります。長いページの定番ユーティリティをひとつにまとめました。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 進捗リング付き「トップへ戻る」ボタン -->
<div class="sbt-frame">
<div class="sbt-scroll" id="sbtScroll">
<article class="sbt-body">
<h1>戻る↑ボタン(進捗リング付き)</h1>
<p>少し下へ進むと、右下に「トップへ戻る」ボタンが現れます。周囲のリングが読み進み度を表します。</p>
<p>長いページで上部のナビへ素早く戻れる、定番のユーティリティです。出現は控えめなフェード&スケールで。</p>
<p>リングは scrollTop ÷(全体高 − ビューポート高)で算出した割合を stroke-dashoffset に反映しています。</p>
<p>ボタンを押すと最上部へ滑らかに戻ります。このデモは自動で上下に往復します。</p>
<p>最下部まで来るとリングがほぼ満タンに。実装コストの低い割に効果的な仕上げです。</p>
<p>以上、追従ユーティリティの王道でした。</p>
</article>
</div>
<button class="sbt-btn" id="sbtBtn" type="button" aria-label="トップへ戻る">
<svg class="sbt-ring" viewBox="0 0 48 48" aria-hidden="true">
<circle class="sbt-ring__bg" cx="24" cy="24" r="21"></circle>
<circle class="sbt-ring__fg" id="sbtRing" cx="24" cy="24" r="21"></circle>
</svg>
<span class="sbt-arrow">↑</span>
</button>
</div>
【CSS】
* { box-sizing: border-box; }
body { margin: 0; font-family: "Segoe UI", system-ui, -apple-system, sans-serif; }
.sbt-frame { position: relative; width: 100%; height: 380px; overflow: hidden; background: #fbfbfd; }
.sbt-scroll { height: 100%; overflow-y: auto; scrollbar-width: thin; }
.sbt-body { padding: 26px 28px 90px; color: #2b2d38; line-height: 1.85; max-width: 600px; }
.sbt-body h1 { font-size: 23px; font-weight: 800; margin: 0 0 14px; }
.sbt-body p { margin: 0 0 15px; font-size: 14px; color: #4b4e5b; }
.sbt-btn {
position: absolute; right: 18px; bottom: 18px; z-index: 10;
width: 54px; height: 54px; padding: 0; cursor: pointer;
border: none; border-radius: 50%;
background: #fff; box-shadow: 0 10px 26px rgba(20,24,40,.18);
display: grid; place-items: center;
opacity: 0; transform: translateY(14px) scale(.85); pointer-events: none;
transition: opacity .3s ease, transform .3s ease;
}
.sbt-btn.is-shown { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.sbt-btn:hover { transform: translateY(-3px) scale(1.04); }
.sbt-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.sbt-ring circle { fill: none; stroke-width: 3; stroke-linecap: round; }
.sbt-ring__bg { stroke: #eceef5; }
.sbt-ring__fg { stroke: #6366f1; stroke-dasharray: 131.95; stroke-dashoffset: 131.95; transition: stroke-dashoffset .1s linear; }
.sbt-arrow { font-size: 20px; font-weight: 700; color: #4f46e5; line-height: 1; }
@media (prefers-reduced-motion: reduce) { .sbt-btn { transition: none; } .sbt-ring__fg { transition: none; } }
【JavaScript】
// 進捗リング更新+しきい値で出現、クリックでトップへ
(() => {
const sc = document.getElementById('sbtScroll');
const btn = document.getElementById('sbtBtn');
const ring = document.getElementById('sbtRing');
if (!sc || !btn || !ring) return;
const CIRC = 131.95; // 2π * 21
function update() {
const max = sc.scrollHeight - sc.clientHeight;
const ratio = max > 0 ? Math.min(sc.scrollTop / max, 1) : 0;
ring.style.strokeDashoffset = String(CIRC * (1 - ratio));
btn.classList.toggle('is-shown', sc.scrollTop > 70);
}
let ticking = false;
sc.addEventListener('scroll', () => {
if (ticking) return; ticking = true;
requestAnimationFrame(() => { update(); ticking = false; });
}, { passive: true });
update();
btn.addEventListener('click', () => { auto = false; sc.scrollTo({ top: 0, behavior: 'smooth' }); });
// 自動で往復し、出現とリングの充填を実演
let auto = !matchMedia('(prefers-reduced-motion: reduce)').matches, dir = 1;
['wheel', 'touchstart'].forEach(ev => sc.addEventListener(ev, () => { auto = false; }, { passive: true }));
if (auto) {
setTimeout(function step() {
if (!auto) return;
const max = sc.scrollHeight - sc.clientHeight;
sc.scrollTop += dir * 1.5;
if (sc.scrollTop >= max - 1) dir = -1; else if (sc.scrollTop <= 0) dir = 1;
requestAnimationFrame(step);
}, 1100);
}
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。