Skip to main content
WireKit
Copy for LLM

Scroll to Top

The <x-wirekit::scroll-to-top> component renders a fixed-position button that appears after the user scrolls past a configurable viewport depth. Clicking it smooth-scrolls back to the top of the page.

Basic Usage

Scroll to Top Button
{{-- Place once in your layout, typically at the end of <body> --}}
<x-wirekit::scroll-to-top />

The button is invisible by default and fades in once the user scrolls past 1.5x the viewport height. It positions itself in the bottom-right corner.

Custom Threshold

The threshold prop controls when the button appears, expressed as a multiplier of the viewport height:

{{-- Appear early (after half a screen of scrolling) --}}
<x-wirekit::scroll-to-top :threshold="0.5" />

{{-- Appear late (after 3 full screens) --}}
<x-wirekit::scroll-to-top :threshold="3" />

{{-- Default: 1.5x viewport height --}}
<x-wirekit::scroll-to-top />
Value Meaning
0.5 After scrolling half the viewport
1 After scrolling one full viewport
1.5 After scrolling 1.5 viewports (default)
3 After scrolling 3 viewports

Size Variants

<x-wirekit::scroll-to-top size="sm" />  {{-- Compact --}}
<x-wirekit::scroll-to-top size="md" />  {{-- Default --}}
<x-wirekit::scroll-to-top size="lg" />  {{-- Large, touch-friendly --}}

Position

The button defaults to the bottom-right corner. Use the position prop to move it:

<x-wirekit::scroll-to-top position="bottom-right" />  {{-- Default --}}
<x-wirekit::scroll-to-top position="bottom-left" />
<x-wirekit::scroll-to-top position="top-right" />
<x-wirekit::scroll-to-top position="top-left" />

The offset from the edge uses the --padding-wk-x-lg design token, so it adapts to your theme.

How It Works

  1. An Alpine.js scroll event listener (with { passive: true }) tracks the scroll position
  2. requestAnimationFrame throttles updates to avoid layout thrashing
  3. When window.scrollY > window.innerHeight * threshold, the button fades in via x-transition
  4. Clicking calls window.scrollTo({ top: 0, behavior: 'smooth' }) for native smooth scrolling
  5. The destroy() lifecycle hook removes the scroll listener on component teardown (e.g. Livewire navigation)

Props

Prop Type Default Description
threshold float 1.5 Viewport multiplier — button appears after scrolling threshold * viewportHeight pixels
size string 'md' 'sm', 'md', 'lg'
position string 'bottom-right' 'bottom-right', 'bottom-left', 'top-right', 'top-left'
scope string|null null Scoped personalization key

Accessibility

  • <button type="button"> — semantically correct, keyboard-focusable
  • aria-label="Scroll to top" — announces the action to screen readers
  • Visible focus ring via focus-visible:ring-* design tokens
  • SVG icon is aria-hidden="true" — purely decorative
  • Smooth scrolling respects prefers-reduced-motion: reduce (browsers disable behavior: 'smooth' automatically)
  • x-cloak prevents flash of the button before Alpine initializes

Keyboard Interaction

Key Action
Tab Move focus to the button
Enter / Space Activate the button

Pitfalls

  • Don't show scroll-to-top above the fold. The component auto-shows after 200vh of scroll by default — overriding the threshold to "always visible" defeats the disclosure pattern.

Design Tokens

Token Purpose
--color-wk-accent Button background
--color-wk-accent-fg Arrow icon color
--color-wk-accent-hover Hover background
--shadow-wk-lg Drop shadow for elevation
--z-wk-sticky Z-index (above content, below overlays)
--size-wk-sm / md / lg Button dimensions per size
--padding-wk-x-lg Offset from viewport edge
--ring-wk-width Focus ring width
--transition-wk-duration Hover/show animation speed

Customization

Override defaults in config/wirekit.php:

'components' => [
    'scroll-to-top' => ['size' => 'lg'],
],

Personalization

Override classes globally:

use Pushery\WireKit\WireKit;

WireKit::personalize('scroll-to-top', [
    'base' => 'fixed z-50 rounded-full bg-neutral-900 text-white shadow-xl cursor-pointer',
]);

Further Reading

Was this page helpful?

Thanks — that helps.

Voting requires cookies or local storage. What we store