Skip to content

ExpressiveCSS

Browse documentation

Back to top

A link that appears once a long page has scrolled down, and returns to the top with focus on the main content.

View as Markdown

Use .back-to-top for a link that appears once a long page has scrolled down, and takes the reader back to the top.

Write an <a> whose href names the page's main content, give it an aria-label, and add the FAB classes button circle extra for its look. AutoInit() starts every .back-to-top. The link stays hidden until the page has scrolled one viewport height, then sits in the bottom inline-end corner. Activating it scrolls to the top and moves focus to the element the href names. Scroll this page down to see the one below.

<main id="main">…</main>
<a class="back-to-top button circle extra" href="#main" aria-label="Back to top">
  <span class="material-symbols" aria-hidden="true">arrow_upward</span>
</a>

Without the script, the link is always visible and jumps to its target, as any in-page link does. Until the script starts it is also visible at the top of the page, so call AutoInit() before the first paint, from a script at the end of the body.

Where focus goes

Focus moves to the href target, so the next Tab reaches the first control after it. A target that does not take focus, such as a <main>, gets tabindex="-1" until it loses focus again. An href of # or #top, or one that names no element, focuses the body instead. The address bar keeps its fragment, since the script replaces the jump. A click with Ctrl, Meta, Shift or Alt, or one a page handler already prevented, is left to the browser.

While hidden, the link is inert: Tab skips it, it takes no clicks, and screen readers do not list it. That state belongs to the script, so don't write inert in the markup. A focused link stays until focus leaves, so pressing Home on it does not drop focus to the body.

What scrolls

The link watches the window's scroll, through an IntersectionObserver on an empty element at the threshold, so scrolling runs no script. The threshold is measured from the top of the body, so a body with position: relative adds its own top margin. A pane, dialog or scroll area that scrolls on its own is not supported.

The scroll to the top is smooth, and instant under reduced motion. The link fades in and hides at once, and under reduced motion it also appears at once.

Options

Name Type Default Description
threshold String '100vh' How far the page scrolls before the link shows, as a CSS length from the top of the page. Viewport units follow window resizes. A value that is not a CSS length falls back to '100vh' with a warning.
<a class="back-to-top button circle extra" href="#main" aria-label="Back to top"
   data-back-to-top-options='{"threshold": "400px"}'>…</a>

Methods

const link = Expressive.BackToTop.getInstance(document.querySelector('.back-to-top'));
link.destroy(); // removes the observer and the threshold element, and shows the link again

Tokens

Override these on the .back-to-top. A .fab-menu sits in the same corner, so a page with both raises one of them, for example with --md-comp-back-to-top-offset: 88px.

Token Default
--md-comp-back-to-top-offset16px
--md-comp-back-to-top-z997
--md-comp-back-to-top-durationthe fast effects spring's duration
--md-comp-back-to-top-easingfast effects spring

No matching pages

  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.

    Default source: Mist Iris.

    ExpressiveCSS presets: Lagoon, Harbor, Breeze, Cobalt, Iris.

    Bold

    Soft

    Mist

    Glow