Sheet
Motion's bottom-sheet choreography on the native HTML dialog element, with a 50px fade-up entrance plus drag and flick dismissal. Keep SheetBackdrop and SheetPanel as direct Sheet children.
API reference
Components
<Sheet />
The coordinating root: adapts controlled or uncontrolled state to a native <dialog>, shares the drag position and element refs through context, and keeps the overlay mounted until its Motion exit animation completes.
children?
The trigger, backdrop, panel and your resting content. Keep the backdrop and panel as direct children so the root can place them in the dialog.
open?
Controlled open state. Omit for uncontrolled (drive it via SheetTrigger / SheetClose / the drag).
defaultOpen?
Initial open state when uncontrolled. Defaults to false.
onOpenChange?
Called whenever the sheet requests an open-state change (trigger, close button, scrim, Escape, drag-dismiss). Required to observe controlled changes.
<SheetTrigger />
The styled trigger that opens the sheet: a primary fill with the theme's snap hover/tap feel and the shared focus ring.
children?
The trigger's contents (label, and optionally an icon).
className?
Merged onto the trigger button, last, so it wins.
<SheetBackdrop />
The dimming scrim behind the open sheet. Its opacity follows the sheet's drag position so it un-dims as the sheet is pulled down.
className?
The scrim surface class, merged onto the shared Backdrop. Defaults to the universal bg-black modal scrim; pass a section-local veil (e.g. a color-mix(in srgb, var(--background) 72%, transparent) class) to tint it to your ground colour in both modes.
label?
The scrim's accessible label. When set, the scrim is a keyboard-reachable dismiss button; omit it for a decorative scrim.
<SheetPanel />
The draggable spring panel. Fades up 50px on open (theme gentle), is draggable on the y-axis with a bottom-weighted elastic, and dismisses on a downward drag past dismissOffset OR a flick past dismissVelocity. Modal semantics come from the native dialog owned by Sheet.
children?
The sheet's contents - your handle, header, close button and rows.
dismissOffset?
Downward drag distance (px) past which releasing dismisses the sheet. Defaults to 92.
dismissVelocity?
Downward flick velocity (px/s) past which releasing dismisses the sheet. Defaults to 840.
labelledBy?
The id of the element that labels the dialog, wired to aria-labelledby. Put this same id on your sheet's heading. Defaults to "velocity-sheet-title".
className?
Merged onto the panel, last, so it wins.
<SheetHandle />
The decorative grab affordance at the top of the sheet: a short pill, aria-hidden (a mouse/touch handle, not a control). Its tone is a faint, below-AA derived ink - fine here because the mark is decorative and hidden from assistive tech.
className?
Merged onto the centring row, last, so it wins.
<SheetClose />
The close button, top-right of the sheet. Carries the close ref, so focus lands here when the sheet opens; the theme's snap colour fade on hover and the shared focus ring. Renders a default × glyph, overridable via children.
children?
The button's contents. Defaults to a close (×) glyph.
label?
The accessible label. Defaults to "Close".
className?
Merged onto the button, last, so it wins.
Hooks
useSheet
Reads the enclosing sheet's open state (and setter), or drives the sheet from your own control. The native dialog handles the modal background state. Must be used inside Sheet.
useSheet(): UseSheetReturns
open
setOpen
Functions
shouldDismissSheet
Whether a drag release should dismiss the sheet: a downward drag past dismissOffset (px) OR a downward flick past dismissVelocity (px/s). Both thresholds are downward-only (positive y is down), so an upward over-drag never dismisses. Exported so the dismiss contract is unit-testable without a browser (the drag gesture itself is browser-only).
shouldDismissSheet(offsetY: number, velocityY: number, dismissOffset: number, dismissVelocity: number): booleanFeatured in
See the sheet composed into full sections.
