The back-to-top button has been on the web for decades, and almost nobody bothers to do it well. A grey static arrow in the corner and on to the next thing. A shame, because it's exactly the kind of micro-interaction that separates a polished site from a put-together one.
Here are the four techniques I use—depending on the project—and when each one fits.
1. Animated GIF: the zero-code option
Works anywhere, including a WordPress image widget with a #top link. To make the GIF you can use SVGator (drag-and-drop, exports to animated SVG or GIF) or borrow a base icon from IconScout or Flaticon and animate it.
Pro: zero JS, zero CSS, copy-paste and done. Con: weight (any decent GIF is 30–80 KB), aliasing on Retina displays, and no control over when it appears.
2. Font Awesome + one animation class
If you're already loading Font Awesome, the job is practically done:
<a href="#top" class="back-to-top">
<i class="fas fa-arrow-up fa-bounce"></i>
</a>
The classes fa-bounce, fa-shake, fa-beat and fa-flip give you animation for free. No extra JS.
3. CSS + JavaScript: the classic, done right
This is what I default to when I want full control. The trick is to make the button appear with a soft fade-in once the user has scrolled enough, not pin it visible all the time:
const btn = document.getElementById('backToTop');
window.addEventListener('scroll', () => {
btn.classList.toggle('is-visible', window.scrollY > 300);
}, { passive: true });
btn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
Pair it with a CSS transition using a slightly elastic easing (cubic-bezier(.34, 1.56, .64, 1)) and you'll get that subtle pop that makes the button feel alive without being annoying.
4. Pure CSS, not a single gram of JS
If you're wrestling with a site where you can't inject JavaScript (an old CMS, an email render…), you can draw the arrow entirely with CSS:
.arrow-up {
width: 18px; height: 18px;
border-top: 3px solid currentColor;
border-right: 3px solid currentColor;
transform: rotate(-45deg);
}
Add a @keyframes animation and you've got movement without touching JS or Font Awesome.
Which one to use?
- Brand-critical site (storefront, portfolio): option 3. Full control, light, accessible.
- Blog already loading Font Awesome: option 2. Five minutes.
- WordPress without touching code: option 1. Fastest.
- Restricted environments (intranet, email): option 4.
This store runs option 3 (look at the bottom-right corner as you scroll). It takes 250 ms to appear, comes in with an elastic scale-up, and repositions itself when the cookie banner is visible. 30 lines of footer-scoped JS and CSS.