Back to Top Button
A round button that bounces in once the visitor has scrolled well down, and glides them back to the top, focus included.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Utilities has 43 effects, including 3 free. Explore the category →
3 usage examples



How it works
The .btt-btn button stays hidden (transparent and visibility: hidden, so out of the keyboard path) until the scroll goes past data-btt-threshold, 400 px by default. Beyond that it gets the btt-visible class and comes in on a 0.4 s elastic curve; it leaves again when you scroll back above the threshold, unless it has keyboard focus at that moment.
On click (or Enter, Space), the page scrolls up with scrollTo({ top: 0, behavior: 'smooth' }), then focus is placed at the top: on the whole page by default, or on the element named by data-btt-focus if you set it (a heading, a search field). That element gets a temporary tabindex="-1" if it is not focusable, removed as soon as it loses focus. The next Tab therefore starts from the top, not from the bottom of the page.
With no setting, the button is fixed at the bottom right of the window (24 px from the edges, --btt-right and --btt-bottom). For a block that scrolls on its own (a panel, a comments window), give its selector to data-btt-target and make the root the block's last child: it becomes position: sticky at the bottom of the block, with no height of its own, and the button stays in its corner. Each root is independent.
Accessibility
- Accessible name: the icon is hidden from screen readers (
aria-hidden) and the button carriesaria-label="Back to top of page". Adapt it for a block ("Back to the start of the log"). - Focus moved: after scrolling up, focus goes to the top of the page or block, so a keyboard user is not left stranded at the bottom. A hidden button can never be reached with Tab.
- Reduced motion: under
prefers-reduced-motion: reduce, the jump to the top is instant, the button appears with a plain 0.2 s fade, no bounce, and the arrow stops hopping on hover. - Visible focus: a 2 px light ring on
:focus-visible, on top of the lift. In Safari on Mac, Tab only stops on buttons if the system keyboard option allows it: that is a Mac setting, not a flaw of the button. - What the button does not replace: a skip link or a table of contents are still useful at the top; the button is for going back, not for navigating.
Browser compatibility
Dependency-free JavaScript. Smooth scrolling through scrollTo({ behavior: 'smooth' }) has been around since Chrome 61, Firefox 36 and Safari 15.4; before that the jump is instant. focus({ preventScroll }): Chrome 64, Firefox 68, Safari 15; Chrome Android ignores it, harmlessly, since the target is already at the top. Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): a page button and a block button in the same page, code pasted twice, threshold, click, Enter on the keyboard, touch, 390 px, reduced motion, no name added to window, 0 errors.
Without JavaScript the button stays hidden: the page works without it. In Safari 15 to 15.3, the jump to the top is instant instead of gliding.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0736-root" data-btt-threshold="400">
<button class="btt-btn" type="button" aria-label="Back to top of page">
<svg aria-hidden="true">…</svg>
</button>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Show threshold (HTML — data-btt-threshold) |
400 (px) |
Distance scrolled before the button comes in. On a long article, 800 to 1200 px keeps it from showing on the first screen. |
Followed block (HTML — data-btt-target) |
absent: the page | CSS selector of a block that scrolls on its own. Then make the root that block's last child. |
Focus destination (HTML — data-btt-focus) |
the page (or the followed block) | Selector of the element that gets focus after the return: #search, h1… |
Position (CSS — --btt-right, --btt-bottom) |
24px and 24px |
Distance from the right edge and the bottom of the window (or block). Raise --btt-bottom if a cookie banner sits at the bottom. |
Size (CSS — width, height of .btt-btn) |
50px |
40 px for a panel, 32 px for a small window; shrink the SVG icon in the same proportion. |
Colours (CSS — background of .btt-btn) |
gradient #6366f1 → #8b5cf6 |
Your brand colour; keep enough contrast between the white arrow and the background. |
Refresh (JS — root.fx0736.update()) |
— | Re-checks whether the button should show right away, for instance after changing the threshold from a script. |
FAQ
div with overflow: auto) instead of the page. In that case, give that container's selector to data-btt-target and put the root inside it, last.data-btt-target. Each button only watches its own block. A page-level button can live alongside them.