Drawer A panel from a screen edge.
Anatomy copy copy < script >
import { Drawer } from '@shardsui/svelte/drawer'
</ script >
< Drawer . Provider >
< Drawer . IndentBackground />
< Drawer . Indent >
< Drawer . Root >
< Drawer . Trigger />
< Drawer . SwipeArea />
< Drawer . Portal >
< Drawer . Backdrop />
< Drawer . Viewport >
< Drawer . Popup >
< Drawer . Content >
< Drawer . Title />
< Drawer . Description />
< Drawer . Close />
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
</ Drawer . Root >
</ Drawer . Indent >
</ Drawer . Provider > Swipe gestures dismiss the drawer; swipeDirection sets which direction. <Drawer.Viewport> owns those gestures and the touch scroll locking, so <Drawer.Popup> must render inside it. <Drawer.Content> lets mouse users select text in its children without swipe interference; add data-shards-ui-swipe-ignore to a descendant to opt it out of swipe dismissal for all input types.
In Chromium on Android, the system back gesture closes the topmost open drawer.
Usage guidelines Drawer extends Dialog : it adds gesture support, snap points, and indent effects. If you don't need these, a slide-in panel is just a positioned Dialog — use Dialog instead.Examples State By default, Drawer manages its own state.
copy copy < Drawer . Root >
< Drawer . Trigger >Open</ Drawer . Trigger >
< Drawer . Portal >
< Drawer . Viewport >
< Drawer . Popup >
< Drawer . Content >
< Drawer . Title >Example drawer</ Drawer . Title >
< Drawer . Close >Close</ Drawer . Close >
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
</ Drawer . Root > Drive open with open / onOpenChange, or bind:open.
copy copy < script >
let open = $ state ( false )
</ script >
< Drawer . Root { open } onOpenChange ={( next ) => (open = next)}>
< Drawer . Trigger >Open</ Drawer . Trigger >
< Drawer . Portal >
< Drawer . Viewport >
< Drawer . Popup >
< Drawer . Content >
< Drawer . Title >Example drawer</ Drawer . Title >
< Drawer . Close >Close</ Drawer . Close >
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
</ Drawer . Root > Position Positioning is handled by your styles. swipeDirection defaults to "down" for bottom sheets. Use "up", "left", or "right" for other drawer positions.
copy copy < Drawer . Root swipeDirection = "left" >...</ Drawer . Root > Nested drawers Use the [data-nested-drawer-open] selector and the --nested-drawers CSS variable to style drawers when a nested drawer is open. This demo stacks nested drawers with a constant peek: the frontmost drawer stays anchored to the bottom while the ones behind are scaled down and lifted, using the --drawer-height and --drawer-frontmost-height CSS variables to handle varying heights.
copy copy < script lang = "ts" >
import { Drawer } from '@shardsui/svelte/drawer'
</ script >
< Drawer . Root >
< Drawer . Trigger
class = "flex h-8 items-center justify-center rounded-md border border-gray-200 bg-gray-50 px-3 text-sm font-normal text-gray-900 select-none hover:bg-gray-100 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:bg-gray-100"
>
Open group
</ Drawer . Trigger >
< Drawer . Portal >
< Drawer . Backdrop
class = "fixed inset-0 min-h-dvh bg-black opacity-[calc(var(--backdrop-opacity)*(1-var(--drawer-swipe-progress)))] transition-opacity duration-450 ease-[cubic-bezier(0.32,0.72,0,1)] [--backdrop-opacity:0.2] data-ending-style:opacity-0 data-ending-style:duration-[calc(var(--drawer-swipe-strength)*0.4s)] data-starting-style:opacity-0 data-swiping:duration-0 supports-[-webkit-touch-callout:none]:absolute"
/>
< Drawer . Viewport class = "fixed inset-0 flex items-end justify-center" >
< Drawer . Popup
class = "group/popup relative -mb-12 h-(--drawer-height,auto) max-h-[calc(80vh+3rem)] w-full origin-[50%_calc(100%-var(--bleed))] transform-[translateY(calc(var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--shrink)*var(--height))))_scale(var(--scale))] touch-auto overflow-y-auto overscroll-contain rounded-t-2xl bg-gray-50 px-4 pt-4 pb-[calc(1.5rem+env(safe-area-inset-bottom,0)+3rem)] text-gray-900 outline-1 outline-gray-200 [--bleed:3rem] [--height:max(0px,calc(var(--drawer-frontmost-height,var(--drawer-height))-var(--bleed)))] [--peek:1rem] [--scale-base:calc(max(0,1-(var(--nested-drawers)*var(--stack-step))))] [--scale:clamp(0,calc(var(--scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--shrink:calc(1-var(--scale))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-step:0.05] [transition:transform_450ms_cubic-bezier(0.32,0.72,0,1),height_450ms_cubic-bezier(0.32,0.72,0,1),opacity_450ms_cubic-bezier(0.32,0.72,0,1)] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-transparent after:transition-[background-color] after:duration-450 after:ease-[cubic-bezier(0.32,0.72,0,1)] after:content-[''] data-ending-style:transform-[translateY(calc(100%-var(--bleed)+2px))] data-ending-style:opacity-[0.9999] data-ending-style:duration-[calc(var(--drawer-swipe-strength)*0.4s)] data-nested-drawer-open:h-[calc(var(--height)+var(--bleed))] data-nested-drawer-open:overflow-hidden data-nested-drawer-open:after:bg-black/5 data-nested-drawer-swiping:duration-0 data-ending-style:data-nested-drawer-swiping:duration-[calc(var(--drawer-swipe-strength)*0.4s)] data-starting-style:transform-[translateY(calc(100%-var(--bleed)+2px))] data-swiping:duration-0 data-swiping:select-none data-ending-style:data-swiping:duration-[calc(var(--drawer-swipe-strength)*0.4s)]"
>
< div
class = "mx-auto mb-4 h-1 w-12 rounded-full bg-gray-300 transition-opacity duration-200 group-data-nested-drawer-open/popup:opacity-0 group-data-nested-drawer-swiping/popup:opacity-100"
></ div >
< Drawer . Content
class = "mx-auto w-full max-w-lg transition-opacity duration-300 ease-[cubic-bezier(0.45,1.005,0,1.005)] group-data-nested-drawer-open/popup:opacity-0 group-data-nested-drawer-swiping/popup:opacity-100"
>
< Drawer . Title class = "mb-1 text-center text-base font-semibold" >Group</ Drawer . Title >
< Drawer . Description class = "mb-4 text-center text-sm text-gray-600" >
Nested drawers stack while each stays independently focus managed.
</ Drawer . Description >
< div class = "flex items-center justify-end gap-4" >
< div class = "mr-auto" >
< Drawer . Root >
< Drawer . Trigger
class = "-m-0.5 rounded px-1.5 py-0.5 text-sm text-gray-950 hover:bg-gray-950/5 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:bg-gray-950/10"
>
Invite member
</ Drawer . Trigger >
< Drawer . Portal >
< Drawer . Viewport class = "fixed inset-0 flex items-end justify-center" >
< Drawer . Popup
class = "group/popup relative -mb-12 h-(--drawer-height,auto) max-h-[calc(80vh+3rem)] w-full origin-[50%_calc(100%-var(--bleed))] transform-[translateY(calc(var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--shrink)*var(--height))))_scale(var(--scale))] touch-auto overflow-y-auto overscroll-contain rounded-t-2xl bg-gray-50 px-4 pt-4 pb-[calc(1.5rem+env(safe-area-inset-bottom,0)+3rem)] text-gray-900 outline-1 outline-gray-200 [--bleed:3rem] [--height:max(0px,calc(var(--drawer-frontmost-height,var(--drawer-height))-var(--bleed)))] [--peek:1rem] [--scale-base:calc(max(0,1-(var(--nested-drawers)*var(--stack-step))))] [--scale:clamp(0,calc(var(--scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--shrink:calc(1-var(--scale))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-step:0.05] [transition:transform_450ms_cubic-bezier(0.32,0.72,0,1),height_450ms_cubic-bezier(0.32,0.72,0,1),opacity_450ms_cubic-bezier(0.32,0.72,0,1)] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-transparent after:transition-[background-color] after:duration-450 after:ease-[cubic-bezier(0.32,0.72,0,1)] after:content-[''] data-ending-style:transform-[translateY(calc(100%-var(--bleed)+2px))] data-ending-style:opacity-[0.9999] data-ending-style:duration-[calc(var(--drawer-swipe-strength)*0.4s)] data-nested-drawer-open:h-[calc(var(--height)+var(--bleed))] data-nested-drawer-open:overflow-hidden data-nested-drawer-open:after:bg-black/5 data-nested-drawer-swiping:duration-0 data-ending-style:data-nested-drawer-swiping:duration-[calc(var(--drawer-swipe-strength)*0.4s)] data-starting-style:transform-[translateY(calc(100%-var(--bleed)+2px))] data-swiping:duration-0 data-swiping:select-none data-ending-style:data-swiping:duration-[calc(var(--drawer-swipe-strength)*0.4s)]"
>
< div
class = "mx-auto mb-4 h-1 w-12 rounded-full bg-gray-300 transition-opacity duration-200 group-data-nested-drawer-open/popup:opacity-0 group-data-nested-drawer-swiping/popup:opacity-100"
></ div >
< Drawer . Content
class = "mx-auto w-full max-w-lg transition-opacity duration-300 ease-[cubic-bezier(0.45,1.005,0,1.005)] group-data-nested-drawer-open/popup:opacity-0 group-data-nested-drawer-swiping/popup:opacity-100"
>
< Drawer . Title class = "mb-1 text-center text-base font-semibold" >
Invite member
</ Drawer . Title >
< Drawer . Description class = "mb-4 text-center text-sm text-gray-600" >
This drawer is taller to show variable-height stacking.
</ Drawer . Description >
< div class = "mb-4 grid gap-1.5 text-left" >
< label class = "text-sm font-semibold text-gray-700" for = "invite-email" >
Email
</ label >
< input
id = "invite-email"
class = "w-full rounded-md border border-gray-200 bg-gray-50 px-2.5 py-2 text-sm font-normal text-gray-900 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 any-pointer-coarse:text-base"
type = "email"
value = "member@example.com"
/>
</ div >
< div class = "mb-4 grid gap-1.5 text-left" >
< label class = "text-sm font-semibold text-gray-700" for = "invite-note" >
Note
</ label >
< textarea
id = "invite-note"
class = "w-full resize-y rounded-md border border-gray-200 bg-gray-50 px-2.5 py-2 text-sm font-normal text-gray-900 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 any-pointer-coarse:text-base"
rows ={ 3 }>Join us for the next review session!</ textarea
>
</ div >
< div class = "flex items-center justify-between" >
< Drawer . Root >
< Drawer . Trigger
class = "-m-0.5 rounded px-1.5 py-0.5 text-sm text-gray-950 hover:bg-gray-950/5 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:bg-gray-950/10"
>
Set permissions
</ Drawer . Trigger >
< Drawer . Portal >
< Drawer . Viewport class = "fixed inset-0 flex items-end justify-center" >
< Drawer . Popup
class = "group/popup relative -mb-12 h-(--drawer-height,auto) max-h-[calc(80vh+3rem)] w-full origin-[50%_calc(100%-var(--bleed))] transform-[translateY(calc(var(--drawer-swipe-movement-y)-var(--stack-peek-offset)-(var(--shrink)*var(--height))))_scale(var(--scale))] touch-auto overflow-y-auto overscroll-contain rounded-t-2xl bg-gray-50 px-4 pt-4 pb-[calc(1.5rem+env(safe-area-inset-bottom,0)+3rem)] text-gray-900 outline-1 outline-gray-200 [--bleed:3rem] [--height:max(0px,calc(var(--drawer-frontmost-height,var(--drawer-height))-var(--bleed)))] [--peek:1rem] [--scale-base:calc(max(0,1-(var(--nested-drawers)*var(--stack-step))))] [--scale:clamp(0,calc(var(--scale-base)+(var(--stack-step)*var(--stack-progress))),1)] [--shrink:calc(1-var(--scale))] [--stack-peek-offset:max(0px,calc((var(--nested-drawers)-var(--stack-progress))*var(--peek)))] [--stack-progress:clamp(0,var(--drawer-swipe-progress),1)] [--stack-step:0.05] [transition:transform_450ms_cubic-bezier(0.32,0.72,0,1),height_450ms_cubic-bezier(0.32,0.72,0,1),opacity_450ms_cubic-bezier(0.32,0.72,0,1)] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-transparent after:transition-[background-color] after:duration-450 after:ease-[cubic-bezier(0.32,0.72,0,1)] after:content-[''] data-ending-style:transform-[translateY(calc(100%-var(--bleed)+2px))] data-ending-style:opacity-[0.9999] data-ending-style:duration-[calc(var(--drawer-swipe-strength)*0.4s)] data-nested-drawer-open:h-[calc(var(--height)+var(--bleed))] data-nested-drawer-open:overflow-hidden data-nested-drawer-open:after:bg-black/5 data-nested-drawer-swiping:duration-0 data-ending-style:data-nested-drawer-swiping:duration-[calc(var(--drawer-swipe-strength)*0.4s)] data-starting-style:transform-[translateY(calc(100%-var(--bleed)+2px))] data-swiping:duration-0 data-swiping:select-none data-ending-style:data-swiping:duration-[calc(var(--drawer-swipe-strength)*0.4s)]"
>
< div
class = "mx-auto mb-4 h-1 w-12 rounded-full bg-gray-300 transition-opacity duration-200 group-data-nested-drawer-open/popup:opacity-0 group-data-nested-drawer-swiping/popup:opacity-100"
></ div >
< Drawer . Content
class = "mx-auto w-full max-w-lg transition-opacity duration-300 ease-[cubic-bezier(0.45,1.005,0,1.005)] group-data-nested-drawer-open/popup:opacity-0 group-data-nested-drawer-swiping/popup:opacity-100"
>
< Drawer . Title class = "mb-1 text-center text-base font-semibold" >
Permissions
</ Drawer . Title >
< Drawer . Description
class = "mb-4 text-center text-sm text-gray-600"
>
The stack keeps growing — each drawer scales the ones beneath
it.
</ Drawer . Description >
< div class = "mb-4 grid gap-2 text-left" >
{# each [ 'Editor' , 'Reviewer' , 'Viewer' ] as role (role)}
< label
class = "flex items-center gap-2.5 text-sm text-gray-700"
>
< input
type = "radio"
name = "role"
value ={role}
checked ={role === 'Editor' }
class = "size-4 accent-gray-900"
/>
{role}
</ label >
{/ each }
</ div >
< div class = "flex justify-end" >
< Drawer . Close
class = "flex h-8 items-center justify-center rounded-md border border-gray-200 bg-gray-50 px-3 text-sm font-normal text-gray-900 select-none hover:bg-gray-100 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:bg-gray-100"
>
Done
</ Drawer . Close >
</ div >
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
</ Drawer . Root >
< Drawer . Close
class = "flex h-8 items-center justify-center rounded-md border border-gray-200 bg-gray-50 px-3 text-sm font-normal text-gray-900 select-none hover:bg-gray-100 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:bg-gray-100"
>
Send invite
</ Drawer . Close >
</ div >
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
</ Drawer . Root >
</ div >
< Drawer . Close
class = "flex h-8 items-center justify-center rounded-md border border-gray-200 bg-gray-50 px-3 text-sm font-normal text-gray-900 select-none hover:bg-gray-100 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-gray-950 active:bg-gray-100"
>
Close
</ Drawer . Close >
</ div >
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
</ Drawer . Root > Snap points Use snapPoints to snap a bottom sheet to preset heights. Numbers up to 1 are fractions of the viewport height, numbers above 1 are pixel values, and strings support px and rem units ('148px', '30rem'). Snap points only apply when swipeDirection is "up" or "down".
copy copy < script >
const snapPoints = [ '148px' , 1 ]
let snapPoint = $ state (snapPoints[ 0 ])
</ script >
< Drawer . Root { snapPoints } bind : snapPoint >...</ Drawer . Root > Apply the snap point offset in your styles when using vertical drawers:
copy copy .drawer-popup {
transform : translateY ( calc ( var ( --drawer-snap-point-offset ) + var ( --drawer-swipe-movement-y )));
} Fast swipes can skip snap points. Set snapToSequentialPoints to disable velocity-based skipping so drag distance determines the snap target (you can still drag past multiple points).
Indent effect To scale the background down whenever a drawer opens, wrap your app in <Drawer.Provider> and place <Drawer.IndentBackground> + <Drawer.Indent> at the top of your tree. Both parts carry data-active while any <Drawer.Root> inside the provider is open.
Non-modal Set modal={false} to opt out of focus trapping and disablePointerDismissal to keep the drawer open on outside clicks.
Mobile navigation Build a full-screen mobile navigation sheet from Drawer parts, with flick-to-dismiss.
Swipe to open Place <Drawer.SwipeArea> along the edge of the viewport to enable swipe-to-open gestures. It is hidden from assistive technology, so keep a <Drawer.Trigger> as the accessible way to open the drawer.
Close confirmation A nested confirmation dialog guards against losing work: it opens when the text typed into the drawer is about to be discarded.
Veto the close with a function binding — bind:open={() => open, (next) => …}. When a close is requested the setter runs; if you don't commit the new value, the getter keeps returning the old one and the drawer stays open. Open the confirmation there instead, so the prompt appears whether the user clicks the backdrop, presses Esc, hits a close button, or swipes to dismiss.
copy copy < Drawer . Root
bind : open ={
() => open,
( next ) => {
if ( ! next && hasUnsavedChanges) return // veto: don't commit, drawer stays open
open = next
}
}
> Action sheet with separate destructive action An action sheet pairing a grouped list of actions with a separate, destructive action button.
Detached triggers For a simple one-off, keep <Drawer.Trigger> inside <Drawer.Root>. When the content can't sit beside its trigger, detach it: create a handle with new Drawer.Handle() and pass it to both <Drawer.Trigger handle={...}> and <Drawer.Root handle={...}>. They stay linked no matter where each lives in the tree.
copy copy < script >
const demoDrawer = new Drawer. Handle ()
</ script >
< Drawer . Trigger handle ={demoDrawer} >Open</ Drawer . Trigger >
< Drawer . Root handle ={demoDrawer} >
< Drawer . Portal >
< Drawer . Viewport >
< Drawer . Popup >
< Drawer . Content >
< Drawer . Title >Drawer</ Drawer . Title >
< Drawer . Close >Close</ Drawer . Close >
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
</ Drawer . Root > To show different content depending on which trigger opened the drawer, pass a payload to each <Drawer.Trigger> and read it through the children snippet on <Drawer.Root>. Give new Drawer.Handle() a type argument to type the payload:
copy copy < script >
const demoDrawer = new Drawer. Handle <{ title : string }>()
</ script >
< Drawer . Trigger handle ={demoDrawer} payload ={{ title: 'Profile' }}>Profile</ Drawer . Trigger >
< Drawer . Trigger handle ={demoDrawer} payload ={{ title: 'Settings' }}>Settings</ Drawer . Trigger >
< Drawer . Root handle ={demoDrawer}>
{# snippet children ({ payload })}
< Drawer . Portal >
< Drawer . Viewport >
< Drawer . Popup >
< Drawer . Content >
{# if payload !== undefined }
< Drawer . Title >{ payload .title}</ Drawer . Title >
{/ if }
< Drawer . Close >Close</ Drawer . Close >
</ Drawer . Content >
</ Drawer . Popup >
</ Drawer . Viewport >
</ Drawer . Portal >
{/ snippet }
</ Drawer . Root > Stacking and animations Use CSS transitions or animations to animate drawer opening, closing, swipe interactions, and nested stacking. data-starting-style is applied when a drawer starts to open, data-ending-style when it starts to close.
The --nested-drawers CSS variable gives the stack depth; the frontmost drawer has index 0.
copy copy .drawer-popup {
--stack-step : 0.05 ;
--stack-scale : calc ( 1 - ( var ( --nested-drawers ) * var ( --stack-step )));
transform : translateY ( var ( --drawer-swipe-movement-y )) scale ( var ( --stack-scale ));
} When stacked drawers have varying heights, use --drawer-height and --drawer-frontmost-height to keep collapsed drawers aligned with the frontmost one.
copy copy .drawer-popup {
--bleed : 3 rem ;
--stack-height : max (
0 px ,
calc ( var ( --drawer-frontmost-height , var ( --drawer-height )) - var ( --bleed ))
);
height : var ( --drawer-height , auto );
}
.drawer-popup [ data-nested-drawer-open ] {
height : calc ( var ( --stack-height ) + var ( --bleed ));
overflow : hidden ;
} The data-nested-drawer-open attribute marks drawers behind the frontmost drawer. Use it with data-nested-drawer-swiping to fade parent drawer content to zero opacity, so it stays mounted and laid out during nested swipe interactions.
copy copy .drawer-content {
transition : opacity 300 ms ;
}
.drawer-popup [ data-nested-drawer-open ] .drawer-content {
opacity : 0 ;
}
.drawer-popup [ data-nested-drawer-open ][ data-nested-drawer-swiping ] .drawer-content {
opacity : 1 ;
} Use the --drawer-swipe-movement-x, --drawer-swipe-movement-y, and --drawer-snap-point-offset CSS variables for drag and snap offsets:
copy copy .drawer-popup [ data-swipe-direction = 'right' ] {
transform : translateX ( var ( --drawer-swipe-movement-x ));
}
.drawer-popup [ data-swipe-direction = 'down' ] {
transform : translateY ( calc ( var ( --drawer-snap-point-offset ) + var ( --drawer-swipe-movement-y )));
} Combine data-swipe-direction with data-ending-style to animate directional dismissal:
copy copy .drawer-popup [ data-ending-style ][ data-swipe-direction = 'right' ] {
transform : translateX ( 100 % );
}
.drawer-popup [ data-ending-style ][ data-swipe-direction = 'down' ] {
transform : translateY ( 100 % );
} Use --drawer-swipe-progress to fade the backdrop as the drawer is swiped, and --drawer-swipe-strength to scale release transition durations based on swipe velocity.
copy copy .drawer-backdrop {
--backdrop-opacity : 0.2 ;
opacity : calc ( var ( --backdrop-opacity ) * ( 1 - var ( --drawer-swipe-progress )));
}
.drawer-popup [ data-ending-style ],
.drawer-backdrop [ data-ending-style ] {
transition-duration : calc ( var ( --drawer-swipe-strength ) * 400 ms );
}
.drawer-popup [ data-swiping ],
.drawer-backdrop [ data-swiping ] {
transition-duration : 0 ms ;
} API reference Provider Tracks the open state of every drawer inside it, driving <Drawer.Indent> and <Drawer.IndentBackground>.
Doesn't render its own HTML element.
IndentBackground A background layer placed before <Drawer.Indent>, shown behind the app while a drawer indents it.
Renders a <div> element.
Indent A wrapper element intended to contain your app's main UI.
Renders a <div> element.
Root Groups all parts of the drawer.
Doesn't render its own HTML element.
Trigger A button that opens the drawer.
Renders a <button> element.
SwipeArea An invisible area that listens for swipe gestures to open the drawer.
Renders a <div> element.
Portal A portal that moves the popup out to <body>, clear of ancestor clipping and stacking.
Renders a <div> element.
Backdrop An overlay displayed beneath the popup.
Renders a <div> element.
Viewport A positioning container for the drawer popup that can be made scrollable.
Owns the swipe gestures and the touch scroll locking, so <Drawer.Popup> must render inside it.
Renders a <div> element.
A container for the drawer contents.
Renders a <div> element.
Content An inner container whose children can be selected with a mouse or pen without starting a swipe.
Renders a <div> element.
Title A heading that labels the drawer.
Renders an <h2> element.
Description A paragraph with additional information about the drawer.
Renders a <p> element.
Close A button that closes the drawer.
Renders a <button> element.
Handle Connects a <Drawer.Root> with detached <Drawer.Trigger> components, and controls the drawer imperatively. Pass a type argument to type the payload.
copy copy const drawer = new Drawer. Handle < Payload >()