Drawer
Overview
A gesture-driven panel that slides in from an edge of the screen. Unlike SSheet (a dialog with a side), the drawer owns a real domain state machine: snap points, swipe progress, a drag handle, and nested scaling are behaviour the dialog family does not have.
SDrawer combines the Aria drawer primitive family with the drawerVariants style recipe (extends sheetVariants, adds a drag handle and an opt-in swipeArea; 6 sizes × 4 sides). Modality, focus trapping and dismissal still come from the shared dialog primitives.
Usage
Features
- 🧩 Dialog-backed modality — inherits the dialog contract (
open/defaultOpen, focus trap, focus restoration, Escape/outside dismissal) while adding the drawer state machine - 🧭 4 sides —
side="top"/"bottom"/"left"/"right"(defaultbottom); horizontal sides are mirrored under RTL, vertical sides cap their height and scroll their content - 📏 Snap points —
snapPointsaccepts fractions (0.5), pixel offsets or CSS lengths; bind the active level withv-model:snap-point; the drawer opens at the first snap point and resets to it when closed - 🪜 Sequential snapping —
snapToSequentialPointswalks one level at a time instead of jumping to the nearest point - 🖐️ Swipe dismiss — dragging the panel or the handle past
closeThresholdcloses it;dismissible={false}forces an explicit action;swipeDirectionoverrides the dismiss direction - 👉 Swipe to open — opt into an edge gesture strip with
swipeable(DrawerSwipeArea), with axis locking, direction damping, sampled velocity and scroll yielding - 🖼️ Page indent —
DrawerIndent/DrawerIndentBackgroundscale, shift and round the page behind the drawer following the live swipe progress (CSS-variable driven) - 🧲 Handle-only dragging —
handleOnlyrestricts the gesture to the handle;fixedpins the panel while its content scrolls - 🪗 Nested drawers —
nestedrenders throughDrawerRootNestedso drag, release and open state stay coordinated with the parent - 🎛️ Modality tiers —
modalacceptstrue(full modal),'trap-focus'(focus trapped, outside pointer events alive) orfalse - 🔘 Dialog footer —
showClose/showCancel/showConfirmwith localizedcancelText/confirmText - 📐 6 sizes — xs–2xl
size; per-slotuioverrides - ⛶ Fullscreen —
fullscreen/defaultFullscreen(orv-model:fullscreen) grow the panel to the viewport from its anchored edge; snapping and dragging are suspended while fullscreen - ♿ Accessible —
role="dialog", focus moves into the panel,axe-coreclean
Component family
SDrawer(styled) — the entry wrapper;drawerVariantsrecipe with dynamic slot forwardingDrawerRoot/DrawerRootNested(Aria) — the state owner;open,snapPoints,snapPoint,dismissible,nested, drag/swipe stateDrawerTrigger(Aria) — the opener, wired toaria-haspopup/aria-expandedDrawerPortal(Aria) — the teleport boundaryDrawerOverlay(Aria) — the dimmed backdrop; fades with the live swipe progressDrawerPopup(Aria) — the focus-trapped surface and the drag gesture host; its position is driven entirely by CSS variables (--vean-drawer-snap-point-offset+--vean-drawer-swipe-movement-x/y)DrawerViewport(Aria) — the scrollable region that carries snap-point stateDrawerSwipeArea(Aria) — the opt-in edge strip that opens the drawer by swipeDrawerHandle(Aria) — the grab handle; double-tap cycles snap points.DrawerCompactrenders it only forside="bottom"DrawerIndent/DrawerIndentBackground(Aria) — wrap the page behind the drawer to get the indent effect;data-activemarks an open drawer and--vean-drawer-swipe-progresscarries the live progressDrawerHeader/DrawerContent/DrawerFooter/DrawerTitle/DrawerDescription/DrawerClose/DrawerCancel/DrawerConfirm(Aria) — chrome primitives wrapping Dialog; DOM usesdata-vean-drawer-*DrawerCompact(Aria) — the aggregated composite; composes handle, swipe area, header, content and footer and exposes the slots
Demos
Basic
Nested
Snap Points
Swipe
Form Inputs
API
Aria Layer
21Drawer
Props
| Prop | Type | Default | Description |
|---|---|---|---|
description | string | — | The description of the dialog. This is used for accessibility purposes and will be rendered in the content of the dialog if the `description` slot is not provided. |
open | boolean | undefined | The controlled open state of the dialog. Can be bound with `v-model:open`. |
defaultOpen | boolean | false | The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. |
dir | Direction | — | The text direction of the dialog |
draggable | boolean | false | Whether the dialog can be moved by dragging its header. |
title | string | — | The title of the dialog. This is used for accessibility purposes and will be rendered in the header of the dialog if the `title` slot is not provided. |
icon | IconValue | — | The icon of the dialog. This is used for accessibility purposes and will be rendered in the header of the dialog if the `icon` slot is not provided. |
pure | boolean | false | Whether to use the pure version of the dialog, which does not include the header and footer.
This is useful when you want to fully control the content of the dialog and do not need the built-in header and footer. |
portalProps | DrawerPortalProps | — | Properties forwarded to the portal element. |
popupProps | DrawerPopupProps | — | Properties forwarded to the popup element. |
triggerProps | DialogTriggerProps | — | Properties forwarded to the trigger element. |
headerProps | DialogHeaderProps | — | Properties forwarded to the header element. |
contentProps | DialogContentProps | — | Properties forwarded to the content element. |
footerProps | DialogFooterProps | — | Properties forwarded to the footer element. |
closeProps | DialogCloseProps | — | Properties forwarded to the close element. |
fullscreen | boolean | undefined | The controlled fullscreen state of the dialog. Can be bound with `v-model:fullscreen`. |
showClose | boolean | true | Whether show the close button in the header of the dialog. |
showFullscreen | boolean | true | Whether show the fullscreen toggle button in the header of the dialog. |
showCancel | boolean | 'onlyWarning' | 'onlyWarning' | Whether to show the cancel button.
When set to `onlyWarning`, the cancel button will only be shown when the dialog is an alert dialog with `alertType="warning"`.
When set to `true`, the cancel button will always be shown. |
cancelText | string | — | The text of the cancel button. This is used for accessibility purposes and will be rendered in the footer of the dialog if the `cancel` slot is not provided.
Defaults to the localized `dialog.cancel` message from `ConfigProvider`. |
showConfirm | boolean | — | Whether to show the confirm button when the dialog is an alert dialog.
The default value is `true` when the dialog is an alert dialog. |
confirmText | string | — | The text of the confirm button. This is used for accessibility purposes and will be rendered in the footer of the dialog if the `confirm` slot is not provided.
Defaults to the localized `dialog.confirm` message from `ConfigProvider`. |
overlayProps | DrawerOverlayProps | — | Properties forwarded to the overlay element. |
titleProps | DialogTitleProps | — | Properties forwarded to the title element. |
descriptionProps | DialogDescriptionProps | — | Properties forwarded to the description element. |
fullscreenProps | DialogFullscreenProps | — | Properties forwarded to the fullscreen element. |
cancelProps | DialogCancelProps | — | Properties forwarded to the cancel element. |
confirmProps | DialogConfirmProps | — | Properties forwarded to the confirm element. |
isAlert | boolean | false | Whether the dialog is an alert dialog. An alert dialog is a dialog that interrupts the user's workflow to communicate an important message and requires a response.
When set to `true`, the dialog will have `role="alertdialog"` and will require a `DialogTitle` to be provided. This is used for accessibility purposes. |
alertType | DialogAlertType | — | The alert type of the dialog, which determines the default icon and styles when the dialog is an alert dialog. |
defaultFullscreen | boolean | false | The fullscreen state of the dialog when it is initially rendered. Use when you do not need to control its fullscreen state. |
modal | import("../../types").ModalityTier | true | Modality tier. `true` blocks outside pointer events, `'trap-focus'` only traps focus,
`false` keeps the surface non-modal. |
snapPoint | DrawerSnapPoint | null | — | The controlled snap point. Can be bound with `v-model:snapPoint`. |
defaultSnapPoint | DrawerSnapPoint | null | — | The snap point used when the drawer is initially rendered. Use when you do not need to control it.
Defaults to the first entry of `snapPoints`. |
snapToSequentialPoints | boolean | false | When `true`, snaps to the next sequential snap point (one step at a time).
When `false`, snaps to the nearest snap point by distance. |
closeThreshold | number | 0.25 | Close threshold as a fraction of the drawer size (0–1). |
swipeDirection | SwipeDirection | — | The swipe direction that dismisses the drawer. Defaults to the direction
opposite the drawer's entry edge (`side`). |
fixed | boolean | — | Whether fixed. |
dismissible | boolean | true | Whether dismissible. |
nested | boolean | false | Whether nested. |
side | Side | 'bottom' | Direction. |
noBodyStyles | boolean | — | When `true` the `body` doesn't get any styles assigned from the drawer |
handleOnly | boolean | false | Whether handle only. |
preventScrollRestoration | boolean | — | Whether prevent scroll restoration. |
snapPoints | DrawerSnapPoint[] | — | Snap points used to position the drawer.
Use numbers between 0 and 1 to represent fractions of the viewport height,
numbers greater than 1 as pixel values, or strings in `px`/`rem` units. |
handleProps | DrawerHandleProps | — | Properties forwarded to the handle element. |
swipeable | boolean | false | Render the opt-in swipe-to-open area at the drawer's edge. |
swipeAreaProps | DrawerSwipeAreaProps | — | Properties forwarded to the swipe area element. |
class | ClassValue | — | the popup class of the drawer |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<DrawerUi> | — | Per-slot class overrides for the component. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
cancel | MouseEvent | Event handler called when the dialog is requested to be canceled. |
click | PointerEvent | Event handler called when the dialog trigger is activated. |
fullscreen | MouseEvent | Event handler called when the fullscreen state of the dialog is requested to be toggled. |
confirm | MouseEvent | Event handler called when the dialog is requested to be closed by confirming. |
update:open | boolean | Event handler called when the open state of the dialog changes. |
update:fullscreen | boolean | Event handler called when the fullscreen state of the dialog changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown`
event happens outside or focus moves outside of it. Can be prevented. |
openAutoFocus | Event | Event handler called when auto-focusing on open. Can be prevented. |
closeAutoFocus | Event | Event handler called when auto-focusing on close. Can be prevented. |
drag | number | Emitted while dragging with the live progress: 0 fully open, 1 closed. |
release | boolean | Emitted when a drag gesture releases; `true` when the drawer stays open. |
close | — | Emitted when close occurs. |
update:snapPoint | DrawerSnapPoint | null | Emitted when the snap point value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | props: DialogCompactBaseSlotProps | Custom content for the default slot. |
trigger | DialogCompactBaseSlotProps | Custom content for the trigger slot. |
title | DialogCompactBaseSlotProps | Custom content for the title slot. |
description | DialogCompactBaseSlotProps | Custom content for the description slot. |
close | DialogCompactBaseSlotProps | Custom content for the close slot. |
fullscreen | DialogCompactBaseSlotProps | Custom content for the fullscreen slot. |
footer | DialogCompactBaseSlotProps | Custom content for the footer slot. |
cancel | DialogCompactBaseSlotProps | Custom content for the cancel slot. |
confirm | DialogCompactBaseSlotProps | Custom content for the confirm slot. |
DrawerCancel
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
cancel | MouseEvent | Event handler called when the dialog is requested to be canceled. |
DrawerClose
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
close | MouseEvent | Event handler called when the dialog is requested to be closed. |
DrawerCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
description | string | — | The description of the dialog. This is used for accessibility purposes and will be rendered in the content of the dialog if the `description` slot is not provided. |
open | boolean | undefined | The controlled open state of the dialog. Can be bound with `v-model:open`. |
defaultOpen | boolean | false | The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. |
dir | Direction | — | The text direction of the dialog |
draggable | boolean | false | Whether the dialog can be moved by dragging its header. |
title | string | — | The title of the dialog. This is used for accessibility purposes and will be rendered in the header of the dialog if the `title` slot is not provided. |
icon | IconValue | — | The icon of the dialog. This is used for accessibility purposes and will be rendered in the header of the dialog if the `icon` slot is not provided. |
pure | boolean | false | Whether to use the pure version of the dialog, which does not include the header and footer.
This is useful when you want to fully control the content of the dialog and do not need the built-in header and footer. |
portalProps | DrawerPortalProps | — | Properties forwarded to the portal element. |
popupProps | DrawerPopupProps | — | Properties forwarded to the popup element. |
triggerProps | DialogTriggerProps | — | Properties forwarded to the trigger element. |
headerProps | DialogHeaderProps | — | Properties forwarded to the header element. |
contentProps | DialogContentProps | — | Properties forwarded to the content element. |
footerProps | DialogFooterProps | — | Properties forwarded to the footer element. |
closeProps | DialogCloseProps | — | Properties forwarded to the close element. |
fullscreen | boolean | undefined | The controlled fullscreen state of the dialog. Can be bound with `v-model:fullscreen`. |
showClose | boolean | true | Whether show the close button in the header of the dialog. |
showFullscreen | boolean | true | Whether show the fullscreen toggle button in the header of the dialog. |
showCancel | boolean | 'onlyWarning' | 'onlyWarning' | Whether to show the cancel button.
When set to `onlyWarning`, the cancel button will only be shown when the dialog is an alert dialog with `alertType="warning"`.
When set to `true`, the cancel button will always be shown. |
cancelText | string | — | The text of the cancel button. This is used for accessibility purposes and will be rendered in the footer of the dialog if the `cancel` slot is not provided.
Defaults to the localized `dialog.cancel` message from `ConfigProvider`. |
showConfirm | boolean | — | Whether to show the confirm button when the dialog is an alert dialog.
The default value is `true` when the dialog is an alert dialog. |
confirmText | string | — | The text of the confirm button. This is used for accessibility purposes and will be rendered in the footer of the dialog if the `confirm` slot is not provided.
Defaults to the localized `dialog.confirm` message from `ConfigProvider`. |
overlayProps | DrawerOverlayProps | — | Properties forwarded to the overlay element. |
titleProps | DialogTitleProps | — | Properties forwarded to the title element. |
descriptionProps | DialogDescriptionProps | — | Properties forwarded to the description element. |
fullscreenProps | DialogFullscreenProps | — | Properties forwarded to the fullscreen element. |
cancelProps | DialogCancelProps | — | Properties forwarded to the cancel element. |
confirmProps | DialogConfirmProps | — | Properties forwarded to the confirm element. |
isAlert | boolean | false | Whether the dialog is an alert dialog. An alert dialog is a dialog that interrupts the user's workflow to communicate an important message and requires a response.
When set to `true`, the dialog will have `role="alertdialog"` and will require a `DialogTitle` to be provided. This is used for accessibility purposes. |
alertType | DialogAlertType | — | The alert type of the dialog, which determines the default icon and styles when the dialog is an alert dialog. |
defaultFullscreen | boolean | false | The fullscreen state of the dialog when it is initially rendered. Use when you do not need to control its fullscreen state. |
modal | import("../../types").ModalityTier | true | Modality tier. `true` blocks outside pointer events, `'trap-focus'` only traps focus,
`false` keeps the surface non-modal. |
snapPoint | DrawerSnapPoint | null | — | The controlled snap point. Can be bound with `v-model:snapPoint`. |
defaultSnapPoint | DrawerSnapPoint | null | — | The snap point used when the drawer is initially rendered. Use when you do not need to control it.
Defaults to the first entry of `snapPoints`. |
snapToSequentialPoints | boolean | false | When `true`, snaps to the next sequential snap point (one step at a time).
When `false`, snaps to the nearest snap point by distance. |
closeThreshold | number | 0.25 | Close threshold as a fraction of the drawer size (0–1). |
swipeDirection | SwipeDirection | — | The swipe direction that dismisses the drawer. Defaults to the direction
opposite the drawer's entry edge (`side`). |
fixed | boolean | — | Whether fixed. |
dismissible | boolean | true | Whether dismissible. |
nested | boolean | false | Whether nested. |
side | Side | 'bottom' | Direction. |
noBodyStyles | boolean | — | When `true` the `body` doesn't get any styles assigned from the drawer |
handleOnly | boolean | false | Whether handle only. |
preventScrollRestoration | boolean | — | Whether prevent scroll restoration. |
snapPoints | DrawerSnapPoint[] | — | Snap points used to position the drawer.
Use numbers between 0 and 1 to represent fractions of the viewport height,
numbers greater than 1 as pixel values, or strings in `px`/`rem` units. |
handleProps | DrawerHandleProps | — | Properties forwarded to the handle element. |
swipeable | boolean | false | Render the opt-in swipe-to-open area at the drawer's edge. |
swipeAreaProps | DrawerSwipeAreaProps | — | Properties forwarded to the swipe area element. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
cancel | MouseEvent | Event handler called when the dialog is requested to be canceled. |
click | PointerEvent | Event handler called when the dialog trigger is activated. |
fullscreen | MouseEvent | Event handler called when the fullscreen state of the dialog is requested to be toggled. |
confirm | MouseEvent | Event handler called when the dialog is requested to be closed by confirming. |
update:open | boolean | Event handler called when the open state of the dialog changes. |
update:fullscreen | boolean | Event handler called when the fullscreen state of the dialog changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown`
event happens outside or focus moves outside of it. Can be prevented. |
openAutoFocus | Event | Event handler called when auto-focusing on open. Can be prevented. |
closeAutoFocus | Event | Event handler called when auto-focusing on close. Can be prevented. |
drag | number | Emitted while dragging with the live progress: 0 fully open, 1 closed. |
release | boolean | Emitted when a drag gesture releases; `true` when the drawer stays open. |
close | — | Emitted when close occurs. |
update:snapPoint | DrawerSnapPoint | null | Emitted when the snap point value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | props: DialogCompactBaseSlotProps | Custom content for the default slot. |
trigger | DialogCompactBaseSlotProps | Custom content for the trigger slot. |
title | DialogCompactBaseSlotProps | Custom content for the title slot. |
description | DialogCompactBaseSlotProps | Custom content for the description slot. |
close | DialogCompactBaseSlotProps | Custom content for the close slot. |
fullscreen | DialogCompactBaseSlotProps | Custom content for the fullscreen slot. |
footer | DialogCompactBaseSlotProps | Custom content for the footer slot. |
cancel | DialogCompactBaseSlotProps | Custom content for the cancel slot. |
confirm | DialogCompactBaseSlotProps | Custom content for the confirm slot. |
DrawerConfirm
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
confirm | MouseEvent | Event handler called when the dialog is requested to be closed by confirming. |
DrawerContent
DrawerDescription
DrawerFooter
DrawerHandle
Props
| Prop | Type | Default | Description |
|---|---|---|---|
preventCycle | boolean | — | Whether prevent cycle. |
DrawerHeader
DrawerIndent
DrawerIndentBackground
DrawerOverlay
DrawerPopup
DrawerPortal
DrawerRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | undefined | The controlled open state of the dialog. Can be bound with `v-model:open`. |
defaultOpen | boolean | false | The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. |
dir | Direction | — | The text direction of the dialog |
draggable | boolean | false | Whether the dialog can be moved by dragging its header. |
fullscreen | boolean | undefined | The controlled fullscreen state of the dialog. Can be bound with `v-model:fullscreen`. |
isAlert | boolean | false | Whether the dialog is an alert dialog. An alert dialog is a dialog that interrupts the user's workflow to communicate an important message and requires a response.
When set to `true`, the dialog will have `role="alertdialog"` and will require a `DialogTitle` to be provided. This is used for accessibility purposes. |
alertType | DialogAlertType | — | The alert type of the dialog, which determines the default icon and styles when the dialog is an alert dialog. |
defaultFullscreen | boolean | false | The fullscreen state of the dialog when it is initially rendered. Use when you do not need to control its fullscreen state. |
modal | import("../../types").ModalityTier | true | Modality tier. `true` blocks outside pointer events, `'trap-focus'` only traps focus,
`false` keeps the surface non-modal. |
snapPoint | DrawerSnapPoint | null | — | The controlled snap point. Can be bound with `v-model:snapPoint`. |
defaultSnapPoint | DrawerSnapPoint | null | — | The snap point used when the drawer is initially rendered. Use when you do not need to control it.
Defaults to the first entry of `snapPoints`. |
snapToSequentialPoints | boolean | false | When `true`, snaps to the next sequential snap point (one step at a time).
When `false`, snaps to the nearest snap point by distance. |
closeThreshold | number | 0.25 | Close threshold as a fraction of the drawer size (0–1). |
swipeDirection | SwipeDirection | — | The swipe direction that dismisses the drawer. Defaults to the direction
opposite the drawer's entry edge (`side`). |
fixed | boolean | — | Whether fixed. |
dismissible | boolean | true | Whether dismissible. |
nested | boolean | false | Whether nested. |
side | Side | 'bottom' | Direction. |
noBodyStyles | boolean | — | When `true` the `body` doesn't get any styles assigned from the drawer |
handleOnly | boolean | false | Whether handle only. |
preventScrollRestoration | boolean | — | Whether prevent scroll restoration. |
snapPoints | DrawerSnapPoint[] | — | Snap points used to position the drawer.
Use numbers between 0 and 1 to represent fractions of the viewport height,
numbers greater than 1 as pixel values, or strings in `px`/`rem` units. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean | Event handler called when the open state of the dialog changes. |
update:fullscreen | boolean | Event handler called when the fullscreen state of the dialog changes. |
drag | number | Emitted while dragging with the live progress: 0 fully open, 1 closed. |
release | boolean | Emitted when a drag gesture releases; `true` when the drawer stays open. |
close | — | Emitted when close occurs. |
update:snapPoint | DrawerSnapPoint | null | Emitted when the snap point value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | { open: boolean; close: () => void; } | Custom content for the default slot. |
DrawerRootNested
DrawerSwipeArea
Props
| Prop | Type | Default | Description |
|---|---|---|---|
swipeDirection | SwipeDirection | — | Override the swipe side that opens the drawer. Defaults to the opposite of the root swipe direction. |
disabled | boolean | false | Disable swipe-to-open. |
DrawerTitle
DrawerTrigger
Props
| Prop | Type | Default | Description |
|---|---|---|---|
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
click | PointerEvent | Event handler called when the dialog trigger is activated. |
DrawerViewport
Notes
Architecture and benchmark differences
DrawerCompact owns the handle/swipe-area/overlay/popup/header/content/footer composition and the drag/snap state flow (via useDrawerSnapPoints and useSwipeDismiss), while every primitive stays style-free and only the UI wrapper injects the drawerVariants classes. The popup transform is CSS-variable driven — the gesture layer writes variables, never inline transforms — so snap settling, release bounce and dismissal all resolve through CSS transitions. This mirrors the Base UI Drawer model. Ant Design, Element Plus, Mantine and Naive UI ship a single styled drawer; a dedicated draggable panel with snapPoints is typically a separate library (vaul, Base UI Drawer). VeanUI exposes per-slot *Props, a size scale, and the snap/indent/drag/swipe model inline.
| Capability | VeanUI | shadcn/ui + vaul | reka-ui Drawer | Base UI | Ant Design | Element Plus | Mantine |
|---|---|---|---|---|---|---|---|
| Reuses dialog primitives | ✅ | ✅ | ✅ | ✅ | — | — | — |
| Aria/styled split | ✅ | ✅ | ✅ | ✅ | — | — | — |
| Drag-to-dismiss | ✅ | ✅ | ✅ | ✅ | — | — | ✅ |
| Snap points | ✅ | ✅ | ✅ | ✅ | — | — | — |
| Swipe-to-open area | ✅ | — | ✅ | ✅ | — | — | — |
| Page indent effect | ✅ | ✅ | — | ✅ | — | — | — |
| Nested drawers | ✅ | ✅ | ✅ | ✅ | — | — | — |
| Modality tiers | ✅ | — | ✅ | ✅ | — | — | — |
| Sizes (6) | ✅ | — | — | — | — | — | — |
— = unsupported or a different interaction model.
Cautions
modaldefaults totrue; the panel teleports todocument.bodyand body scroll is locked by the hide-others layer. The'trap-focus'tier keeps outside pointer events alive but still traps focus.sidepicks the anchored edge. Vertical sides (top/bottom) cap the panel atcalc(100dvh - 2rem)so a long body scrolls inside thecontentslot instead of growing past the viewport; horizontal sides fill the viewport height and cap their width.- Drag-to-dismiss uses pointer capture;
dismissible(defaulttrue) allows releasing pastcloseThresholdto close. Setfalseto force explicit actions.swipeDirectionoverrides the dismiss direction (defaults to the direction opposite the entry edge). - Horizontal sides ship vertical snap only in this release —
snapPointsresolves against the vertical axis, soleft/rightsnap behaviour is not yet supported. snapPointsaccepts fractions (0–1), pixel values (> 1) or CSS length strings;snapPointtracks the current level and is bound withv-model:snap-point. The drawer opens at the first snap point and resets to it on close.- The drag handle is rendered only for
side="bottom"inDrawerCompact; composeDrawerHandlemanually for other sides. handleOnlyrestricts dragging to the handle;fixedkeeps the panel in place while inner content scrolls.swipeablerenders a gesture strip at the drawer’s edge; it is inert while the drawer is open.nestedrenders viaDrawerRootNested; each nested drawer coordinates drag and release with its parent.- Fullscreen defines the panel’s size and freezes its drag, matching how a dialog disables its draggable in fullscreen: a resting snap level below “fully open” would translate the panel back down and leave the half beyond its anchored edge off-screen, and a drag away from the edge would contradict the prop. Snapping and dragging resume when fullscreen is switched off;
snapPointsstay configured throughout. - Because the drag is suspended, dismissal in fullscreen falls back to the explicit paths: the close button, Escape, and an outside press on a non-modal drawer. A modal fullscreen drawer with
showClose={false}anddismissible={false}therefore has no way out — keep the close button (ormodal={false}) if the user must be able to leave it. - The handle stops cycling snap levels while fullscreen, so it cannot rewrite
snapPointbehind the scenes. - The drawer renders no fullscreen button of its own, so the dialog’s
showFullscreenhas no effect here — drive the state withv-model:fullscreen/defaultFullscreen, or render your own control inside the panel. - An uncontrolled fullscreen session does not survive a close: the drawer reopens at
defaultFullscreen(matching the dialog’s reset).
Migrating from BottomSheet
v0.50.0 renamed the whole family — the name bottom-sheet is retired.
| Before | After |
|---|---|
SBottomSheet | SDrawer |
BottomSheetRoot / BottomSheetRootNested | DrawerRoot / DrawerRootNested |
BottomSheetPopup / BottomSheetOverlay | DrawerPopup / DrawerOverlay |
BottomSheetHandle / BottomSheetCompact | DrawerHandle / DrawerCompact |
BottomSheetTitle / BottomSheetDescription | DrawerTitle / DrawerDescription |
BottomSheetHeader / BottomSheetContent / BottomSheetFooter | DrawerHeader / DrawerContent / DrawerFooter |
BottomSheetTrigger / BottomSheetClose / BottomSheetCancel / BottomSheetConfirm | DrawerTrigger / DrawerClose / DrawerCancel / DrawerConfirm |
v-model:active-snap-point | v-model:snap-point |
direction prop (Aria) | side prop |
@vean/aria/bottom-sheet | @vean/aria/drawer |
data-vean-bottom-sheet-*, soybean-bottom-sheet-dragging | data-vean-drawer-*, soybean-drawer-dragging |
data-vean-bottom-sheet-scale | removed with the scale-background engine (see below) |
The old SDrawer (a dialog with a side) was renamed to SSheet. See Sheet for the side-panel API.
Migrating from the vaul-style engine
v0.50.0 replaced the vaul-derived engine with a Base UI-style one. The public surface changes:
| Before | After |
|---|---|
shouldScaleBackground / setBackgroundColorOnScale | removed. Wrap the page in DrawerIndent + DrawerIndentBackground inside DrawerRoot |
fadeFromIndex | removed; the overlay fades continuously with the swipe progress |
snapPoint defaulting to null | defaults to the first entry of snapPoints (the drawer opens positioned at a snap point) |
implicit drag direction from side | still the default, overridable with swipeDirection |
| imperative transforms | CSS variables (--vean-drawer-snap-point-offset, --vean-drawer-swipe-movement-x/y) |
<!-- Before -->
<SBottomSheet v-model:open="open" should-scale-background>
<div data-vean-drawer-scale>Page</div>
</SBottomSheet>
<!-- After -->
<SDrawer v-model:open="open">
<DrawerIndent class="page-indent">Page</DrawerIndent>
<template #trigger><SButton>Open</SButton></template>
</SDrawer><!-- Before -->
<SBottomSheet v-model:open="open" v-model:active-snap-point="snap" :snap-points="[0.5, 1]">
<template #trigger><SButton>Open</SButton></template>
Content
</SBottomSheet>
<!-- After -->
<SDrawer v-model:open="open" v-model:snap-point="snap" :snap-points="[0.5, 1]">
<template #trigger><SButton>Open</SButton></template>
Content
</SDrawer>Roadmap
Horizontal snap points, on-device verification of the touch pipeline (iOS Safari / Android Chrome), and iOS virtual keyboard coordination.
FAQ
How do I anchor the drawer to a different edge?
Set side to top/bottom/left/right:
<SDrawer v-model:open="open" side="left" title="Filters">
<template #trigger><SButton>Open</SButton></template>
<div>Drawer content</div>
</SDrawer>How do I enable snap points?
Pass an array of fractions, pixels or CSS lengths, and bind the active level:
<SDrawer v-model:open="open" v-model:snap-point="snapPoint" :snap-points="[0.4, 0.8, 1]" title="Filters">
<template #trigger><SButton>Open</SButton></template>
<div>Drawer content</div>
</SDrawer>How do I open the drawer with a swipe gesture?
Set swipeable. An edge strip is rendered at the panel’s side and swipes in the opposite direction:
<SDrawer v-model:open="open" swipeable title="Details">
<template #trigger><SButton>Open</SButton></template>
<div>Drawer content</div>
</SDrawer>How do I disable drag-to-dismiss?
Set dismissible={false} to require an explicit action:
<SDrawer :dismissible="false" title="Confirm">
<template #trigger><SButton>Open</SButton></template>
<div>Drawer content</div>
</SDrawer>How do I restrict dragging to the handle?
Set handle-only:
<SDrawer handle-only title="Details">
<template #trigger><SButton>Open</SButton></template>
<div>Drawer content</div>
</SDrawer>How do I build a non-modal side panel that still traps focus?
Use the 'trap-focus' tier:
<SDrawer v-model:open="open" modal="trap-focus" title="Inspector">
<template #trigger><SButton>Open</SButton></template>
<div>Drawer content</div>
</SDrawer>How do I make the drawer fullscreen?
Bind v-model:fullscreen, or let it start fullscreen with default-fullscreen. The panel grows to the viewport from its side edge and stops responding to drags, so snapping and dragging resume only once you switch fullscreen off:
<SDrawer v-model:open="open" v-model:fullscreen="fullscreen" :snap-points="[0.6, 1]" title="Details">
<template #trigger><SButton>Open</SButton></template>
<div>Drawer content</div>
</SDrawer>