Drawer
Panel that slides in from an edge of the screen.
- Browsers
- Chrome 123+Firefox 121+Safari 17.5+
- Tier
- 0
- Bundle compressed
- 4.01 kB
- Forced layout budget
- 0 ms
- Axe budget
- 0
- SSR compatible
- Yes
- Built on
- elements/dialog
- Published contract
- Test harness
Playground
Drawer
Closed
Snap points
Widths
Sides
import { DrawerPanel } from "@gunkin/uiify";
export default function Example() {
return (
<div className="">
<DrawerPanel
id="settings-drawer"
side="right"
trigger="Open settings"
title="Settings"
description="Adjust your preferences."
/>
</div>
);
}@layer components {
[data-uiify-drawer][data-part="content"] {
--uiify-drawer-inline-size: 32rem;
margin: 0;
inline-size: 100%;
max-inline-size: 100%;
max-block-size: 100dvh;
padding: var(--space-xl);
border: var(--border-width) solid var(--border);
background: var(--popover);
color: var(--popover-foreground);
box-shadow: var(--shadow-modal);
flex-direction: column;
gap: var(--space-md);
}
[data-uiify-drawer][data-part="content"][open] {
display: flex;
}
[data-uiify-drawer][data-size="sm"] {
--uiify-drawer-inline-size: 24rem;
}
[data-uiify-drawer][data-size="lg"] {
--uiify-drawer-inline-size: 40rem;
}
[data-uiify-drawer][data-size="full"] {
--uiify-drawer-inline-size: 100%;
}
[data-uiify-drawer][data-part="content"]::backdrop {
background: rgb(0 0 0 / 45%);
}
[data-uiify-drawer][data-backdrop="transparent"]::backdrop {
background: transparent;
}
[data-uiify-drawer][data-part="content"][data-side="bottom"] {
margin-block-start: auto;
block-size: min(var(--uiify-drawer-inline-size), 80dvh);
max-block-size: 80dvh;
border-start-start-radius: var(--radius-xl);
border-start-end-radius: var(--radius-xl);
transform: var(--uiify-drawer-transform, none);
}
[data-uiify-drawer][data-part="content"][data-side="top"] {
margin-block-end: auto;
block-size: min(var(--uiify-drawer-inline-size), 80dvh);
max-block-size: 80dvh;
border-end-start-radius: var(--radius-xl);
border-end-end-radius: var(--radius-xl);
transform: var(--uiify-drawer-transform, none);
}
[data-uiify-drawer][data-part="content"]:is([data-side="left"], [data-side="right"]) {
inline-size: min(var(--uiify-drawer-inline-size), 100%);
block-size: 100dvh;
transform: var(--uiify-drawer-transform, none);
}
[data-uiify-drawer][data-part="content"][data-side="left"] {
margin-inline-end: auto;
}
[data-uiify-drawer][data-part="content"][data-side="right"] {
margin-inline-start: auto;
border-start-end-radius: 0;
border-end-end-radius: 0;
}
:dir(rtl) [data-uiify-drawer][data-part="content"][data-side="left"] {
margin-inline: auto 0;
}
:dir(rtl) [data-uiify-drawer][data-part="content"][data-side="right"] {
margin-inline: 0 auto;
}
[data-uiify-drawer][data-part="content"][data-size="full"] {
inline-size: 100dvi;
max-inline-size: 100dvi;
block-size: 100dvh;
max-block-size: 100dvh;
}
[data-uiify-drawer] [data-part="header"] {
position: relative;
flex: none;
padding-inline-end: 2.75rem;
}
[data-uiify-drawer] [data-part="body"] {
min-block-size: 0;
overflow: auto;
overscroll-behavior: contain;
touch-action: pan-y;
}
[data-uiify-drawer] [data-part="footer"] {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: var(--space-sm);
}
[data-uiify-drawer] [data-part="close"] {
display: inline-grid;
place-items: center;
inline-size: 2.25rem;
block-size: 2.25rem;
padding: 0;
border: 0;
border-radius: 50%;
corner-shape: round;
background: var(--secondary);
color: var(--secondary-foreground);
}
[data-uiify-drawer] [data-part="header"] > [data-part="close"] {
position: absolute;
inset-block-start: 0;
inset-inline-end: 0;
}
[data-uiify-drawer] :is([data-part="grabber"], [data-part="handle"]) {
display: none;
flex: none;
block-size: 1.5rem;
touch-action: none;
}
[data-uiify-drawer] :is([data-part="grabber"], [data-part="handle"])::after {
content: "";
display: block;
inline-size: 2.25rem;
block-size: 0.25rem;
margin-block: 0.5rem 0;
margin-inline: auto;
border-radius: 999px;
background: var(--border);
}
[data-uiify-drawer][data-snap-enabled] [data-part="handle"] {
display: block;
}
@media (width < 40rem) {
[data-uiify-drawer][data-part="content"][data-side] {
inline-size: 100dvi;
max-inline-size: 100dvi;
block-size: auto;
min-block-size: 50dvh;
max-block-size: 80dvh;
margin-block: auto 0;
margin-inline: 0;
border-start-start-radius: var(--radius-xl);
border-start-end-radius: var(--radius-xl);
border-end-start-radius: 0;
border-end-end-radius: 0;
transform: var(--uiify-drawer-transform, none);
translate: 0 var(--uiify-sheet-offset, 0);
padding-block-end: calc(var(--space-xl) + env(safe-area-inset-bottom));
}
[data-uiify-drawer][data-part="content"][data-size="full"] {
block-size: 100dvh;
max-block-size: 100dvh;
border-radius: 0;
}
[data-uiify-drawer] :is([data-part="grabber"], [data-part="handle"]) {
display: block;
}
[data-uiify-drawer] [data-part="header"] {
touch-action: none;
}
[data-uiify-drawer][data-dragging] {
transition: none;
}
}
@media (prefers-reduced-motion: no-preference) {
[data-uiify-drawer][data-part="content"] {
transition:
opacity 180ms ease,
translate 220ms ease,
transform 220ms ease,
display 220ms allow-discrete,
overlay 220ms allow-discrete;
}
[data-uiify-drawer][data-part="content"]:not([open]) {
opacity: 0;
}
@starting-style {
[data-uiify-drawer][data-part="content"][open] {
opacity: 0;
}
}
}
}import { DrawerPanel } from "@gunkin/uiify";
export default function Example() {
return (
<div
className={
"[&[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)] [&_[data-positioning=native]]:[--uiify" +
"-anchor-offset:var(--space-sm)] [&[popover]:popover-open:not([data-positioning=native])]:[margin:revert] [&" +
"_[popover]:popover-open:not([data-positioning=native])]:[margin:revert] [&_[data-uiify-drawer][data-part=tr" +
"igger]]:relative [&_[data-uiify-drawer][data-part=trigger]]:inline-flex [&_[data-uiify-drawer][data-part=tr" +
"igger]]:min-h-11 [&_[data-uiify-drawer][data-part=trigger]]:min-w-11 [&_[data-uiify-drawer][data-part=trigg" +
"er]]:items-center [&_[data-uiify-drawer][data-part=trigger]]:justify-center [&_[data-uiify-drawer][data-par" +
"t=trigger]]:rounded-full [&_[data-uiify-drawer][data-part=trigger]]:border-[length:var(--border-width)] [&_" +
"[data-uiify-drawer][data-part=trigger]]:border-solid [&_[data-uiify-drawer][data-part=trigger]]:border-tran" +
"sparent [&_[data-uiify-drawer][data-part=trigger]]:bg-foreground [&_[data-uiify-drawer][data-part=trigger]]" +
":px-5 [&_[data-uiify-drawer][data-part=trigger]]:py-0 [&_[data-uiify-drawer][data-part=trigger]]:text-backg" +
"round [&_[data-uiify-drawer][data-part=trigger]]:text-base [&_[data-uiify-drawer][data-part=trigger]]:font-" +
"medium [&_[data-uiify-drawer][data-part=trigger]]:leading-6 [&_[data-uiify-drawer][data-part=trigger]]:no-u" +
"nderline [&_[data-uiify-drawer][data-part=trigger]]:cursor-pointer [&_[data-uiify-drawer][data-part=trigger" +
"]]:[transition:var(--transition-colors)] [&_[data-uiify-drawer][data-part=content]]:[--uiify-drawer-inline-" +
"size:32rem] [&_[data-uiify-drawer][data-size=sm][data-part=content]]:[--uiify-drawer-inline-size:24rem] [&_" +
"[data-uiify-drawer][data-size=lg][data-part=content]]:[--uiify-drawer-inline-size:40rem] [&_[data-uiify-dra" +
"wer][data-size=full][data-part=content]]:[--uiify-drawer-inline-size:100%] [&_[data-uiify-drawer][data-part" +
"=content]]:m-0 [&_[data-uiify-drawer][data-part=content]]:w-full [&_[data-uiify-drawer][data-part=content]]" +
":max-w-full [&_[data-uiify-drawer][data-part=content]]:max-h-dvh [&_[data-uiify-drawer][data-part=content]]" +
":p-6 [&_[data-uiify-drawer][data-part=content]]:border [&_[data-uiify-drawer][data-part=content]]:border-bo" +
"rder [&_[data-uiify-drawer][data-part=content]]:bg-popover [&_[data-uiify-drawer][data-part=content]]:text-" +
"popover-foreground [&_[data-uiify-drawer][data-part=content]]:shadow-modal [&_[data-uiify-drawer][data-part" +
"=content]]:flex-col [&_[data-uiify-drawer][data-part=content]]:gap-md [&_[data-uiify-drawer][data-part=cont" +
"ent]]:rounded-xl [&_[data-uiify-drawer][data-part=content]]:[corner-shape:squircle] [&_[data-uiify-drawer][" +
"data-part=content]::backdrop]:[background:rgb(0_0_0_/_45%)] [&_[data-uiify-drawer][data-part=content][data-" +
"backdrop=transparent]::backdrop]:bg-transparent [&_[data-uiify-drawer][data-part=content][open]]:flex [&_[d" +
"ata-uiify-drawer][data-part=content][data-side=right]]:ms-auto [&_[data-uiify-drawer][data-part=content][da" +
"ta-side=left]]:me-auto [&_[data-uiify-drawer][data-part=content][data-side=top]]:mb-auto [&_[data-uiify-dra" +
"wer][data-part=content][data-side=bottom]]:mt-auto [&_[data-uiify-drawer][data-part=content][data-side=bott" +
"om]]:h-[min(var(--uiify-drawer-inline-size),_80dvh)] [&_[data-uiify-drawer][data-part=content][data-side=to" +
"p]]:h-[min(var(--uiify-drawer-inline-size),_80dvh)] [&_[data-uiify-drawer][data-part=content][data-side=lef" +
"t]]:w-[min(var(--uiify-drawer-inline-size),_100%)] [&_[data-uiify-drawer][data-part=content][data-side=righ" +
"t]]:w-[min(var(--uiify-drawer-inline-size),_100%)] [&_[data-uiify-drawer][data-part=content]:is([data-side=" +
"left],[data-side=right])]:[block-size:100dvh] [&_[data-uiify-drawer][data-part=content][data-side=right]]:[" +
"border-start-end-radius:0] [&_[data-uiify-drawer][data-part=content][data-side=right]]:[border-end-end-radi" +
"us:0] [&_[data-uiify-drawer][data-part=content][data-side=left]]:[border-start-start-radius:0] [&_[data-uii" +
"fy-drawer][data-part=content][data-side=left]]:[border-end-start-radius:0] [&_[data-uiify-drawer][data-part" +
"=content][data-size=full]]:[inline-size:100dvi] [&_[data-uiify-drawer][data-part=content][data-size=full]]:" +
"[block-size:100dvh] [&_[data-uiify-drawer]_[data-part=header]]:relative [&_[data-uiify-drawer]_[data-part=h" +
"eader]]:flex-none [&_[data-uiify-drawer]_[data-part=header]]:pe-11 [&_[data-uiify-drawer]_[data-part=body]]" +
":min-h-0 [&_[data-uiify-drawer]_[data-part=body]]:overflow-auto [&_[data-uiify-drawer]_[data-part=body]]:ov" +
"erscroll-contain [&_[data-uiify-drawer]_[data-part=body]]:[touch-action:pan-y] [&_[data-uiify-drawer]_[data" +
"-part=footer]]:flex [&_[data-uiify-drawer]_[data-part=footer]]:flex-wrap [&_[data-uiify-drawer]_[data-part=" +
"footer]]:justify-end [&_[data-uiify-drawer]_[data-part=footer]]:gap-sm [&_[data-uiify-drawer]_[data-part=cl" +
"ose]]:inline-grid [&_[data-uiify-drawer]_[data-part=close]]:place-items-center [&_[data-uiify-drawer]_[data" +
"-part=close]]:size-9 [&_[data-uiify-drawer]_[data-part=close]]:p-0 [&_[data-uiify-drawer]_[data-part=close]" +
"]:border-0 [&_[data-uiify-drawer]_[data-part=close]]:rounded-full [&_[data-uiify-drawer]_[data-part=close]]" +
":bg-secondary [&_[data-uiify-drawer]_[data-part=close]]:text-secondary-foreground [&_[data-uiify-drawer]_[d" +
"ata-part=close]]:cursor-pointer [&_[data-uiify-drawer]_[data-part=header]>[data-part=close]]:absolute [&_[d" +
"ata-uiify-drawer]_[data-part=header]>[data-part=close]]:[inset-block-start:0] [&_[data-uiify-drawer]_[data-" +
"part=header]>[data-part=close]]:[inset-inline-end:0] [&_[data-uiify-drawer]_:is([data-part=grabber],[data-p" +
"art=handle])]:hidden [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:flex-none [&_[data" +
"-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:[block-size:1.5rem] [&_[data-uiify-drawer]_:is(" +
"[data-part=grabber],[data-part=handle])]:[touch-action:none] [&_[data-uiify-drawer]_:is([data-part=grabber]" +
",[data-part=handle])]:after:content-[''] [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])" +
"]:after:block [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:w-9 [&_[data-uiify-" +
"drawer]_:is([data-part=grabber],[data-part=handle])]:after:h-1 [&_[data-uiify-drawer]_:is([data-part=grabbe" +
"r],[data-part=handle])]:after:[margin-block:0.5rem_0] [&_[data-uiify-drawer]_:is([data-part=grabber],[data-" +
"part=handle])]:after:mx-auto [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:roun" +
"ded-full [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:bg-border max-[40rem]:[&" +
"_[data-uiify-drawer][data-part=content][data-side]]:[inline-size:100dvi] max-[40rem]:[&_[data-uiify-drawer]" +
"[data-part=content][data-side]]:[block-size:auto] max-[40rem]:[&_[data-uiify-drawer][data-part=content][dat" +
"a-side]]:[min-block-size:50dvh] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[max-bloc" +
"k-size:80dvh] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[margin-block:auto_0] max-[" +
"40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[margin-inline:0] max-[40rem]:[&_[data-uiify-d" +
"rawer][data-part=content][data-side]]:[border-start-start-radius:var(--radius-xl)] max-[40rem]:[&_[data-uii" +
"fy-drawer][data-part=content][data-side]]:[border-start-end-radius:var(--radius-xl)] max-[40rem]:[&_[data-u" +
"iify-drawer][data-part=content][data-side]]:[border-end-start-radius:0] max-[40rem]:[&_[data-uiify-drawer][" +
"data-part=content][data-side]]:[border-end-end-radius:0] max-[40rem]:[&_[data-uiify-drawer][data-part=conte" +
"nt][data-side]]:[padding-block-end:calc(var(--space-xl)_+_env(safe-area-inset-bottom))] max-[40rem]:[&_[dat" +
"a-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:block max-[40rem]:[&_[data-uiify-drawer]_[data" +
"-part=header]]:[touch-action:none] [&_[data-uiify-drawer][data-part=title]]:m-0 [&_[data-uiify-drawer][data" +
"-part=title]]:text-[length:var(--text-xl)] [&_[data-uiify-drawer][data-part=description]]:m-0 [&_[data-uiif" +
"y-drawer][data-part=description]]:text-muted-foreground [&_[data-uiify-drawer][data-part=description]]:text" +
"-sm [&_[data-uiify-drawer][data-part=handle]]:mx-auto [&_[data-uiify-drawer][data-part=handle]]:h-1 [&_[dat" +
"a-uiify-drawer][data-part=handle]]:w-10 [&_[data-uiify-drawer][data-part=handle]]:rounded-full [&_[data-uii" +
"fy-drawer][data-part=handle]]:bg-muted-foreground"
}
>
<DrawerPanel
id="settings-drawer"
side="right"
trigger="Open settings"
title="Settings"
description="Adjust your preferences."
/>
</div>
);
}/* app.css */
@layer reset, ui, app, tokens, base, demo, components, utilities, theme;
@import "@gunkin/uiify/styles/reset";
@import "@gunkin/uiify/components/behavior.css";
@import "@gunkin/uiify/styles/tokens";
@import "@gunkin/uiify/styles/tailwind";
@import "tailwindcss/utilities.css";
@theme { --spacing: 0.25rem; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; }
@source inline('[&[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)] [&_[data-positioning=native]]:[--uiify-anchor-offset:var(--space-sm)] [&[popover]:popover-open:not([data-positioning=native])]:[margin:revert] [&_[popover]:popover-open:not([data-positioning=native])]:[margin:revert] [&_[data-uiify-drawer][data-part=trigger]]:relative [&_[data-uiify-drawer][data-part=trigger]]:inline-flex [&_[data-uiify-drawer][data-part=trigger]]:min-h-11 [&_[data-uiify-drawer][data-part=trigger]]:min-w-11 [&_[data-uiify-drawer][data-part=trigger]]:items-center [&_[data-uiify-drawer][data-part=trigger]]:justify-center [&_[data-uiify-drawer][data-part=trigger]]:rounded-full [&_[data-uiify-drawer][data-part=trigger]]:border-[length:var(--border-width)] [&_[data-uiify-drawer][data-part=trigger]]:border-solid [&_[data-uiify-drawer][data-part=trigger]]:border-transparent [&_[data-uiify-drawer][data-part=trigger]]:bg-foreground [&_[data-uiify-drawer][data-part=trigger]]:px-5 [&_[data-uiify-drawer][data-part=trigger]]:py-0 [&_[data-uiify-drawer][data-part=trigger]]:text-background [&_[data-uiify-drawer][data-part=trigger]]:text-base [&_[data-uiify-drawer][data-part=trigger]]:font-medium [&_[data-uiify-drawer][data-part=trigger]]:leading-6 [&_[data-uiify-drawer][data-part=trigger]]:no-underline [&_[data-uiify-drawer][data-part=trigger]]:cursor-pointer [&_[data-uiify-drawer][data-part=trigger]]:[transition:var(--transition-colors)] [&_[data-uiify-drawer][data-part=content]]:[--uiify-drawer-inline-size:32rem] [&_[data-uiify-drawer][data-size=sm][data-part=content]]:[--uiify-drawer-inline-size:24rem] [&_[data-uiify-drawer][data-size=lg][data-part=content]]:[--uiify-drawer-inline-size:40rem] [&_[data-uiify-drawer][data-size=full][data-part=content]]:[--uiify-drawer-inline-size:100%] [&_[data-uiify-drawer][data-part=content]]:m-0 [&_[data-uiify-drawer][data-part=content]]:w-full [&_[data-uiify-drawer][data-part=content]]:max-w-full [&_[data-uiify-drawer][data-part=content]]:max-h-dvh [&_[data-uiify-drawer][data-part=content]]:p-6 [&_[data-uiify-drawer][data-part=content]]:border [&_[data-uiify-drawer][data-part=content]]:border-border [&_[data-uiify-drawer][data-part=content]]:bg-popover [&_[data-uiify-drawer][data-part=content]]:text-popover-foreground [&_[data-uiify-drawer][data-part=content]]:shadow-modal [&_[data-uiify-drawer][data-part=content]]:flex-col [&_[data-uiify-drawer][data-part=content]]:gap-md [&_[data-uiify-drawer][data-part=content]]:rounded-xl [&_[data-uiify-drawer][data-part=content]]:[corner-shape:squircle] [&_[data-uiify-drawer][data-part=content]::backdrop]:[background:rgb(0_0_0_/_45%)] [&_[data-uiify-drawer][data-part=content][data-backdrop=transparent]::backdrop]:bg-transparent [&_[data-uiify-drawer][data-part=content][open]]:flex [&_[data-uiify-drawer][data-part=content][data-side=right]]:ms-auto [&_[data-uiify-drawer][data-part=content][data-side=left]]:me-auto [&_[data-uiify-drawer][data-part=content][data-side=top]]:mb-auto [&_[data-uiify-drawer][data-part=content][data-side=bottom]]:mt-auto [&_[data-uiify-drawer][data-part=content][data-side=bottom]]:h-[min(var(--uiify-drawer-inline-size),_80dvh)] [&_[data-uiify-drawer][data-part=content][data-side=top]]:h-[min(var(--uiify-drawer-inline-size),_80dvh)] [&_[data-uiify-drawer][data-part=content][data-side=left]]:w-[min(var(--uiify-drawer-inline-size),_100%)] [&_[data-uiify-drawer][data-part=content][data-side=right]]:w-[min(var(--uiify-drawer-inline-size),_100%)] [&_[data-uiify-drawer][data-part=content]:is([data-side=left],[data-side=right])]:[block-size:100dvh] [&_[data-uiify-drawer][data-part=content][data-side=right]]:[border-start-end-radius:0] [&_[data-uiify-drawer][data-part=content][data-side=right]]:[border-end-end-radius:0] [&_[data-uiify-drawer][data-part=content][data-side=left]]:[border-start-start-radius:0] [&_[data-uiify-drawer][data-part=content][data-side=left]]:[border-end-start-radius:0] [&_[data-uiify-drawer][data-part=content][data-size=full]]:[inline-size:100dvi] [&_[data-uiify-drawer][data-part=content][data-size=full]]:[block-size:100dvh] [&_[data-uiify-drawer]_[data-part=header]]:relative [&_[data-uiify-drawer]_[data-part=header]]:flex-none [&_[data-uiify-drawer]_[data-part=header]]:pe-11 [&_[data-uiify-drawer]_[data-part=body]]:min-h-0 [&_[data-uiify-drawer]_[data-part=body]]:overflow-auto [&_[data-uiify-drawer]_[data-part=body]]:overscroll-contain [&_[data-uiify-drawer]_[data-part=body]]:[touch-action:pan-y] [&_[data-uiify-drawer]_[data-part=footer]]:flex [&_[data-uiify-drawer]_[data-part=footer]]:flex-wrap [&_[data-uiify-drawer]_[data-part=footer]]:justify-end [&_[data-uiify-drawer]_[data-part=footer]]:gap-sm [&_[data-uiify-drawer]_[data-part=close]]:inline-grid [&_[data-uiify-drawer]_[data-part=close]]:place-items-center [&_[data-uiify-drawer]_[data-part=close]]:size-9 [&_[data-uiify-drawer]_[data-part=close]]:p-0 [&_[data-uiify-drawer]_[data-part=close]]:border-0 [&_[data-uiify-drawer]_[data-part=close]]:rounded-full [&_[data-uiify-drawer]_[data-part=close]]:bg-secondary [&_[data-uiify-drawer]_[data-part=close]]:text-secondary-foreground [&_[data-uiify-drawer]_[data-part=close]]:cursor-pointer [&_[data-uiify-drawer]_[data-part=header]>[data-part=close]]:absolute [&_[data-uiify-drawer]_[data-part=header]>[data-part=close]]:[inset-block-start:0] [&_[data-uiify-drawer]_[data-part=header]>[data-part=close]]:[inset-inline-end:0] [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:hidden [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:flex-none [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:[block-size:1.5rem] [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:[touch-action:none] [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:block [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:w-9 [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:h-1 [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:[margin-block:0.5rem_0] [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:mx-auto [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:rounded-full [&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:bg-border max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[inline-size:100dvi] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[block-size:auto] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[min-block-size:50dvh] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[max-block-size:80dvh] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[margin-block:auto_0] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[margin-inline:0] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[border-start-start-radius:var(--radius-xl)] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[border-start-end-radius:var(--radius-xl)] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[border-end-start-radius:0] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[border-end-end-radius:0] max-[40rem]:[&_[data-uiify-drawer][data-part=content][data-side]]:[padding-block-end:calc(var(--space-xl)_+_env(safe-area-inset-bottom))] max-[40rem]:[&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:block max-[40rem]:[&_[data-uiify-drawer]_[data-part=header]]:[touch-action:none] [&_[data-uiify-drawer][data-part=title]]:m-0 [&_[data-uiify-drawer][data-part=title]]:text-[length:var(--text-xl)] [&_[data-uiify-drawer][data-part=description]]:m-0 [&_[data-uiify-drawer][data-part=description]]:text-muted-foreground [&_[data-uiify-drawer][data-part=description]]:text-sm [&_[data-uiify-drawer][data-part=handle]]:mx-auto [&_[data-uiify-drawer][data-part=handle]]:h-1 [&_[data-uiify-drawer][data-part=handle]]:w-10 [&_[data-uiify-drawer][data-part=handle]]:rounded-full [&_[data-uiify-drawer][data-part=handle]]:bg-muted-foreground');
@source inline("[&_[data-uiify-drawer]_:is([data-part=grabber],[data-part=handle])]:after:content-['']");
@layer base { :root { --uiify-control-text: var(--text-sm); } @media (pointer: coarse) { :root { --uiify-control-text: var(--text-base); } } }
/* Authored animation and registered properties used by the Tailwind skin.
* Visual rules belong in utility recipes; only at-rule definitions live here. */
@layer components {
@keyframes uiify-presence-in {
from {
opacity: 0;
scale: var(--presence-scale);
}
}
}
@layer components {
@keyframes uiify-presence-out {
to {
opacity: 0;
scale: var(--presence-scale);
}
}
}
@property --uiify-spinner-tail {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
@property --uiify-spinner-length {
syntax: "<angle>";
inherits: false;
initial-value: 36deg;
}
@layer components {
@keyframes uiify-spinner-rotate {
to {
transform: rotate(360deg);
}
}
}
@layer components {
@keyframes uiify-spinner-orbit {
0% {
--uiify-spinner-tail: 0deg;
--uiify-spinner-length: 36deg;
}
50% {
--uiify-spinner-tail: 90deg;
--uiify-spinner-length: 324deg;
}
100% {
--uiify-spinner-tail: 360deg;
--uiify-spinner-length: 36deg;
}
}
}
@layer components {
@keyframes uiify-skeleton-pulse {
50% {
opacity: 0.45;
}
}
}
@layer components {
@keyframes uiify-skeleton-shimmer {
to {
background-position: -200% 0;
}
}
}
@layer components {
@keyframes uiify-skeleton-reveal {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
}
@property --candle-body-height {
syntax: "<percentage>";
inherits: true;
initial-value: 0%;
}
@property --candle-body-top {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --candle-slot {
syntax: "<percentage>";
inherits: true;
initial-value: 100%;
}
@property --candle-wick-height {
syntax: "<percentage>";
inherits: true;
initial-value: 0%;
}
@property --candle-wick-top {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --candle-x {
syntax: "<percentage>";
inherits: true;
initial-value: 0%;
}
@property --candle-scale {
syntax: "<number>";
inherits: true;
initial-value: 1;
}
@property --line-y {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --line-prev-y {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
@property --line-count {
syntax: "<integer>";
inherits: false;
initial-value: 1;
}
@property --line-scale {
syntax: "<number>";
inherits: true;
initial-value: 1;
}
@layer components {
@keyframes uiify-otp-input-caret-blink {
50% {
opacity: 0;
}
}
}API
DrawerPanel
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "full" | "md" | Desktop widths: 24rem, 32rem, 40rem or available width. On mobile every size becomes a vertical bottom sheet. |
dismiss | "any" | "closerequest" | "none" | "any" | Native dismissal policy. Any permits backdrop, Escape and mobile downward swipe; none requires an explicit close control. |
backdrop | "opaque" | "transparent" | "opaque" | Backdrop appearance; does not change modal semantics. |
footer | ReactNode | — | Actions following the scrollable body. |
id* | string | — | Unique dialog id that the trigger and close button target, so the drawer opens and closes before hydration. |
trigger* | ReactNode | — | Content of the button that opens the drawer. |
title* | ReactNode | — | Heading rendered inside the drawer and used as its accessible name. |
description | ReactNode | — | Supporting text rendered under the title and referenced by aria-describedby. |
children | ReactNode | — | Body content placed between the description and the close button. |
side | "top" | "right" | "bottom" | "left" | "bottom" | Edge the drawer slides in from. |
closeLabel | string | "Close" | Accessible label for the circular close icon. |
className | string | — | Class applied to the dialog element. |
Drawer.Root (client)
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled open state. |
defaultOpen | boolean | — | Uncontrolled initial open state. |
onOpenChange | DialogChangeHandler | — | Called with the next open state and the details of what caused the change. |
side | "top" | "right" | "bottom" | "left" | "bottom" | Edge the drawer slides in from, which also sets the drag axis. |
modal | boolean | true | Open with showModal() so the rest of the page is inert behind a backdrop. |
dismissible | boolean | true | Allow a drag past the last snap point to close the drawer. |
snapPoints | readonly number[] | — | Fractions of the drawer size the panel settles on when dragged. |
activeSnapPoint | number | — | Controlled snap point, as one of the values in snapPoints. |
defaultActiveSnapPoint | number | — | Uncontrolled initial snap point. |
onSnapChange | (snap: number) => void | — | Called with the snap point the drawer settled on. |
children | ReactNode | — | The Trigger, Content and inner parts. |
Drawer.Trigger (client)
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Class applied to the trigger button. |
onClick | (event: MouseEvent<HTMLButtonElement>) => void | — | Called when the trigger is clicked, before the drawer opens. |
ref | Ref<HTMLButtonElement> | — | Ref to the trigger button, which is refocused when the drawer closes. |
children | ReactNode | — | Content of the trigger button. |
Drawer.Content (client)
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Class applied to the dialog element. |
style | CSSProperties | — | Inline style merged with the drag transform the drawer applies. |
onCancel | ReactEventHandler<HTMLDialogElement> | — | Called on the native dialog cancel event, such as pressing Escape. |
onClose | ReactEventHandler<HTMLDialogElement> | — | Called on the native dialog close event. |
ref | Ref<HTMLDialogElement> | — | Ref to the dialog element. |
children | ReactNode | — | Content of the drawer. |
Drawer.Title (client)
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Class applied to the title element. |
children | ReactNode | — | Content of the title, which becomes the drawer's accessible name. |
Drawer.Description (client)
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Class applied to the description element. |
children | ReactNode | — | Content of the description, referenced by aria-describedby. |
Drawer.Close (client)
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Class applied to the close button. |
onClick | (event: MouseEvent<HTMLButtonElement>) => void | — | Called when the close button is clicked, before the drawer closes. |
children | ReactNode | — | Content of the close button. |
Drawer.Handle (client)
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | Class applied to the drag handle. |
onPointerDown | (event: PointerEvent<HTMLDivElement>) => void | — | Called when a drag starts on the handle. |
onPointerMove | (event: PointerEvent<HTMLDivElement>) => void | — | Called as the pointer moves during a drag. |
onPointerUp | (event: PointerEvent<HTMLDivElement>) => void | — | Called when the drag ends and the drawer settles on a snap point. |
onPointerCancel | (event: PointerEvent<HTMLDivElement>) => void | — | Called when the browser cancels the drag. |
Keyboard
| Key | Action |
|---|---|
| Enter / Space | Open the dialog from its trigger or activate Close |
| Escape | Request dismissal unless dismiss is none |
| Tab / Shift+Tab | Move within the modal dialog |
Accessibility
DrawerPanel uses a dialog with target ids, so opening, dismissal, focus containment and focus return work before hydration; dismiss controls backdrop and Escape. Panels come in sm, md, lg and full widths and become bottom sheets on phones. Swipe starts on the handle or header, so the body still scrolls. The client API adds controlled state and snap points.
Styling hooks
data-uiify-drawerdata-partdata-sidedata-sizedata-backdropdata-statedata-dragging--uiify-drawer-inline-size--uiify-drawer-transform--uiify-sheet-offset