Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

BottomSheet

A snap-point bottom sheet with three resting heights — peek (always visible), half, and full. The user drags the sheet vertically; a velocity-aware flick advances or retreats one snap, otherwise it springs to the nearest level. A backdrop scrim darkens as the sheet rises, and half/full content crossfades in at its level.

Import

import { BottomSheet } from "@roger-emerson/ploxum-primitives";

Usage

The sheet is absolutely positioned against the nearest positioned ancestor, so wrap it in a bounded relative container.

<div className="relative h-[480px] overflow-hidden">
  <BottomSheet
    full={<RouteDetail />}
    half={<DriverSummary />}
    peek={<p>Trip summary · 24 min away</p>}
  />
</div>

Controlled

Pass snap plus onSnapChange to drive the level externally; omit them (optionally with defaultSnap) for uncontrolled behavior.

const [snap, setSnap] = useState<0 | 1 | 2>(0);
 
<BottomSheet onSnapChange={setSnap} peek={peek} half={half} full={full} snap={snap} />

Props

PropTypeDefaultDescription
peekReactNodeContent always visible, even when collapsed. Required.
halfReactNodeContent that crossfades in at the half snap (level 1) and above.
fullReactNodeContent that crossfades in at the full snap (level 2).
snap0 | 1 | 2Controlled snap level. Pair with onSnapChange.
defaultSnap0 | 1 | 20Initial resting level when uncontrolled.
onSnapChange(snap: 0 | 1 | 2) => voidFired whenever the resting snap level changes.
heightnumber400Total sheet height (px) at the full snap.
halfHeightnumber240Sheet height (px) at the half snap.
peekHeightnumber96Height (px) of the always-visible peek strip.
classNamestringMerged onto the sheet panel.
refRef<HTMLDivElement>Forwarded to the sheet panel.

Design rules

  • Three snaps only: 0 peek, 1 half, 2 full. A flick faster than 300px/s advances or retreats one level; a slow release snaps to the nearest offset.
  • The scrim is transparent at peek and dims progressively (0 → 0.15 → 0.35). It only intercepts pointer events above peek, and clicking it collapses straight back to peek.
  • Keyboard: the panel is focusable (role="dialog"); ArrowUp expands one snap, ArrowDown collapses one snap.
  • Reduced motion (prefers-reduced-motion): springs and crossfades become instant (duration 0). Dragging is preserved, but the settle is snapped, not animated.
  • Spring physics use the ploxum springs.snappy token; the grab handle and scrim are decorative (aria-hidden).

Storybook

Open BottomSheet in Storybook ↗