PRACTICAL WEB TIP
First Train — First Stop
A route indicator tracks scrolling and returns readers to the first stop.
Design Preview
See the design above before trying the scroll interaction.
Live Demo
Press Try scrolling inside the frame, or scroll inside it. The button appears at the lower right and returns you to the top.
Source Code
Copy the HTML, CSS or JavaScript you need for your project.
<button class="btt-control btt-train" data-btt type="button" aria-label="ページの先頭・始発駅へ戻る"><span class="r-btn-label"><i class="r-btn-train-icon">▰</i><span><b>始発へ</b><small>FIRST STOP<br>PAGE TOP</small></span></span><span class="r-btn-body"><span class="r-line" aria-hidden="true"><i></i><i></i><i></i><i></i></span><span class="r-stopnames" aria-hidden="true"><span>潮見</span><span>青野</span><span>木立</span><span>湊町</span></span><i class="r-traveler" aria-hidden="true"></i></span><span class="r-btn-foot">BACK TO THE START <span>↖</span></span></button>
/* Isolated button CSS, extracted from approved original demo */
.btt-control,.btt-control *, .btt-control *::before,.btt-control *::after{box-sizing:border-box}.btt-control{font-family:Arial,"Noto Sans CJK JP","Yu Gothic",sans-serif}
.btt-control:focus-visible{outline:3px solid #f0ad4d;outline-offset:5px}
.btt-train{position:fixed;z-index:30;left:clamp(12px,2.3vw,34px);bottom:27px;width:154px;display:grid;grid-template-rows:auto auto auto;text-align:left;cursor:pointer;border:2px solid #d8eafa;border-radius:15px;background:#174774;color:#fff;padding:0;box-shadow:0 12px 30px #12394e3b;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(20px);transition:opacity .32s,transform .32s,visibility .32s}
.btt-train.is-visible{opacity:1;visibility:visible;pointer-events:auto;transform:translateY(0)}
.r-btn-label{padding:12px 12px 10px;display:flex;align-items:center;gap:10px;border-bottom:1px solid #ffffff45}
.r-btn-train-icon{display:grid;place-items:center;width:32px;height:32px;border:2px solid #fff;border-radius:7px;font-style:normal;font-size:21px;line-height:1;transform:rotate(0)}
.r-btn-label b{display:block;font:800 19px/1.2 "Noto Sans CJK JP",sans-serif}
.r-btn-label small{font:700 7.4px/1.6 Arial;letter-spacing:.09em;white-space:nowrap}
.r-btn-body{height:130px;position:relative;padding:12px 8px 14px 27px}
.r-line{position:absolute;left:26px;top:17px;bottom:20px;width:3px;border-radius:4px;background:#9bc3d8;display:flex;flex-direction:column;justify-content:space-between;align-items:center}
.r-line i{width:10px;height:10px;border:2px solid white;border-radius:50%;background:#15517b}
.r-line i:first-child{background:#e6b558;border-color:#ffe4a5;box-shadow:0 0 0 4px #edc56833;animation:r-dot 3.8s ease-in-out infinite}
.r-stopnames{display:flex;flex-direction:column;justify-content:space-between;margin-left:21px;height:100%;font-size:11px;letter-spacing:.05em}
.r-traveler{position:absolute;width:15px;height:15px;border:3px solid white;border-radius:50%;background:#f2b653;left:20px;top:calc(16px + var(--progress,0)*78px);box-shadow:0 0 0 5px #91cbeb3b;transition:top .24s;pointer-events:none}
.r-btn-foot{background:#0a345b;padding:9px 11px;font:700 8.5px Arial;letter-spacing:.09em;display:flex;justify-content:space-between}
.r-btn-foot span{font-size:15px;line-height:9px;color:#c4e8f2}
.btt-train:hover{box-shadow:0 15px 42px #12416c77;transform:translateY(-4px)}
@keyframes r-dot{0%,100%{opacity:.6}50%{opacity:1;box-shadow:0 0 0 7px #edc56815}}
@keyframes r-drift{from{transform:translateX(-20px)}to{transform:translateX(30px)}}
@media (max-width:560px){.btt-train{left:12px;bottom:14px;width:138px}
.r-btn-body{height:104px}
.r-stopnames{font-size:10px}
.r-line{bottom:13px;top:14px}
.r-traveler{top:calc(13px + var(--progress,0)*59px)}}
.btt-train{box-sizing:border-box;width:184px;max-width:calc(100vw - 24px);overflow:hidden}
.r-btn-label{box-sizing:border-box;display:grid;grid-template-columns:32px minmax(0,1fr);gap:10px;padding:12px 11px 10px;min-width:0;width:100%;align-items:center}
.r-btn-label>span{min-width:0;display:block}
.r-btn-train-icon{width:32px;height:32px}
.r-btn-label b{font-size:18px;letter-spacing:0;white-space:nowrap}
.r-btn-label small{display:block;font:700 8.5px/1.3 Arial,sans-serif;letter-spacing:.04em;white-space:normal;max-width:100%;overflow-wrap:normal;margin-top:4px}
.r-btn-foot{align-items:center;gap:5px;font-size:9px;letter-spacing:.035em;white-space:normal}
.r-btn-foot span{flex:none}
@media (max-width:560px){.btt-train{width:160px;bottom:14px;left:10px}
.r-btn-label{grid-template-columns:29px minmax(0,1fr);gap:8px;padding:10px 9px 9px}
.r-btn-train-icon{width:29px;height:29px;font-size:18px}
.r-btn-label b{font-size:16px}
.r-btn-label small{font-size:7.7px;line-height:1.4}
.r-btn-body{height:113px}
.r-btn-foot{padding:9px 8px;font-size:8.1px}}
(()=>{'use strict';const el=document.querySelector('[data-btt]');if(!el)return;const reduce=matchMedia('(prefers-reduced-motion: reduce)');let raf=0;function update(){raf=0;const y=scrollY;let p=Math.min(1,Math.max(0,y/Math.max(1,document.documentElement.scrollHeight-innerHeight)));el.classList.toggle('is-visible',y>170);el.tabIndex=y>170?0:-1;el.setAttribute('aria-hidden',String(!(y>170)));el.style.setProperty('--progress',p.toFixed(3));}addEventListener('scroll',()=>{if(!raf)raf=requestAnimationFrame(update)},{passive:true});addEventListener('resize',update,{passive:true});el.addEventListener('click',()=>window.scrollTo({top:0,behavior:reduce.matches?'auto':'smooth'}));update()})();
How to Use
Insert the HTML button near the end of body, load the CSS, and include JavaScript using defer. Test scrolling, returning to the top, and positioning on mobile.
Use Case
Local railways, travel and tourism.
Technical Notes
Keep controls usable on touch devices. Test keyboard focus and reduced-motion preferences.
Mobile:Check tap target, visibility and positioning on small screens.
Browser:Designed for current Chrome, Edge, Firefox and Safari.
Reduced Motion:CSS animations respect the reduced-motion preference.