Sheet
Overview
A panel that slides out from the edge of the screen. It reuses the declarative API and slot contract of SDialog (same Aria DialogCompact base, same modal/focus/dismissable behavior), and adds side to control where the panel enters — top/bottom/left/right (default right).
SSheet combines the Aria dialog primitive family with the sheetVariants style recipe (extends dialogVariants, 6 sizes × 4 sides).
Looking for a gesture-driven panel with snap points and swipe dismiss? That is Drawer — the sheet is deliberately “a dialog with a side”.
Usage
Features
- 🧩 Reuses the dialog base — built on
DialogCompact, inheritsSDialog’s slots, events, per-part*Props,pure,isAlertand the imperativedialog(...)API - 🧭 4 sides —
side="top"/"bottom"/"left"/"right"(defaultright); RTL-aware slide direction for left/right - 🎭 Modal by default —
aria-modal,useHideOthers, outside-pointer blocking and focus trapping, same asSDialog - ❌ Closable —
showClose, Escape, outside pointer/focus and the close button all dismiss - 🎞️ Animated — enter/exit transitions (
slide-in-from-*/slide-out-to-*) driven by the open state - 📐 6 sizes — xs–2xl
size; per-slotuioverrides - ⛶ Fullscreen —
showFullscreenrenders a toggle;fullscreen/defaultFullscreendrive av-model:fullscreenstate that grows the panel to the viewport - 🔘 Cancel/confirm footer —
showCancel/showConfirmwith localizedcancelText/confirmText - ♿ Accessible —
role="dialog", focus trap + loop, focus restoration on close,axe-corezero violations
Component family
SSheet(styled) — the entry wrapper;sheetVariantsrecipe (size+side) with dynamic slot forwarding- All other parts come from the Aria dialog family (see
Dialog):DialogRoot,DialogTrigger,DialogOverlay,DialogPopup,DialogHeader,DialogContent,DialogFooter,DialogTitle,DialogDescription,DialogClose,DialogCancel,DialogConfirm,DialogCompact
Demos
Basic
API
Aria Layer
1Sheet
Props
| Prop | Type | Default | Description |
|---|---|---|---|
side | Side | — | Side placement of the component. |
showFullscreen | boolean | false | Whether show the fullscreen toggle button in the header of the dialog. |
class | ClassValue | — | the popup class of the dialog |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<DialogUi> | — | Per-slot class overrides for the component. |
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. |
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. |
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. |
showClose | boolean | true | Whether show the close button in the header of the dialog. |
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. |
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`. |
triggerProps | DialogTriggerProps | — | Properties forwarded to the trigger element. |
overlayProps | DialogOverlayProps | — | Properties forwarded to the overlay element. |
portalProps | PortalProps | — | Properties forwarded to the portal element. |
popupProps | DialogPopupProps | — | Properties forwarded to the popup 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. |
titleProps | DialogTitleProps | — | Properties forwarded to the title element. |
descriptionProps | DialogDescriptionProps | — | Properties forwarded to the description element. |
closeProps | DialogCloseProps | — | Properties forwarded to the close 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. |
modal | ModalityTier | true | The modality of the dialog. When set to `true`, interaction with outside elements will be disabled and only dialog
content will be visible to screen readers.
Set to `'trap-focus'` to trap focus while still letting outside pointer events through (non-modal side panels such
as `Drawer` in the `'trap-focus'` tier). |
dir | Direction | — | The text direction of the dialog |
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. |
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`. |
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. |
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. |
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. |
click | PointerEvent | Event handler called when the dialog trigger is activated. |
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. |
close | MouseEvent | Event handler called when the dialog is requested to be closed. |
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. |
cancel | MouseEvent | Event handler called when the dialog is requested to be canceled. |
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. |
Notes
Architecture and benchmark differences
SSheet is a thin styled wrapper: it forwards every prop/slot/event to the Aria DialogCompact and only supplies the sheetVariants recipe that extends dialogVariants with side-specific popup classes. This keeps sheet and dialog behavior identical while varying only presentation — the same headless/styled split as shadcn-ui/vaul-style panels, versus Ant Design’s drawer (single styled component with placement/width/closable/mask props) and Element Plus/Mantine/Naive UI equivalents.
| Capability | VeanUI | shadcn/ui | Ant Design Drawer | Element Plus Drawer | Mantine Drawer | Naive UI Drawer |
|---|---|---|---|---|---|---|
| Reuses dialog base | ✅ | ✅ | — | — | — | — |
| Aria/styled split | ✅ | ✅ | — | — | — | — |
| 4 placements (side) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Modal (aria-modal + trap) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Focus return on close | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Sizes (6) | ✅ | — | — | — | — | — |
| Pure (no header/footer) | ✅ | — | — | — | — | — |
— = unsupported or a different interaction model.
Cautions
- Sheet inherits the dialog contract: it is modal by default and the popup teleports to
document.body; Escape/outside interaction dismisses it. sideonly changes the slide direction and position classes; the accessibleroleremainsdialog(a sheet is not a distinct ARIA role).- Left/right sheets slide in the logical direction and are mirrored under RTL (
dir). - In fullscreen the panel covers the viewport from the edge it is anchored to, so
sideonly decides which edge that is; the per-side size caps (w-3/4/sm:max-w-sm, and the100dvh - 2remheight cap) are released. - The imperative
dialog(...)API also renders sheets if you pass the matching options — no separate sheet service is needed.
Migrating from SDrawer (v0.50.0)
The name SDrawer now belongs to the gesture-driven Drawer. The side panel keeps its exact behaviour under a new name:
| Before | After |
|---|---|
SDrawer (side panel) | SSheet |
@vean/ui → SDrawer | @vean/ui → SSheet |
drawerVariants | sheetVariants |
DrawerProps / DrawerEmits / DrawerSlots | SheetProps / SheetEmits / SheetSlots |
<!-- Before -->
<SDrawer v-model:open="open" side="left" title="Filters">...</SDrawer>
<!-- After -->
<SSheet v-model:open="open" side="left" title="Filters">...</SSheet>FAQ
How do I slide the sheet from a specific edge?
Set side to top/bottom/left/right:
<SSheet v-model:open="open" side="left" title="Filters">...</SSheet>How do I control the open state?
Bind open with v-model, or use defaultOpen for an uncontrolled sheet:
<SSheet v-model:open="open" title="Settings">...</SSheet>How do I add cancel/confirm actions?
Use the footer slot, or rely on showCancel/showConfirm with localized text:
<SSheet v-model:open="open" show-confirm confirm-text="Apply" title="Preferences">
<template #trigger><SButton>Open</SButton></template>
</SSheet>How do I build a custom sheet?
Use pure and fill the default slot:
<SSheet v-model:open="open" pure side="bottom">
<div class="custom">...</div>
</SSheet>How do I make the sheet fullscreen?
Render the header toggle with show-fullscreen, or drive the state with v-model:fullscreen. Either way the panel grows to the viewport from its side edge:
<SSheet v-model:open="open" v-model:fullscreen="fullscreen" show-fullscreen side="right" title="Settings">
<template #trigger><SButton>Open</SButton></template>
</SSheet>