# ScrollToTop

> Back to the top of the page, set by the shell or by hand.

A link to the `top` fragment, which the HTML specification reserves for the
top of the document when no element carries that identifier. Nothing to add to
the template, nothing to load.

## It is already there

**The button is set on every page**, by the shell. Back-to-top is page
furniture, not content: writing it in every file would mean repeating it
everywhere, and forgetting it somewhere.

Look at the bottom right of this page while scrolling: it is there without
anything having asked for it.

To remove it from the whole site:

```js
// docpensieve.config.mjs
scrollToTop: false,
```

## The component, for special cases

`ScrollToTop` stays available when you want a button **in the flow of the
text**, with its own content — a pointer at the end of a chapter, for
instance.

<ForTheme framework="tailwind">

<div className="flex flex-wrap items-center gap-3">
  <ScrollToTop className="!static !inline-flex !w-auto !shadow-none px-4" label="Top of page">
    Top
  </ScrollToTop>
  <ScrollToTop
    className="!static !inline-flex !w-auto !shadow-none px-4"
    label="Back to the summit"
  >
    ↑ Summit
  </ScrollToTop>
  <ScrollToTop
    className="!static !inline-flex !w-auto !shadow-none px-4 border-indigo-400 text-indigo-700"
    label="Start reading again"
  >
    Start again
  </ScrollToTop>
</div>

```mdx
<ScrollToTop className="!static !inline-flex !w-auto" label="Top of page">
  Top
</ScrollToTop>
```

All three carry `!static`: that is what takes them out of the corner of the
screen to set them in the text. **Without this class, they would stack** on
top of the shell's button, all in the same place.

</ForTheme>
<ForTheme framework="custom">

<div className="row">
  <ScrollToTop className="inline-button" label="Top of page">
    Top
  </ScrollToTop>
  <ScrollToTop className="inline-button" label="Back to the summit">
    ↑ Summit
  </ScrollToTop>
  <ScrollToTop className="inline-button accent-text" label="Start reading again">
    Start again
  </ScrollToTop>
</div>

```mdx
<ScrollToTop className="inline-button" label="Top of page">
  Top
</ScrollToTop>
```

```css
.inline-button {
  position: static;
  display: inline-flex;
  inline-size: auto;
  box-shadow: none;
  padding-inline: 1rem;
}
```

All three carry `inline-button`, from `theme/99-docpensieve.css`: its
`position: static` takes them out of the corner of the screen to set them in
the text. **Without this class, they would stack** on top of the shell's
button, all in the same place.

</ForTheme>

The accessible name stays set by `label`: without it, the link would announce
itself by its appearance alone.

## Appearing

It is driven by `animation-timeline: scroll()`: scrolling drives the
animation, with no event listener. Where the browser does not know that
property yet, the button simply stays visible — always there is better than
never there.

Smooth scrolling follows the same caution: it only applies to the pages that
carry this button, and only if the system does not ask for less motion.

## Room to breathe

Some text, so that the page is long enough to scroll and the appearance of the
button is noticeable.

The generator produces a static site: each page is a complete HTML file,
served as is. No hydration step, no bundle to download before reading. That is
what lets a component like this one be nothing but a link and two style rules.

A version lives on its own branch. The working branch keeps the sources, the
version branch keeps the output. The two never mix, and going back to an old
version does not require rebuilding anything.

The shipped components all follow the same rule: they provide the structure,
and the look is set at use. None of them embeds a hard-coded colour; all of
them read the tokens of the active theme.

When a page needs interaction, the answer is first sought in native elements —
`details` for expanding, a link for moving, a CSS transition for appearing.
Only after that, and rarely, would a script be justified.

This constraint has a happy side effect: a page stays readable in ten years,
because there is nothing that can stop working. No dependency to update, no
interface that breaks with the next browser.

And a less happy side effect, better known in advance: what depends on the
moment is frozen at build time. A page that shows “the offer ends tomorrow”
will still say so in six months if the site has not been rebuilt. A scheduled
build is enough to keep it right.
