Drawer

Slide-over panels for detail views, configuration forms, and HTMX-driven workflows that need more room than a modal.

Usage Example
@drawer.Drawer(drawer.Config{
    ID:    "projectDetails",
    Title: "Project details",
    Side:  drawer.SideRight,
    Width: drawer.WidthLG,
}) {
    <div id="projectDetails-body">...</div>
}

Left Side

Set Side: drawer.SideLeft when the panel should enter from the navigation edge.

Left Side
@drawer.Drawer(drawer.Config{
    ID:    "filtersDrawer",
    Title: "Filters",
    Side:  drawer.SideLeft,
    Width: drawer.WidthSM,
}) {
    <div id="filtersDrawer-body">...</div>
}

Persistent Drawer

Set Persistent: true when the drawer hosts an unfinished form or confirmation flow. The close button still works, but backdrop clicks and Escape do not dismiss it.

Persistent Drawer
@drawer.Drawer(drawer.Config{
    ID:         "editProfile",
    Title:      "Edit profile",
    Persistent: true,
}) {
    <form id="editProfile-body">...</form>
}

HTMX Body Target

Give BodyID a stable value so HTMX responses can replace only the drawer body, then open the drawer with a drawer:open event or HX-Trigger header.

HTMX Body Target
@drawer.Drawer(drawer.Config{
    ID:     "orderDrawer",
    Title:  "Order details",
    BodyID: "orderDrawerBody",
}) {
    <div id="orderDrawerBody">Select an order.</div>
}

<button
    hx-get="/orders/42/drawer"
    hx-target="#orderDrawerBody"
    x-on:click="$dispatch('drawer:open', { id: 'orderDrawer' })">
    View order
</button>

API Reference

PropTypeDefaultDescription
IDstring""Unique drawer id used for Alpine state, events, body id, and aria-labelledby wiring.
Titlestring""Accessible drawer heading.
SideSideSideRightPanel edge: "right" or "left".
WidthWidthWidthMDPreset max width: sm, md, lg, xl, or full.
BodyIDstring"{ID}-body"Inner scroll container id, useful as an HTMX target.
PersistentboolfalseDisable Esc and backdrop dismissal.
PanelClassstring""Extra classes on the sliding panel.