Overview of the Phoenix and LiveView Headless Drawer Component
The Mishka Chelekom Headless Drawer
ships zero opinionated styling
- only the semantic markup, full WAI-ARIA
wiring, a focus trap, swipe-to-dismiss, and paired-presence state
(
data-open
/
data-closed
).
Every part - trigger, swipe-area, backdrop, viewport, popup, handle, title, description,
content
and footer
- is addressable through a
*_class
attribute, so you style it entirely with your own Tailwind
classes while
the accessibility and behavior keep working underneath.
Base UI Drawer example (Phoenix headless)
<.drawer id="baseui-drawer-hero" side="right" swipe_direction="right" trigger_class="flex h-8 items-center justify-center gap-2 border border-neutral-950 dark:border-white bg-white dark:bg-neutral-950 px-3 text-sm font-normal text-neutral-950 dark:text-white select-none hover:bg-neutral-100 dark:hover:bg-neutral-800" backdrop_class="[--backdrop-opacity:0.2] [--bleed:3rem] dark:[--backdrop-opacity:0.7] fixed inset-0 min-h-dvh bg-black opacity-[calc(var(--backdrop-opacity)*(1-var(--drawer-swipe-progress)))] transition-opacity duration-[450ms] ease-[cubic-bezier(0.32,0.72,0,1)] data-[swiping]:duration-0 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" viewport_class="[--viewport-padding:0px] fixed inset-0 flex items-stretch justify-end p-(--viewport-padding)" popup_class="[--bleed:3rem] h-full w-[calc(20rem+3rem)] max-w-[calc(100vw-3rem+3rem)] -mr-[3rem] border-l border-neutral-950 dark:border-white bg-white dark:bg-neutral-950 p-6 pr-[calc(1.5rem+3rem)] text-neutral-950 dark:text-white outline-none shadow-[0.25rem_0.25rem_0] shadow-black/12 overflow-y-auto overscroll-contain touch-auto [transform:translateX(var(--drawer-swipe-movement-x))] transition-transform duration-[450ms] ease-[cubic-bezier(0.32,0.72,0,1)] data-[ending-style]:[transform:translateX(calc(100%-var(--bleed)+var(--viewport-padding)+2px))] data-[starting-style]:[transform:translateX(calc(100%-var(--bleed)+var(--viewport-padding)+2px))]" content_class="mx-auto w-full max-w-[32rem]" title_class="mb-1 text-base font-bold" description_class="mb-6 text-sm text-neutral-600 dark:text-neutral-400" footer_class="flex justify-end gap-3" > <:trigger>Open drawer</:trigger> <:title>Drawer</:title> <:description> This is a drawer that slides in from the side. You can swipe to dismiss it. </:description> <:close> <button type="button" data-close class="flex h-8 items-center gap-2 border border-neutral-950 dark:border-white bg-white dark:bg-neutral-950 px-3 text-sm text-neutral-950 dark:text-white hover:bg-neutral-100 dark:hover:bg-neutral-800" > Close </button> </:close> </.drawer>
Parts & data attributes
Each part carries a stable
data-part
and a structural
chelekom-drawer__*
class you can target, plus paired-presence state for open/closed and swipe transitions.
| Part | data-part | State / ARIA | Purpose |
|---|---|---|---|
| Trigger | trigger | aria-haspopup, aria-expanded, aria-controls, data-popup-open | The button that opens the drawer. |
| Swipe area | swipe-area | aria-hidden, data-open, data-closed, data-swiping, data-swipe-direction | Opt-in edge strip; drag from it to open a closed drawer. |
| Backdrop | backdrop | aria-hidden, data-swiping, --drawer-swipe-progress | The overlay behind the popup; fades with swipe progress. |
| Popup | popup | role="dialog", aria-modal, data-side, data-open, data-closed, data-swiping, data-expanded, data-starting-style, data-ending-style | The drawer panel itself; focus-trapped when modal. |
| Handle | handle | aria-hidden | Optional drag-handle pill for bottom-sheet UX. |
| Title / Description | title / description | aria-labelledby / aria-describedby | Announced to screen readers; wired automatically. |
Accessibility
Follows the
WAI-ARIA Dialog (Modal) pattern
: focus is trapped inside the open drawer and restored to the trigger on close,
Escape
closes it, and an outside click dismisses it (both configurable). Swipe-to-dismiss is
pointer-only and has no keyboard equivalent, so a visible close control is always
required. The title and description slots are announced to screen readers.