Skip Navigation

Accessible, Smooth Scroll-to-top Buttons with Little Code

Some of the original content in this post has been archived. This post previously showed how to create this component with a button element. I've since learned a better approach is to create this component with an anchor element and have it link to a Skip Navigation Link. You can read the archived post if you want to.

Scroll-to-top buttons are great for pages that are long enough to require a few scrolls to read everything. They're even better for extremely long pages. Scrolling is a lot of work for some users, especially on a mobile device. We (website creators) can greatly reduce the amount of work it takes to scroll our pages with surprisingly little effort on our part.

One example is the WAI-ARIA Authoring Practices, which is 140,923 pixels tall at the time of this writing. I'm not calling them out, it's just one of my favorite pages on the entire internet! It's a great resource for making accessible custom components.

Making a scroll-to-top button and making it scroll smoothly is probably a lot easier than you think. It's definitely a lot easier than I thought! The code bits I'm about to show are for React and SCSS, but you don't need to know either. The basic concepts here are for JavaScript and CSS. We'll also cover how to get rid of the smooth scrolling when it's an accessibility concern.The best part of this approach? It requires no focus management!


If you don't already have one, you need to add a Skip Navigation Link to your site. It's a quick, easy win as well, and the instructions below reference the one I use on my site. Not sure how to do it? Then read my post on How I Added a Skip Navigation Link to My Next.js Site!

The React Code

As you may have read in my post about Skip Navigation Links, I use Next.js Per-Page Layouts to help me render repeated content, such as navigation links, on many pages. The layout I use for the navigation header is where I placed a Skip Navigation Link and Scroll-to-Top link. The code looks something like this:

1import styles from './base-layout.module.css';
3const BaseLayout = ({ children }) => {
4  return (
5    <div>
6      <a href="#main-content" id="skip-navigation-link">
7        Skip Navigation
8      </a>
9      <SiteHeader />
10      <main id="main-content" tabIndex={-1}>
11        {children}
12      </main>
13      <a href="#skip-navigation-link">Back to Top</a>
14      <SiteFooter />
15    </div>
16  );

What's important from this code:

  1. The Scroll-to-Top link goes to the first focusable control on the page, which should be a Skip Navigation Link.
  2. The href of the Scroll-top-Top link matches the id of the Skip Navigation Link.

The SCSS Code

The code below takes an accessibility- and browser-support- first approach. Rather than making the default behavior for scrolling be smooth, an animation which can be physically harmful to some folks (including myself), it only applies smooth scrolling when two conditions are true:

  1. The prefers-reduced-motion query is supported (see the page)
  2. The value of the setting is no-preference (which unfortunately is the default value)
1html {
2  @media (prefers-reduced-motion: no-preference) {
3    scroll-behavior: smooth;
4  }

One thing to keep in mind is that scroll-behavior does not have full browser support. At the time of writing, the feature is still in development for Safari.


That's it! It really is just a few lines of code. After you add this, everyone will be able to quickly scroll to the top of your website. You've also made it so your site doesn't accidentally harm someone who can't tolerate animations! Well done. I am proud of you!

Back to Top