Accessibility pattern · Navigation

Back to top

The button appears once you are past the first screen, but never takes focus or interrupts; it is the last thing in the Tab order, where it is needed. Pressing it scrolls to the top, instantly if you asked for reduced motion, and moves focus to the first heading, so the next Tab starts from the top too.

WCAG criteria
6
Keyboard rules
3
Checked with
axe, keyboard and the inspector

Live demo

Try it

Use it with a mouse, a keyboard or a screen reader. The inspector beside it shows what the browser tells assistive technology as you go: focus, state changes and announcements.

Release notes for Khata

What changed in the app, newest first.

Version 4.2

  • Added Split a bill between up to ten people.
  • Added Reminders on WhatsApp for unpaid bills.
  • Fixed Totals no longer round twice on GST invoices.

Version 4.1

  • Added A dark theme that follows your phone's setting.
  • Changed The add button is larger and sits within reach of your thumb.
  • Fixed Screen readers now read amounts in rupees, not as plain numbers.

Version 4.0

  • Added Hindi, Tamil and Bengali.
  • Changed Reports open in half the time.
  • Removed The old CSV export. Use the Excel export instead.

Version 3.9

  • Added Search across all your notes.
  • Fixed Long names are no longer cut off on small phones.

Version 3.8

  • Added Export a month of entries as a PDF.
  • Fixed Changes made offline sync when you reconnect.

Version 3.7

  • Added Attach a photo of the receipt to any entry.
  • Fixed The app no longer asks for your PIN twice after an update.

Version 3.6

  • Changed Amounts use Indian digit grouping, like 1,00,000.
  • Fixed Every icon button now has a name for screen readers.

Older releases

The demo works without JavaScript only as far as its HTML does; the inspector needs JavaScript.

Keyboard

Keys it answers to

Every action works without a pointer.

KeyWhat it does
TabMoves to the scroll area, then through the links in it, then to Back to top once it is showing.
Arrow Down or Page Down or Space or EndWith focus on the scroll area, scrolls the notes; Back to top appears once you are past the first screen.
Enter or SpaceOn Back to top, scrolls to the top and moves focus to the first heading.

Screen readers

What it announces

Written from the roles, names and states in the markup.

WhenExpected announcement
Tab reaches the scroll areaRelease notes for Khata, region
The button appearsNothing: it appears without taking focus or interrupting
Tab reaches the buttonBack to top, button
Enter on Back to topRelease notes for Khata, heading level 3

These are expected announcements, not recordings. Wording and order differ between screen readers and browsers.

Code

Copy the code

The exact files this demo runs on. The styles are served with a prefix that keeps this site's own styles out of the demo; what you copy is the original.

The markup

<div class="ap-backtop" data-ap-backtop>
  <div class="ap-backtop__scroller" role="region" aria-labelledby="btt-name" tabindex="0">
    <div class="ap-backtop__mark" aria-hidden="true"></div>
    <article class="ap-backtop__article">
      <h3 class="ap-backtop__name" id="btt-name" tabindex="-1">Release notes for Khata</h3>
      <p class="ap-backtop__intro">What changed in the app, newest first.</p>

      <section class="ap-backtop__release">
        <div class="ap-backtop__meta">
          <h4 class="ap-backtop__version" id="btt-42">Version 4.2</h4>
          <time class="ap-backtop__date" datetime="2026-09-30">30 September 2026</time>
        </div>
        <ul class="ap-backtop__changes">
          <li><span class="ap-backtop__tag ap-backtop__tag--added">Added</span> <span>Split a bill between up to ten people.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--added">Added</span> <span>Reminders on WhatsApp for unpaid bills.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--fixed">Fixed</span> <span>Totals no longer round twice on GST invoices.</span></li>
        </ul>
      </section>

      <section class="ap-backtop__release">
        <div class="ap-backtop__meta">
          <h4 class="ap-backtop__version" id="btt-41">Version 4.1</h4>
          <time class="ap-backtop__date" datetime="2026-08-12">12 August 2026</time>
        </div>
        <ul class="ap-backtop__changes">
          <li><span class="ap-backtop__tag ap-backtop__tag--added">Added</span> <span>A dark theme that follows your phone's setting.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--changed">Changed</span> <span>The add button is larger and sits within reach of your thumb.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--fixed">Fixed</span> <span>Screen readers now read amounts in rupees, not as plain numbers.</span></li>
        </ul>
      </section>

      <section class="ap-backtop__release">
        <div class="ap-backtop__meta">
          <h4 class="ap-backtop__version" id="btt-40">Version 4.0</h4>
          <time class="ap-backtop__date" datetime="2026-07-01">1 July 2026</time>
        </div>
        <ul class="ap-backtop__changes">
          <li><span class="ap-backtop__tag ap-backtop__tag--added">Added</span> <span>Hindi, Tamil and Bengali.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--changed">Changed</span> <span>Reports open in half the time.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--removed">Removed</span> <span>The old CSV export. Use the Excel export instead.</span></li>
        </ul>
      </section>

      <section class="ap-backtop__release">
        <div class="ap-backtop__meta">
          <h4 class="ap-backtop__version" id="btt-39">Version 3.9</h4>
          <time class="ap-backtop__date" datetime="2026-05-20">20 May 2026</time>
        </div>
        <ul class="ap-backtop__changes">
          <li><span class="ap-backtop__tag ap-backtop__tag--added">Added</span> <span>Search across all your notes.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--fixed">Fixed</span> <span>Long names are no longer cut off on small phones.</span></li>
        </ul>
      </section>

      <section class="ap-backtop__release">
        <div class="ap-backtop__meta">
          <h4 class="ap-backtop__version" id="btt-38">Version 3.8</h4>
          <time class="ap-backtop__date" datetime="2026-04-02">2 April 2026</time>
        </div>
        <ul class="ap-backtop__changes">
          <li><span class="ap-backtop__tag ap-backtop__tag--added">Added</span> <span>Export a month of entries as a PDF.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--fixed">Fixed</span> <span>Changes made offline sync when you reconnect.</span></li>
        </ul>
      </section>

      <section class="ap-backtop__release">
        <div class="ap-backtop__meta">
          <h4 class="ap-backtop__version" id="btt-37">Version 3.7</h4>
          <time class="ap-backtop__date" datetime="2026-02-18">18 February 2026</time>
        </div>
        <ul class="ap-backtop__changes">
          <li><span class="ap-backtop__tag ap-backtop__tag--added">Added</span> <span>Attach a photo of the receipt to any entry.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--fixed">Fixed</span> <span>The app no longer asks for your PIN twice after an update.</span></li>
        </ul>
      </section>

      <section class="ap-backtop__release">
        <div class="ap-backtop__meta">
          <h4 class="ap-backtop__version" id="btt-36">Version 3.6</h4>
          <time class="ap-backtop__date" datetime="2026-01-09">9 January 2026</time>
        </div>
        <ul class="ap-backtop__changes">
          <li><span class="ap-backtop__tag ap-backtop__tag--changed">Changed</span> <span>Amounts use Indian digit grouping, like 1,00,000.</span></li>
          <li><span class="ap-backtop__tag ap-backtop__tag--fixed">Fixed</span> <span>Every icon button now has a name for screen readers.</span></li>
        </ul>
      </section>
      <p class="ap-backtop__more"><a href="/khata/changelog/archive/">Older releases</a></p>
    </article>
    <div class="ap-backtop__dock">
      <button type="button" class="ap-backtop__btn">
        <svg class="ap-backtop__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 19V5M6 11l6-6 6 6"/></svg>
        <span>Back to top</span>
      </button>
    </div>
  </div>
</div>

WCAG 2.2

What it meets

The success criteria this pattern takes care of, and how.

  • 2.1.1 Keyboard Level A

    The scroll area takes focus so it scrolls from the keyboard, and the button is a real button.

  • 2.3.3 Animation from Interactions Level AAA

    The scroll back up is smooth only when the system allows motion; with reduced motion it is instant.

  • 2.4.3 Focus Order Level A

    Focus moves to the first heading with the scroll, so the next Tab continues from the top instead of from the bottom.

  • 2.4.7 Focus Visible Level AA

    The button shows a focus ring that clears 3:1 against the content behind it.

  • 2.4.11 Focus Not Obscured (Minimum) Level AA

    Bottom padding and scroll-padding keep the last lines, and any link that takes focus, clear of the floating button.

  • 2.5.8 Target Size (Minimum) Level AA

    The button is 44 pixels high with a visible label, well over the 24 pixel minimum.

Usage

When to use it

Use it

  • Long pages or panels people scroll through, like release notes, policies or long lists.
  • Phones, where getting back to the top of a long page takes many swipes.

Use something else

  • Pages that fit in two screens: the button is clutter.
  • Instead of a contents list, when people need to reach a section rather than the top.
  • Pages whose header stays on screen anyway, with nothing at the top worth returning to.

Common failures

How it usually goes wrong

  • Focus left at the bottom

    If the page scrolls up but focus stays on the button, the next Tab jumps straight back down. Focus moves to the first heading here.

  • A button that takes focus when it appears

    Moving focus to the button, or announcing it, interrupts reading. It just appears, and waits to be reached.

  • Invisible but still focusable

    A button faded out with opacity alone can still be tabbed to, and lands people on nothing. visibility: hidden takes it out of the Tab order.

  • Covering what you are reading

    A floating button can hide the last line or a focused link. Padding at the end and scroll-padding keep both clear of it.

  • An arrow with no words

    An icon-only arrow is easy to miss and has to be named in code. The word is on the button, so everyone gets the same label.

  • Smooth scrolling for everyone

    A long animated scroll can make people with vestibular disorders unwell. It is instant when the system asks for reduced motion.

Notes

Building it

  • The button is the last thing in the scroll area, so it comes after the content in the Tab order; position: sticky keeps it in view while you read.
  • An IntersectionObserver watches an empty marker as tall as the threshold, so nothing runs while you scroll.
  • The button stays while it has focus, even back above the threshold, so focus is never left on something that has gone.
  • For a whole page, the same button can scroll the window and focus the h1; a link to the top heading also works without JavaScript.

Sources: Understanding WCAG 2.2: Focus not obscured (minimum) · MDN: prefers-reduced-motion

Checked with axe in light and dark themes, at desktop and phone widths, and by keyboard. Not yet tested with every screen reader and browser pair. Report a correction