WebberUI

Section Snap Pin

Section pinning plus snap-scroll choreography — full-height sections snap one by one to fill the viewport, content reveals and exits in order, with side navigation dots.

This is a WebberUI Pro component

Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.

How to install Pro components →See the plans →

Loading preview…
npx shadcn@latest add "https://webberui.com/r/section-snap-pin.json?t=<install token>"

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

<SectionSnapPin />

Installation

npx shadcn@latest add "https://webberui.com/r/section-snap-pin.json?t=<install token>"

Or, once registries are configured in components.json, install it as @webberui/section-snap-pin.

Usage

SectionSnapPin is itself a vertical snap scroll container, so give it a fixed height (for example h-[80vh] or h-screen). Put several SnapSection elements inside it, and inside each section wrap the elements you want choreographed in SnapReveal.

import {
  SectionSnapPin,
  SnapSection,
  SnapReveal,
} from "@/components/ui/section-snap-pin";

<SectionSnapPin className="h-screen">
  <SnapSection label="Overview" className="bg-neutral-950 text-white">
    <SnapReveal order={0}>
      <span className="text-xs tracking-widest uppercase">01</span>
    </SnapReveal>
    <SnapReveal order={1}>
      <h2 className="text-4xl font-semibold">Snapping sections</h2>
    </SnapReveal>
  </SnapSection>

  <SnapSection label="Choreography" className="bg-indigo-600 text-white">
    <SnapReveal order={0} from="left">
      <h2 className="text-4xl font-semibold">Revealed in order</h2>
    </SnapReveal>
  </SnapSection>
</SectionSnapPin>

Props

SectionSnapPin

PropTypeDefaultDescription
childrenReactNodeOne or more SnapSection elements
snap"mandatory" | "proximity""mandatory"Snap strength
indicatorbooleantrueWhether to show the side section navigation dots
indicatorPosition"left" | "right""right"Position of the navigation dots
onActiveChange(index: number) => voidCallback fired when the snapped section changes
aria-labelstring"Section snap scrolling"Accessible label for the scroll area
classNamestringThe outermost container (set the fixed height here)

SnapSection

PropTypeDefaultDescription
childrenReactNodeSection content, usually several SnapReveal elements
labelstringSection name, used as the navigation dot tooltip and the accessible label
align"start" | "center" | "end""center"Vertical alignment of the content within the section
classNamestringAdditional styling (background, text color, and so on)

SnapReveal

PropTypeDefaultDescription
childrenReactNodeThe content to choreograph into view
ordernumber0Reveal order; multiplied by stagger to get the delay
from"up" | "down" | "left" | "right" | "none""up"Direction of the reveal offset
distancenumber24Reveal offset distance (px)
staggernumber0.08Interval between order steps (seconds)
durationnumber0.6Duration of a single unit's animation (seconds)
classNamestringAdditional styling

How it works

  • SectionSnapPin achieves snapping with CSS Scroll Snap (snap-y + snap-mandatory / snap-proximity), leaving scroll performance to the browser's native implementation.
  • Each SnapSection uses an IntersectionObserver (with the scroll container as root) to decide whether it is more than 55% visible and therefore the primary section; that state is passed through context to the inner SnapReveal elements to trigger the reveal, with an exit when it leaves — so scrolling back in choreographs it again.
  • The thin rail beside the navigation dots fills in real time, tracking the container's scroll progress with useScroll.
  • The scroll container's native scrollbar is hidden with styling; snapping and the navigation dots provide the sense of position instead.

Accessibility

  • The scroll container carries role="region", aria-label, and tabIndex, so it can be focused by keyboard and sections switched with / , PageUp / PageDown, and Home / End.
  • The navigation dots are native <button> elements with an aria-label (taken from the section label) and aria-current; they work by keyboard and show a tooltip.
  • When the user has "reduce motion" enabled at the system level, smooth scrolling becomes an instant jump and SnapReveal renders its content statically with no reveal offset.

On this page