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
{{-- 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
- An Alpine.js
scrollevent listener (with{ passive: true }) tracks the scroll position requestAnimationFramethrottles updates to avoid layout thrashing- When
window.scrollY > window.innerHeight * threshold, the button fades in viax-transition - Clicking calls
window.scrollTo({ top: 0, behavior: 'smooth' })for native smooth scrolling - 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-focusablearia-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 disablebehavior: 'smooth'automatically) x-cloakprevents 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
- MDN:
window.scrollTo()— the smooth scroll API - MDN:
scrollevent — passive listener for performance - MDN:
requestAnimationFrame— throttling pattern used - MDN:
prefers-reduced-motion— browsers auto-disable smooth scrolling