Drawer
import { Drawer } from '@absoluteui/components/drawer';import '@absoluteui/components/drawer/style.css';Using Angular? See Drawer for Angular.
What it does
Section titled “What it does”A panel that slides in from a screen edge, built on native <dialog>.
- Modal (default):
showModal()gives top-layer rendering, an inert background, a backdrop, and native focus containment. AbsoluteUI adds scroll lock and focus restoration. - Non-modal (
modal: false):show()keeps the page interactive. Focus moves into the drawer on open, and Escape closes it when it is the topmost overlay.
Both modes share Dialog’s close reasons and cancelable close events. Use non-modal drawers only for supplementary panels, such as filters or record details next to live content.
When to use
Section titled “When to use”- Secondary tasks that keep page context visible, such as editing a record from a list.
- Navigation or settings panels on small screens (modal).
- Supplementary panels such as filters or details next to live content (non-modal).
When not to use
Section titled “When not to use”- Short confirmations or destructive prompts: use Dialog (role=alertdialog).
- Primary page content or long multi-step flows: use a page.
- Content anchored to a trigger: use a popover or menu.
- Non-modal drawers for tasks that must be completed before continuing: use a modal drawer.
Examples
Section titled “Examples”Modal drawer with actions
Section titled “Modal drawer with actions”import { Drawer, type DrawerCloseDetail } from '@absoluteui/components/drawer';
const editor = Drawer.create({ title: 'Edit customer', description: 'Changes are saved when you select Save.', size: 'lg', actions: [ { label: 'Cancel', value: 'cancel' }, { label: 'Save', value: 'save', variant: 'primary' }, ],});editor.element.addEventListener('aui-close', (event) => { const { returnValue } = (event as CustomEvent<DrawerCloseDetail>).detail; if (returnValue === 'save') { // persist changes }});editor.open();Non-modal filter panel
Section titled “Non-modal filter panel”import { Drawer } from '@absoluteui/components/drawer';
const filters = Drawer.create({ title: 'Filters', modal: false, placement: 'start', size: 'sm' });const list = document.createElement('fieldset');filters.body.append(list);document.querySelector('#toggle-filters')?.addEventListener('click', () => { if (filters.isOpen) filters.close(); else filters.open();});Prevent closing with unsaved changes
Section titled “Prevent closing with unsaved changes”import { Drawer } from '@absoluteui/components/drawer';
let dirty = true;const panel = Drawer.create({ title: 'Edit profile', placement: 'bottom' });panel.element.addEventListener('aui-cancel', (event) => { if (dirty) event.preventDefault();});panel.open();dirty = false;import { Drawer } from '@absoluteui/components/drawer';import '@absoluteui/components/button/style.css';import '@absoluteui/components/drawer/style.css';
const filters = Drawer.create({ title: 'Filters', modal: false, placement: 'start', size: 'sm' });document.querySelector('#toggle-filters')?.addEventListener('click', () => { if (filters.isOpen) filters.close(); else filters.open();});More usage
Section titled “More usage”import { Drawer } from '@absoluteui/components/drawer';
const editor = Drawer.create({ title: 'Edit customer', size: 'lg', actions: [ { label: 'Cancel', value: 'cancel' }, { label: 'Save', value: 'save', variant: 'primary' }, ],});editor.open();Anatomy
Section titled “Anatomy”<dialog class="aui-drawer aui-drawer--{modal|non-modal} aui-drawer--{placement} aui-drawer--{size}" data-aui-side="left|right|top|bottom" dir aria-labelledby aria-describedby> <header class="aui-drawer__header"> <h2 class="aui-drawer__title">title</h2> <button class="aui-button aui-drawer__close" aria-label="Close">icon</button> </header> <p class="aui-drawer__description">description</p> <div class="aui-drawer__body">content</div> <footer class="aui-drawer__footer">action buttons</footer></dialog>API reference
Section titled “API reference”Factory
Section titled “Factory”Drawer.create(options: DrawerOptions, doc?: Document): DrawerGlobal configuration key: drawer (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
title |
string |
Accessible name, rendered as the heading. Required. | |
description |
string |
Referenced by aria-describedby. | |
content |
Node | string |
Body content; strings render as text. | |
actions |
readonly DrawerAction[] |
Footer buttons; clicking closes with the action value. | |
modal |
boolean |
true |
true: showModal() with inert background, backdrop and scroll lock. false: show(); background stays interactive (supplementary panels only). Set at creation only. |
icons |
Partial<{ close: IconRef }> |
Icon slot overrides. | |
placement |
'start' | 'end' | 'top' | 'bottom' |
"end" |
Screen edge; start/end follow the writing direction. Globally configurable. |
size |
'sm' | 'md' | 'lg' |
"md" |
Inline size for side drawers; maximum block size (capped at 85dvh) for top/bottom. Globally configurable. |
closeOnEscape |
boolean |
true |
Escape requests close (cancelable) when the drawer is the topmost overlay. Globally configurable. |
closeOnBackdrop |
boolean |
true |
Backdrop click requests close (cancelable). Modal only. Globally configurable. |
showCloseButton |
boolean |
true |
Render the header close button. Globally configurable. |
direction |
'ltr' | 'rtl' |
"ltr" |
Maps start/end placement to a physical edge; applied as the element’s dir. Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
element |
HTMLDialogElement |
The native dialog element. |
body |
HTMLDivElement |
Consumer content area. |
isOpen |
boolean |
Whether the drawer is open. |
modal |
boolean |
Whether the drawer was created modal. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
open(): void |
Open (modal: showModal() and scroll lock; non-modal: show()); moves focus inside and captures focus for restoration. |
close(returnValue?: string): void |
Close programmatically (not cancelable). |
update(options: DrawerUpdateOptions): void |
Merge and apply options (all except modal). |
destroy(): void |
Close if open, remove element, listeners, overlay registration, subscriptions. |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-open |
null |
No | No | After the drawer opened. |
aui-cancel |
{ returnValue: string; reason: 'escape' | 'backdrop' | 'close-button' | 'action' } |
No | Yes | Before a user-initiated close; preventDefault keeps it open. |
aui-close |
{ returnValue: string; reason: DrawerCloseReason } |
No | No | After the drawer closed. |
Content areas
Section titled “Content areas”| Area | Description |
|---|---|
body |
Drawer.body (or content option). |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-drawer |
Native <dialog> element; data-aui-side holds the physical edge (left | right | top | bottom). |
aui-drawer--modal | --non-modal |
Modality modifiers. |
aui-drawer--start | --end | --top | --bottom |
Logical placement modifiers. |
aui-drawer--sm | --md | --lg |
Size modifiers. |
aui-drawer__header |
Title row with close button. |
aui-drawer__title |
Heading (h2). |
aui-drawer__close |
Close button. |
aui-drawer__description |
Description paragraph. |
aui-drawer__body |
Scrollable content area. |
aui-drawer__footer |
Action buttons. |
Icon slots
Section titled “Icon slots”| Slot | Default icon |
|---|---|
close |
close |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-border-width | --aui-color-backdrop | --aui-color-border | --aui-color-focus-ring | --aui-color-surface | --aui-color-text | --aui-color-text-muted | --aui-drawer-width-lg | --aui-drawer-width-md | --aui-drawer-width-sm | --aui-duration-fast | --aui-duration-normal | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-lg | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-semibold | --aui-line-height-normal | --aui-line-height-tight | --aui-shadow-lg | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-space-5
TypeScript definitions
Section titled “TypeScript definitions”type DrawerPlacement = 'start' | 'end' | 'top' | 'bottom';type DrawerSide = 'left' | 'right' | 'top' | 'bottom';type DrawerCloseReason = 'escape' | 'backdrop' | 'close-button' | 'action' | 'programmatic';interface DrawerAction { label: string; value: string; variant?: ButtonVariant; autofocus?: boolean }interface DrawerCloseDetail { returnValue: string; reason: DrawerCloseReason }interface DrawerOptions { title: string; description?: string; content?: Node | string; actions?: readonly DrawerAction[]; modal?: boolean; direction?: 'ltr' | 'rtl'; icons?: Partial<{ close: IconRef }>; placement?: DrawerPlacement; size?: 'sm' | 'md' | 'lg'; closeOnEscape?: boolean; closeOnBackdrop?: boolean; showCloseButton?: boolean;}type DrawerUpdateOptions = Partial<Omit<DrawerOptions, 'modal'>>;Accessibility
Section titled “Accessibility”Pattern: WAI-ARIA APG Dialog (Modal) pattern implemented with native <dialog>.showModal(); non-modal drawers use <dialog>.show() as a non-modal dialog.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Tab / Shift+Tab | Modal drawer open | Moves focus among drawer controls; the background is inert (native modal). |
| Tab / Shift+Tab | Non-modal drawer open | Follows document order; focus can leave the drawer and reach the page. |
| Escape | Drawer is the topmost overlay, closeOnEscape=true | Emits cancelable aui-cancel, then closes and restores focus. |
| Escape | closeOnEscape=false | No effect; the native close request is prevented, including repeated presses. |
| Escape | Nested overlay (e.g. combobox listbox) open | Closes only the topmost overlay. |
| Enter / Space | Action or close button focused | Activates the button. |
| Element | Role |
|---|---|
| <dialog> | dialog (native) |
| title | heading level 2 |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-labelledby |
<dialog> | id of the title heading |
aria-describedby |
<dialog> | id of the description (when provided) |
aria-label |
close button | Localized ‘Close’ |
aria-modal |
<dialog> | Implicit true for modal drawers (native showModal()); absent for non-modal drawers |
On open, focus moves to the autofocus action or first focusable element. On close, a modal drawer returns focus to the element focused before opening; a non-modal drawer does so only when focus was inside the drawer, so users working in the page keep their place.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”| Criterion | Level | Note |
|---|---|---|
| 1.3.1 Info and Relationships | A | |
| 2.1.1 Keyboard | A | |
| 2.1.2 No Keyboard Trap | A | |
| 2.4.3 Focus Order | A | |
| 2.4.7 Focus Visible | AA | |
| 4.1.2 Name, Role, Value | A |
Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- Background inertness and top-layer rendering of modal drawers are provided by the browser.
- Slide motion is removed under prefers-reduced-motion and data-aui-animation=‘none’.
- Use non-modal drawers only for supplementary panels (filters, details).
- Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.
Behavior
Section titled “Behavior”States
Section titled “States”closed, opening (slide-in), open, closing (an aui-cancel listener is pending), and closing (slide-out).
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| closed | open() |
open (focus moves inside; modal: scroll locked, background inert) |
| open | Escape / backdrop (modal) / close button / action | aui-cancel -> closed, unless prevented |
| open | close() |
closed (no aui-cancel) |
| open | consumer <form method="dialog"> submit |
closed (reason action) |
Accessibility notes
Section titled “Accessibility notes”- Initial focus goes to the action with
autofocus: true, otherwise the first focusable element (native dialog focusing steps). - A modal drawer returns focus to the element that was focused before
open(). - A non-modal drawer returns focus only when focus was inside the drawer at close, so users working in the page keep their place.
- Non-modal drawers do not trap focus. Keep them supplementary, and give users a visible way to reopen and close them.
- Slide motion is removed under
prefers-reduced-motionanddata-aui-animation='none'. - Manual screen-reader verification is pending.
Responsive behavior
Section titled “Responsive behavior”Side drawers use min(drawer-width token, 100vw) and the full viewport height. Top and bottom drawers use the full inline size and max-block-size: min(drawer-width token, 85dvh). The body scrolls. Below 480px, side drawers take 100vw and footer actions stack full-width.
Right-to-left
Section titled “Right-to-left”start and end resolve against the configured direction: start is the left edge in LTR and the right edge in RTL. The resolved edge is exposed as data-aui-side, the slide direction follows it, and the element receives dir. Inner layout uses logical properties; the close button sits at the inline end.
Theming
Section titled “Theming”Token-driven surface, border, shadow, and backdrop. Only the inner edge has a border. Forced-colors mode uses a CanvasText border.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ components: { drawer: { placement: 'start', size: 'lg', closeOnBackdrop: false } } });Changes apply to existing instances immediately.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All configurable options are dynamic; modal is init-only.
icons.close (default close), resolved through the global icon provider/registry.
Security
Section titled “Security”Title, description, and string content use textContent. No HTML parsing, no inline styles. CSP-safe.
Data handling
Section titled “Data handling”No persistence, network access, or telemetry.
Performance
Section titled “Performance”Constant work per open/close: one showModal()/show() call, one overlay-stack entry, one scroll-lock reference (modal), and one focus restore point. Budget: open() under 16 ms in headless Chromium.
Supported data scale
Section titled “Supported data scale”Not data-driven. Body content size is limited only by the consumer; the body scrolls.
Edge cases
Section titled “Edge cases”closeOnEscape: falsealso blocks repeated Escape presses. Chrome would otherwise let the second Escape through without user activation.- A non-modal drawer yields Escape to a modal dialog opened elsewhere, because it is inert behind that dialog.
- Escape closes a non-modal drawer when it is the topmost overlay, even if focus is in the page.
- Destroying an open drawer releases the scroll lock and overlay registration, and restores focus.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
DRAWER_TITLE |
configuration | Missing or blank title. |
DRAWER_OPTIONS |
configuration | Invalid modal or actions. |
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION |
configuration | Invalid or unknown configuration, such as an unknown placement. |
A failed construction or update leaves no rendering, listeners, overlay registration, or subscriptions behind, and a failed update() keeps the previous options.
Known limitations
Section titled “Known limitations”- Non-modal drawers are not in the top layer. They use
z-index: 1000, so page content with a higher stacking context can cover them. - The element’s
dirfollows the configured direction (default'ltr'), not the document’sdirattribute. - Slide animations need
@starting-styleandtransition-behavior: allow-discrete. Older browsers open and close without animation. - Manual screen-reader verification is pending.
Unsupported scenarios
Section titled “Unsupported scenarios”- Switching
modalafter creation. - Swipe-to-dismiss gestures.
- HTML strings as content: pass a
Nodeinstead.