Drawer
HeroUIA dialog that slides in from an edge, HeroUI's on Kobalte's.
Solidtype_heroui.Drawer is Kobalte’s Dialog sliding in from an edge of
the page, with the classes HeroUI’s own drawerVariants gives. It is a
Modal in all but these: its content slides in
from the edge its placement names; its trigger is a button; and while it
may be dismissed its dialog is dragged toward its edge to close, as HeroUI’s
is.
module Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
let make () = <Drawer> <Button variant=`secondary>"Open Drawer"</Button> <Drawer.Backdrop> <Drawer.Content placement=`right> <Drawer.Dialog> <Drawer.Header> <Drawer.Heading>"Drawer Title"</Drawer.Heading> </Drawer.Header> <Drawer.Body> <p> "This is a bottom drawer built with React Aria's Modal \ component. It slides up from the bottom of the screen with a \ smooth CSS transition." </p> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Cancel"</Button> <Button slot=`close>"Confirm"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>Anatomy
Section titled “Anatomy”<Drawer> <Button>"Open Drawer"</Button> <Drawer.Backdrop> <Drawer.Content placement=`bottom> <Drawer.Dialog> <Drawer.Handle /> <Drawer.Close_trigger /> <Drawer.Header> <Drawer.Heading>...</Drawer.Heading> </Drawer.Header> <Drawer.Body>...</Drawer.Body> <Drawer.Footer>...</Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop></Drawer>Examples
Section titled “Examples”Placement
Section titled “Placement”module Jsx = Solidtype.Jsxmodule For = Solidtype.Formodule Show = Solidtype.Showmodule Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
let placements = [| (`bottom, "bottom"); (`top, "top"); (`left, "left"); (`right, "right") |]
let make () = <div class_="flex flex-wrap gap-4"> <For each=(fun () -> placements)> (fun (placement, name) _ -> <Drawer> <Button variant=`secondary> (fun () -> Jsx.string (String.capitalize_ascii name)) </Button> <Drawer.Backdrop> <Drawer.Content placement=(fun () -> placement)> <Drawer.Dialog> <Drawer.Close_trigger /> <Show when_=(fun () -> match placement with | `bottom -> true | `top | `left | `right -> false)> <Drawer.Handle /> </Show> <Drawer.Header> <Drawer.Heading> (fun () -> Jsx.string (String.capitalize_ascii name)) " Drawer" </Drawer.Heading> </Drawer.Header> <Drawer.Body> <p> "This drawer slides in from the " <strong>(fun () -> Jsx.string name)</strong> " edge of the screen." </p> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Cancel"</Button> <Button slot=`close>"Done"</Button> </Drawer.Footer> <Show when_=(fun () -> match placement with | `top -> true | `bottom | `left | `right -> false)> <Drawer.Handle /> </Show> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>) </For> </div>Non-Dismissable
Section titled “Non-Dismissable”module Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
let make () = <Drawer> <Button variant=`secondary>"Important Action"</Button> <Drawer.Backdrop is_dismissable=false> <Drawer.Content> <Drawer.Dialog> <Drawer.Header> <Drawer.Heading>"Confirm Action"</Drawer.Heading> </Drawer.Header> <Drawer.Body> <p> "This drawer cannot be dismissed by clicking outside or \ dragging. You must use one of the buttons below." </p> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Cancel"</Button> <Button slot=`close>"Confirm"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>Scrollable Content
Section titled “Scrollable Content”module Jsx = Solidtype.Jsxmodule For = Solidtype.Formodule Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
let paragraphs = Array.init 20 (fun i -> i + 1)
let make () = <Drawer> <Button variant=`secondary>"Terms & Conditions"</Button> <Drawer.Backdrop> <Drawer.Content> <Drawer.Dialog> <Drawer.Handle /> <Drawer.Close_trigger /> <Drawer.Header> <Drawer.Heading>"Terms & Conditions"</Drawer.Heading> </Drawer.Header> <Drawer.Body> <For each=(fun () -> paragraphs)> (fun n _ -> <p class_="mb-3"> "Paragraph " (fun () -> Jsx.int n) ": Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ Nullam pulvinar risus non risus hendrerit venenatis. \ Pellentesque sit amet hendrerit risus, sed porttitor quam." </p>) </For> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Decline"</Button> <Button slot=`close>"Accept"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>Controlled State
Section titled “Controlled State”A signal is Solid’s state: each half keeps the drawer’s open state in one, where HeroUI’s uses React.useState and useOverlayState.
With a signal
Control the drawer using Solid's signal for simple state management.
Status: closed
With a signal's setter
Use the signal's setter for a cleaner API with convenient calls like S.set set_open true, S.set set_open false, and S.update set_open not.
Status: closed
module S = Solidtype.Solidmodule Jsx = Solidtype.Jsxmodule Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
(* HeroUI shows React's useState and its own useOverlayState; a signal is Solid's state, so both halves keep the drawer's in one. *)let make () = let is_open, set_open = S.create_signal false in let state_open, set_state_open = S.create_signal false in <div class_="flex max-w-md flex-col gap-8"> <div class_="flex flex-col gap-3"> <h3 class_="text-lg font-semibold text-foreground">"With a signal"</h3> <p class_="text-sm leading-relaxed text-pretty text-muted"> "Control the drawer using Solid's " <code class_="text-foreground">"signal"</code> " for simple state management." </p> <div class_="flex flex-col items-start gap-3 rounded-2xl bg-surface p-4 \ shadow-sm"> <div class_="flex w-full items-center justify-between"> <p class_="text-xs text-muted"> "Status: " <span class_="font-mono font-medium text-foreground"> (fun () -> Jsx.string (if S.get is_open then "open" else "closed")) </span> </p> </div> <div class_="flex gap-2"> <Button size=`sm variant=`secondary on_click=(fun _ -> S.set set_open true)> "Open Drawer" </Button> <Button size=`sm variant=`tertiary on_click=(fun _ -> S.update set_open not)> "Toggle" </Button> </div> </div> <Drawer.Backdrop is_open=(fun () -> S.get is_open) on_open_change=(S.set set_open)> <Drawer.Content placement=`right> <Drawer.Dialog> <Drawer.Close_trigger /> <Drawer.Header> <Drawer.Heading>"Controlled with a signal"</Drawer.Heading> </Drawer.Header> <Drawer.Body> <p> "This drawer is controlled by Solid's " <code>"signal"</code> ". Pass " <code>"is_open"</code> " and " <code>"on_open_change"</code> " props to manage the drawer state externally." </p> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Close"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </div> <div class_="flex flex-col gap-3"> <h3 class_="text-lg font-semibold text-foreground"> "With a signal's setter" </h3> <p class_="text-sm leading-relaxed text-pretty text-muted"> "Use the " <code class_="text-foreground">"signal"</code> "'s setter for a cleaner API with convenient calls like " <code>"S.set set_open true"</code> ", " <code>"S.set set_open false"</code> ", and " <code>"S.update set_open not"</code> "." </p> <div class_="flex flex-col items-start gap-3 rounded-2xl bg-surface p-4 \ shadow-sm"> <div class_="flex w-full items-center justify-between"> <p class_="text-xs text-muted"> "Status: " <span class_="font-mono font-medium text-foreground"> (fun () -> Jsx.string (if S.get state_open then "open" else "closed")) </span> </p> </div> <div class_="flex gap-2"> <Button size=`sm variant=`secondary on_click=(fun _ -> S.set set_state_open true)> "Open Drawer" </Button> <Button size=`sm variant=`tertiary on_click=(fun _ -> S.update set_state_open not)> "Toggle" </Button> </div> </div> <Drawer.Backdrop is_open=(fun () -> S.get state_open) on_open_change=(S.set set_state_open)> <Drawer.Content placement=`right> <Drawer.Dialog> <Drawer.Close_trigger /> <Drawer.Header> <Drawer.Heading> "Controlled with a signal's setter" </Drawer.Heading> </Drawer.Header> <Drawer.Body> <p> "The " <code>"signal"</code> {js|'s setter covers the common operations. No need to manually create callbacks—just use |js} <code>"S.set set_open true"</code> ", " <code>"S.set set_open false"</code> ", or " <code>"S.update set_open not"</code> "." </p> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Close"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </div> </div>With Form
Section titled “With Form”module Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawermodule Input = Solidtype_heroui.Inputmodule Label = Solidtype_heroui.Labelmodule Text_field = Solidtype_heroui.Text_field
let make () = <Drawer> <Button variant=`secondary>"Edit Profile"</Button> <Drawer.Backdrop> <Drawer.Content placement=`right> <Drawer.Dialog> <Drawer.Close_trigger /> <Drawer.Header> <Drawer.Heading>"Edit Profile"</Drawer.Heading> </Drawer.Header> <Drawer.Body> <form class_="flex flex-col gap-4"> <Text_field class_="w-full" name="name"> <Label>"Name"</Label> <Input type_="text" placeholder="Enter your name" variant=`secondary /> </Text_field> <Text_field class_="w-full" name="email"> <Label>"Email"</Label> <Input type_="email" placeholder="Enter your email" variant=`secondary /> </Text_field> <Text_field class_="w-full" name="bio"> <Label>"Bio"</Label> <Input placeholder="Tell us about yourself" variant=`secondary /> </Text_field> </form> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Cancel"</Button> <Button slot=`close>"Save Changes"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>Navigation Drawer
Section titled “Navigation Drawer”module Jsx = Solidtype.Jsxmodule For = Solidtype.Formodule Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
let nav_items = [| ((fun () -> <Gravity.House class_="size-5 text-muted" />), "Home"); ((fun () -> <Gravity.Magnifier class_="size-5 text-muted" />), "Search"); ((fun () -> <Gravity.Bell class_="size-5 text-muted" />), "Notifications"); ((fun () -> <Gravity.Envelope class_="size-5 text-muted" />), "Messages"); ((fun () -> <Gravity.Person class_="size-5 text-muted" />), "Profile"); ((fun () -> <Gravity.Gear class_="size-5 text-muted" />), "Settings"); |]
let make () = <Drawer> <Button variant=`secondary><Gravity.Bars /> "Menu"</Button> <Drawer.Backdrop> <Drawer.Content placement=`left> <Drawer.Dialog> <Drawer.Close_trigger /> <Drawer.Header> <Drawer.Heading>"Navigation"</Drawer.Heading> </Drawer.Header> <Drawer.Body> <nav class_="flex flex-col gap-1"> <For each=(fun () -> nav_items)> (fun (icon, label) _ -> <button class_="flex items-center gap-3 rounded-xl px-3 py-2.5 \ text-sm text-foreground transition-colors \ hover:bg-default" type_=`button> (fun () -> Jsx.element (icon ())) (fun () -> Jsx.string label) </button>) </For> </nav> </Drawer.Body> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>Backdrop Variants
Section titled “Backdrop Variants”module Jsx = Solidtype.Jsxmodule For = Solidtype.Formodule Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
let variants = [| (`opaque, "opaque"); (`blur, "blur"); (`transparent, "transparent") |]
let make () = <div class_="flex flex-wrap gap-4"> <For each=(fun () -> variants)> (fun (variant, name) _ -> <Drawer> <Button variant=`secondary> (fun () -> Jsx.string (String.capitalize_ascii name)) </Button> <Drawer.Backdrop variant=(fun () -> variant)> <Drawer.Content> <Drawer.Dialog> <Drawer.Handle /> <Drawer.Close_trigger /> <Drawer.Header> <Drawer.Heading> "Backdrop: " (fun () -> Jsx.string (String.capitalize_ascii name)) </Drawer.Heading> </Drawer.Header> <Drawer.Body> <p> "This drawer uses the " <code>(fun () -> Jsx.string name)</code> " backdrop variant." </p> </Drawer.Body> <Drawer.Footer> <Button class_="w-full" slot=`close>"Close"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>) </For> </div>Customization
Section titled “Customization”Tailwind CSS
Section titled “Tailwind CSS”class_ is added after HeroUI’s classes.
module Button = Solidtype_heroui.Buttonmodule Drawer = Solidtype_heroui.Drawer
let make () = <Drawer> <Button variant=`secondary>"Open filters"</Button> <Drawer.Backdrop variant=`blur> <Drawer.Content placement=`right> <Drawer.Dialog class_="border-l border-border/80 bg-surface"> <Drawer.Header> <Drawer.Heading class_="text-foreground">"Filters"</Drawer.Heading> </Drawer.Header> <Drawer.Body> <p class_="text-sm text-muted"> "Narrow results by status, owner, or date." </p> </Drawer.Body> <Drawer.Footer> <Button slot=`close variant=`secondary>"Cancel"</Button> <Button slot=`close>"Apply"</Button> </Drawer.Footer> </Drawer.Dialog> </Drawer.Content> </Drawer.Backdrop> </Drawer>Global CSS
Section titled “Global CSS”HeroUI’s classes are global, so a stylesheet restyles every one, as with HeroUI in React:
@layer components { .drawer__dialog { @apply rounded-t-3xl; }
.drawer__heading { @apply text-lg font-semibold; }}Styling Reference
Section titled “Styling Reference”CSS Classes
Section titled “CSS Classes”.drawer__trigger,.drawer__backdrop,.drawer__content,.drawer__dialog,.drawer__handle,.drawer__header,.drawer__heading,.drawer__body,.drawer__footer,.drawer__close-trigger.drawer__backdrop--opaque,--blur,--transparent.drawer__content--bottom,--top,--left,--right, and the same of.drawer__dialog
Interactive States
Section titled “Interactive States”- Entering:
[data-entering="true"]on the backdrop and the content - Exiting:
[data-exiting="true"]on the backdrop and the content - Placement:
[data-placement="bottom"], and the other edges, on the content and the dialog
How it is made
Section titled “How it is made”It shares Modal’s making, on Kobalte’s dialog: see Modal. Its drag is HeroUI’s: from anywhere on its dialog but a button, a field, a link or its body, it starts past 8 pixels, follows the pointer toward the drawer’s edge alone, and on release closes the drawer past 30% of its size or at a flick faster than half a pixel a millisecond, leaving the drawer where it was let go to slide on out, else springs back. The dialog’s own slide in and out is waited for, as its backdrop’s and content’s are.
API Reference
Section titled “API Reference”Each prop beside the name HeroUI’s React component gives it.
Drawer
Section titled “Drawer”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
isOpen |
is_open |
bool |
Whether it is open, controlled | |
defaultOpen |
default_open |
bool |
false |
Whether it starts open |
onOpenChange |
on_open_change |
bool -> unit |
Told as it opens or closes |
Drawer.Backdrop
Section titled “Drawer.Backdrop”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
variant |
variant |
`opaque | `blur | `transparent |
`opaque |
Its look |
isDismissable |
is_dismissable |
bool |
true |
Whether a press on it, or a drag, closes the drawer |
isKeyboardDismissDisabled |
is_keyboard_dismiss_disabled |
bool |
false |
Whether Escape does not |
isOpen |
is_open |
bool |
With no Drawer around it, whether it is open |
|
onOpenChange |
on_open_change |
bool -> unit |
With no Drawer around it, told as it opens or closes |
|
className |
class_ |
string |
Added after HeroUI’s classes |
Drawer.Content
Section titled “Drawer.Content”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
placement |
placement |
`top | `bottom | `left | `right |
`bottom |
The edge it slides from |
className |
class_ |
string |
Added after HeroUI’s classes |
Drawer.Trigger, Dialog, Header, Body, Footer, Heading, Handle, CloseTrigger
Section titled “Drawer.Trigger, Dialog, Header, Body, Footer, Heading, Handle, CloseTrigger”| React | solidtype | Type | Description |
|---|---|---|---|
className |
class_ |
string |
Added after HeroUI’s classes |
children |
children |
elements | What it holds, where it holds any |
Beside React
Section titled “Beside React”Original HeroUI documentation: Drawer.
The same HeroUI Drawer twice, under the same props: in solidtype on the left, in React on the right. Below them, every attribute of each trigger: a marked row is one they disagree on, and a dimmed row is a framework’s own, which no style reads.
| attribute | solidtype | React |
|---|
