## drawer (`poetry_drawer`)

A gesture-driven panel that slides in from a screen edge.

Class: Poetry::Ui::Drawer::Component - BEM block `poetry-ui-drawer`.
Slot REQUIRED: with_title (the accessible name) - a call without it raises.
- `direction:` (symbol) - one of down|up|left|right, default "down", required - The dismiss direction - :down is the mobile bottom sheet; the edge chrome and swipe axis derive from it.
- `content_class:` (string) - Extra classes merged onto the <dialog> panel (e.g. "max-h-[50vh]" caps a top/bottom sheet).
- `dismissible:` (boolean) - default true - Backdrop clicks close the dialog; false keeps confirmations from being dismissed accidentally (Esc still closes).
- `modal:` (boolean) - default true - Non-modal (false) opens with show() - no top layer, no scrim, no focus trap, no scroll lock; the page behind stays interactive. Esc (while focus is inside), the swipe, and any wired close button still exit; there is no backdrop to click, so pointer dismissal is off by nature.
- `show_swipe_handle:` (boolean) - default false - Renders the grab pill so the swipe gesture is discoverable.
- `snap_points:` () - Preset resting heights for a bottom sheet, ascending: fractions of the full height (0..1] or CSS px/rem lengths (["31rem", 1]). The popup runs full-height and opens at the first point; drags move between points, below the first dismisses. direction: :down only.
Slots: trigger (The trigger is a poetry Button wired to open the dialog - agents pass Button props: with_trigger(variant: :outline) { "Open" }.; takes poetry_button props, not a block; with_trigger yields NOTHING to the block - no |param|, write content directly), title (The heading - the dialog's accessible name; required.), description (Muted copy under the title, wired to aria-describedby.), footer (The action row at the bottom of the panel.).
- PART `drawer` - Root wrapper around the trigger and the <dialog> element
- PART `drawer-content` - The <dialog> popup - the edge chrome, presence animation, and the swipe contract all ride here (::backdrop inherits the swipe vars, so the overlay fade rides along) | states: data-open (popup is open (the controller flips the pair at runtime)); data-closed (popup is closed or animating out (the server-rendered state)); data-swipe-direction=down|up|left|right (always - the dismiss direction); data-swiping (a pointer drag is tracking (transitions go duration-0 - the drawer follows the finger)); data-snap-points (snap_points: present - the popup runs full-height and --drawer-snap-point-offset rests it at the current point); data-starting-style (the enter transition's first frame (the presence helper's two-frame trick)); data-ending-style (held through the exit transition before the native close()) | vars: --drawer-swipe-movement-x (px dragged toward a left/right dismissal (controller-written during swipes)); --drawer-swipe-movement-y (px dragged toward an up/down dismissal (controller-written during swipes)); --drawer-swipe-progress (0..1 fraction of the dismiss travel (the backdrop fade rides it)); --drawer-swipe-strength (remaining-travel factor set on release - scales the exit duration so a mostly-swiped drawer closes fast)
- PART `drawer-swipe-handle` - The grab pill (show_swipe_handle: true, aria-hidden) - a drag may always start on it
- PART `drawer-header` - Title block at the top of the popup
- PART `drawer-title` - The heading - the drawer's accessible name (required slot)
- PART `drawer-description` - Muted copy under the title, wired to aria-describedby
- PART `drawer-body` - The scrollable content region between header and footer
- PART `drawer-footer` - Action row pinned to the bottom of the popup
- WIRING root: `poetry--core--drawer` registers; values dismissible, direction, modal, snap_points (if)
- WIRING content: `poetry--core--drawer` actions close on cancel, backdropClose on click, escapeClose on keydown (unless modal), swipeStart on pointerdown, swipeMove on pointermove, swipeEnd on pointerup, swipeCancel on pointercancel; targets dialog
- WIRING trigger: `poetry--core--drawer` actions open
- WIRING close: 
- tool open (mutating) - Open the dialog. [opt in with webmcp: "name" on the call; dispatches poetry--core--drawer#open]
- tool close (mutating) - Close the dialog. [opt in with webmcp: "name" on the call; dispatches poetry--core--drawer#close]
- RULE: Open drawers with with_trigger(...) - never a hand-wired button.
- RULE: with_title is REQUIRED (the accessible name) - the inherited Dialog rule.
- RULE: direction: is the DISMISS direction: :down is the mobile bottom sheet (the default); left/right make an edge panel - prefer Sheet on desktop.
- RULE: show_swipe_handle: true renders the grab pill - use it on bottom sheets so the gesture is discoverable.
- RULE: Esc and the backdrop still dismiss (the platform trap) - the swipe is an addition, never the only way out.
- RULE: modal: false keeps the page interactive (no scrim, no focus trap) - pair a wired footer close; Esc while focus is inside still exits.
- RULE: snap_points: ["31rem", 1] snaps a bottom sheet between preset heights (ascending fractions or px/rem lengths; opens at the first) - direction: :down only.
