Sheet
Overview
Sheet is a headless @vyui/core primitive for a panel that slides in from a viewport edge and settles at one of several snap points. Dragging is driven on the main thread for native-smooth tracking, with fling-to-advance and optional drag-to-dismiss. The styled Drawer and ActionSheet components build on it.
@vyui/core primitive (the snap/drag pattern is adapted from lynx-ui). Compose it directly when you need custom snap behavior; otherwise use the styled kit components.Anatomy
<SheetRoot>
<SheetTrigger />
<SheetBackdrop />
<SheetContent>
<SheetHandle />
<!-- sheet body -->
</SheetContent>
</SheetRoot>
Usage
<script setup lang="ts">
import {
SheetBackdrop,
SheetContent,
SheetHandle,
SheetRoot,
SheetTrigger,
} from '@vyui/core'
const open = ref(false)
</script>
<template>
<SheetRoot
v-model:open="open"
side="bottom"
:snap-points="[0.25, 0.5, 0.9]"
:default-snap-index="1"
>
<SheetTrigger>
<text>Open sheet</text>
</SheetTrigger>
<SheetBackdrop />
<SheetContent>
<SheetHandle />
<view class="p-4">
<text>Drag the handle to snap between sizes.</text>
</view>
</SheetContent>
</SheetRoot>
</template>
Features and behavior
sidecontrols the edge and drag axis:top,right,bottom, orleft; default isbottom.snapPointsare fractions of viewport extent on that axis, low → high (e.g.[0.25, 0.5, 0.9]); default is[1](full height or width).snapIndex/v-model:snapIndexcontrols the current snap (0 = most closed);defaultSnapIndexseeds it.- A fling projects a short coast and advances by a snap; outward flings past
dismissVelocitydismiss whenenableDragToClose(defaulttrue). dragHandleOnlyrestricts dragging to<SheetHandle>, leaving the body non-interactive to touch.viewportHeightoverrides the runtime height read;durationtunes the settle animation.
API
SheetRoot
| Prop | Default | Type |
|---|---|---|
defaultOpen | false | boolean | undefinedInitial open state when uncontrolled. |
defaultSnapIndex | 0 | number | undefinedInitial snap index when uncontrolled (defaults to 0). The enter animation always slides fully in first; when this points below the largest snap the sheet then settles down to it. The index persists across close/reopen. |
dismissVelocity | 600 | number | undefinedDownward velocity (px/s) at which a fling dismisses from any position (when `enableDragToClose`). |
duration | 280 | number | undefinedSettle animation duration in ms. Drag release snap-back uses it directly; drag-dismiss and touch-cancel use shorter cuts of it. |
enableDragToClose | true | boolean | undefinedAllow drag below the most-closed snap to dismiss. When `false` with multiple snap points, drag between snaps still works — only the dismiss branch is disabled. |
handleOnly | false | boolean | undefinedRestrict drag interaction to `<SheetHandle>` only. When `true`, the `<SheetContent>` body does not respond to touch. |
open | — | boolean | undefinedControlled open state. Bind with `v-model:open`. |
side | "bottom" | SheetDirection | undefinedEdge the sheet is anchored to. Controls enter/exit animation and drag axis. |
snapIndex | — | number | undefinedControlled current snap index. Bind with `v-model:snapIndex`. Indexes the SORTED `snapPoints` (0 = smallest fraction = most closed). Updated by drag settles; writing it animates the open sheet to that snap. |
snapPoints | [1] | number[] | undefinedSnap points as fractions of viewport extent on the sheet axis, low → high. e.g. `[0.25, 0.5, 0.9]`. For `top`/`bottom` this is viewport height; for `left`/`right` this is viewport width. |
velocityThreshold | 400 | number | undefinedAbsolute velocity (px/s) above which a fling advances by one snap regardless of position. Currently unused — the release logic (mirroring `pickRelease`) implements flick-advance via a 100ms coast projection instead. Reserved. |
viewportHeight | — | number | undefinedViewport height in px. If omitted, reads from `SystemInfo.pixelHeight / pixelRatio` at runtime, falling back to `800`. |
viewportWidth | — | number | undefinedViewport width in px. If omitted, reads from `SystemInfo.pixelWidth / pixelRatio` at runtime, falling back to `400`. |
| Event | Payload |
|---|---|
update:open | [value: boolean] |
update:snapIndex | [value: number] |
SheetContent
| Prop | Default | Type |
|---|---|---|
dragDisabled | false | boolean | undefinedDisable drag / snap / fling; open and close still animate. |
fitContent | false | boolean | undefinedHug content instead of sizing the panel to `snapPoints × viewport`. Forwarded to `SheetContentImpl`. Used by the styled `Tray`. |
SheetTrigger
| Prop | Default | Type |
|---|---|---|
as | "view" | AsTag | undefined |
asChild | — | boolean | undefined |
SheetBackdrop
| Prop | Default | Type |
|---|---|---|
dismissOnTap | true | boolean | undefinedClose the sheet when the backdrop is tapped. |
SheetHandle
| Prop | Default | Type |
|---|---|---|
hidden | — | boolean | undefinedHide the default handle styling. |
Accessibility
- Provide a visible label or handle affordance so the sheet is discoverable and draggable.
- The backdrop dims and (by default) closes the sheet; ensure the open state is reflected to assistive tech via the content semantics.
Platform notes
- Drag tracking and bounce run through main-thread (MTS) touch worklets, so motion stays smooth off the background thread.
idis required by the bounce system to select the container on the main thread; it is auto-generated when omitted.
Related components
Drawer— the styled@vyui/kitsheet.ActionSheet— a styled list-of-actions sheet.Dialog— a centered modal alternative.