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.