# Scroll to top

Scroll to top is a neobrutalist React component from Cruise UI. A square button in a bottom corner that appears on long pages and takes you, and your keyboard focus, back to the top.

Install (with the @cruiseui registry in components.json, see Installation):

```bash
npx shadcn@latest add @cruiseui/scroll-top
```

Import: `import { CruiseScrollTop } from "@/components/cruise/scroll-top";`

## When to use

Long pages: docs, feeds, component galleries.

## When not to

Short pages that fit in two screens, or as a replacement for good navigation.

## Usage

- Put <div id="top" tabIndex={-1} /> first in <body> (or pass targetId), so focus has somewhere to land.
- Use one per page. It moves up above a toast stack in the same corner.

## Example (React)

```tsx
"use client";

import { CruiseScrollTop } from "@/components/cruise/scroll-top";

export default function ScrollTopExample() {
  return (
    <div className="flex flex-col gap-3">
      <p>Scroll down this page: a labelled button appears bottom-left after 300px.</p>
      {/* Once per page. Put <div id="top" tabIndex={-1} /> first in <body>. */}
      <CruiseScrollTop position="bottom-left" showAfter={300} label="Top" />
    </div>
  );
}
```

## HTML

Needs theme.css (https://cruiseui.dev/theme.css).

```html
<body>
  <div id="top" tabindex="-1"></div>
  …
  <button type="button" class="brutal scroll-top" data-position="bottom-right" aria-label="Back to top">
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" aria-hidden="true"><path d="m5 12 7-7 7 7"/><path d="M12 19V5"/></svg>
  </button>
  <script>
    const toTop = document.querySelector(".scroll-top");
    let ticking = false;
    addEventListener("scroll", () => {
      if (ticking) return;
      ticking = true;
      requestAnimationFrame(() => { toTop.toggleAttribute("data-visible", scrollY > 600); ticking = false; });
    }, { passive: true });
    toTop.addEventListener("click", () => {
      const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
      scrollTo({ top: 0, behavior: reduce ? "instant" : "smooth" });
      document.getElementById("top").focus({ preventScroll: true });
    });
  </script>
</body>
```

## Props: CruiseScrollTop

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `position?` | `"bottom-left" \| "bottom-right"` | `"bottom-right"` | — |
| `offset?` | `number` | `16` | Distance from the screen edges, in px (the safe area is added). |
| `showAfter?` | `number` | `600` | Pixels scrolled before it appears. |
| `label?` | `string` | — | Optional text next to the icon, e.g. "Top" (the name stays "Back to top"). |
| `targetId?` | `string` | `"top"` | id of the element that gets focus at the top (give it tabIndex={-1}). |
| `className?` | `string` | — | — |

## Accessibility

- A real <button> named "Back to top" (also when it shows the "Top" text).
- After scrolling up, focus moves to the #top target, so the next Tab starts at the top of the page.
- Hidden from the tab order until it appears; no smooth scroll or slide under reduced motion.
