# drawer

\# Drawer (Component)

A gesture-driven drawer component for Etch. Supports swipe-to-close, nested stacking, non-modal (passive) notifications, and four directions. Built with vanilla JS — no framework required.

Link: \[Drawer in GitBook]\(<https://app.gitbook.com/o/Stk0t1tQGHZDzvdmssfa/s/v6VZuQYokv6QNEpCQiEs/>) (add page "Drawer (Component)" under COMPONENTS section)

\---

\## Core Behavior

\### 1. Positioning and Direction

The drawer can open from any edge. Set \`data-direction\` on the \`.c-drawer\` element to control the entry point. Each direction uses its own CSS transforms and border-radius rules.

\\\* \*\*Bottom\*\* (default): Slides up, rounded top corners, max-height capped \\\* \*\*Top\*\*: Slides down, rounded bottom corners, accounts for admin bar \\\* \*\*Left\*\*: Slides in from the left, full-height with rounded corners \\\* \*\*Right\*\*: Slides in from the right, full-height with rounded corners

\### 2. Drag-to-Close

On open drawers, a pointer drag in the closing direction moves the drawer and dims the overlay proportionally. Releasing past the close threshold (25% of the drawer dimension) closes it; otherwise it snaps back.

\\\* \*\*Velocity detection\*\*: Fast swipes close immediately regardless of distance \\\* \*\*Over-drag dampening\*\*: Dragging past the open position applies logarithmic resistance \\\* \*\*Scroll awareness\*\*: Dragging is suppressed for 100ms after scrolling inside the drawer body, preventing scroll-to-close conflicts \\\* \*\*Handle-only mode\*\*: Restrict drag to the \`.c-drawer\_\_handle\` element with the \`handleOnly\` option \\\* \*\*No-drag mode\*\*: Disable swipe-to-close entirely with \`data-no-drag="true"\` \\\* \`cursor: grabbing\` is applied globally during drag via \`.c-drawer--dragging\`

\### 3. Nested Drawers

Drawers can stack on top of each other. When a drawer initializes inside another drawer's DOM, it is automatically relocated to \`document.body\` so its overlay covers the full viewport.

\\\* Opening a nested drawer scales down and translates the parent (16px displacement) \\\* Dragging a nested drawer closed restores the parent proportionally \\\* Closing a child restores the parent's full state via spring animation \\\* Passive drawers do not participate in the nesting stack \\\* Keyboard focus remains trapped within the active (topmost) drawer

\### 4. Passive Mode

Passive mode renders the drawer as a non-modal notification bar. It has no overlay, cannot trap focus, and does not lock body scroll.

\\\* Overlay is hidden (CSS managed), body scroll is not locked \\\* Click outside does not close — only drag or a close button dismisses it \\\* Trigger elements do not receive \`aria-expanded\` updates \\\* \`pointer-events: none\` on the wrapper lets users interact with content beneath \\\* Ideal for bottom-sheet notifications that don't demand immediate attention

\### 5. Auto-Open and Persistence

Drawers can open automatically on page load with configurable delay and storage-backed frequency control.

\\\* \*\*Frequency options\*\*: \\\* \`every-visit\`: Always show (no persistence) \\\* \`until-dismissed\`: Show until the user closes it, then never again \\\* \`once\`: Show one time only (recorded on open) \\\* \`after-delay\`: Re-show after a configurable duration since last dismissal \\\* Duration strings accept \`30s\`, \`2h\`, \`7d\`, or bare numbers (defaults to hours) \\\* All persistence uses \`localStorage\` with keys namespaced under \`etch-drawer-\`

\### 6. Etch Builder Integration

Inside the Etch visual builder iframe, the Drawer disables all gesture handling and event binding. The builder controls open/close by toggling \`data-editor-toggle\` on the wrapper element, which a \`MutationObserver\` watches.

\\\* No drag, no pointer events, no keyboard bindings \\\* CSS rules handle visibility from the data attribute \\\* The open/close class toggle still synchronizes \`data-state\` for preview rendering

\## Data Attributes Configuration

Configure the Drawer declaratively through data attributes on \`.c-drawer\_\_wrapper\` and \`.c-drawer\` elements.

\| Attribute | Description | Accepted Value | Required | |-----------|-------------|----------------|----------| | \`data-direction\` | Slide direction | Select: \`top\`, \`bottom\`, \`left\`, \`right\` | No | | \`data-passive\` | Enable passive (non-modal) mode | boolean | No | | \`data-no-drag\` | Disable swipe-to-close | \`true\` | No | | \`data-opened-by\` | CSS selector for trigger elements | CSS selector string | No | | \`data-closed-by\` | CSS selector for close elements | CSS selector string | No | | \`data-auto-open\` | Open automatically on page load | boolean | No | | \`data-auto-open-delay\` | Delay before auto-open | number (seconds) | No | | \`data-auto-open-reshow\` | Auto-open frequency | Select: \`every-visit\`, \`until-dismissed\`, \`once\`, \`after-delay\` | No | | \`data-auto-open-reshow-delay\` | Duration before re-showing | duration string (e.g. \`30s\`, \`2h\`, \`7d\`) | No | | \`data-close-on-anchor\` | Close on same-page anchor link click | boolean | No |

\## JavaScript Options

Pass options to the \`Drawer\` constructor:

\| Option | Description | Type | Default | |--------|-------------|------|---------| | \`dismissible\` | Allow closing via overlay click or ESC | boolean | \`true\` | | \`handleOnly\` | Only allow drag via the handle element | boolean | \`false\` | | \`nested\` | Mark as a nested drawer (auto-detected) | boolean | \`false\` | | \`scrollLockTimeout\` | Milliseconds before drag is allowed after scrolling | number | \`100\` | | \`closeThreshold\` | Fraction of drawer dimension required to close via drag | number | \`0.25\` | | \`onOpen\` | Callback when the drawer opens | function | \`null\` | | \`onClose\` | Callback when the drawer closes | function | \`null\` | | \`onDrag\` | Callback during drag (receives event and percentage dragged) | function | \`null\` | | \`onRelease\` | Callback after drag release (receives event and whether it snapped back) | function | \`null\` |

\## CSS Custom Properties

Customize the visual appearance through CSS custom properties on \`.c-drawer\` or \`:root\`:

\| Variable | Description | |----------|-------------| | \`--drawer-bg\` | Drawer background color (default: \`#fff\`) | | \`--drawer-overlay-color\` | Overlay background color (default: \`rgba(0,0,0,0.4)\`) | | \`--drawer-duration\` | Transition duration (default: \`0.5s\`) | | \`--drawer-easing\` | Transition easing curve (default: \`cubic-bezier(0.32,0.72,0,1)\`) | | \`--drawer-radius\` | Drawer border radius (default: \`var(--radius)\`) | | \`--drawer-inset\` | Margin from viewport edges (default: \`8px\`) | | \`--drawer-max-height\` | Maximum height for vertical drawers (default: \`92dvh\`) | | \`--drawer-max-width\` | Maximum width for horizontal drawers (default: \`24rem\`) | | \`--drawer-body-padding\` | Padding inside \`.c-drawer\_\_body\` (default: \`var(--space-m)\`) | | \`--drawer-handle-width\` | Drag handle width (default: \`48px\`) | | \`--drawer-handle-height\` | Drag handle height (default: \`4px\`) | | \`--drawer-handle-color\` | Drag handle color (default: \`#b4b4b8\`) | | \`--drawer-handle-padding\` | Drag handle wrapper padding (default: \`1.25rem 0 1rem\`) |

\## Structural Classes

\| Class | Description | |-------|-------------| | \`.c-drawer\_\_wrapper\` | Fixed-position portal container. All drawers must live inside this element. | | \`.c-drawer\` | The drawer panel. Positioned, animated, and sized by direction. | | \`.c-drawer\_\_overlay\` | Semi-transparent backdrop behind the drawer. | | \`.c-drawer\_\_handle-wrapper\` | Wrapper around the drag handle. Hidden for horizontal drawers. | | \`.c-drawer\_\_handle\` | Visual drag handle (horizontal bar). | | \`.c-drawer\_\_body\` | Scrollable content area. Flex-grows to fill available space. | | \`.c-drawer\_\_header\` | Optional header row (flex, shrink-resistant). | | \`.c-drawer\_\_footer\` | Optional footer row (flex, shrink-resistant). | | \`.c-drawer\_\_close\` | Close button (flex, icon-ready) | | \`.c-drawer\_\_back\` | Back button for nested navigation with gap. | | \`.c-drawer\_\_section-title\` | Section title label (shrink-resistant). |

\## State Classes and Attributes

\| State | Applied To | Description | |-------|------------|-------------| | \`.c-drawer\_\_wrapper--visible\` | \`.c-drawer\_\_wrapper\` | Makes the wrapper interactive and visible | | \`.c-drawer--dragging\` | \`.c-drawer\` | Forces \`grabbing\` cursor globally during drag | | \`\[data-state="open"]\` | \`.c-drawer\`, \`.c-drawer\_\_overlay\` | Shows the drawer/overlay in its open position | | \`\[data-state="closed"]\` | \`.c-drawer\`, \`.c-drawer\_\_overlay\` | Hides the drawer/overlay (CSS default) | | \`\[aria-expanded="true"]\` | Trigger elements | Indicates the drawer is open |

\## JavaScript API

\`\`\`javascript // Create a new drawer instance const drawer = new Drawer('.my-drawer-wrapper', { dismissible: true, handleOnly: false, onOpen: () => console.log('opened'), onClose: () => console.log('closed'), });

// Open the drawer drawer.open();

// Close the drawer drawer.close();

// Remove all event listeners and clean up drawer.destroy(); \`\`\`

\### Static Methods

\`Drawer.init()\` — Auto-initialize all \`.c-drawer\_\_wrapper\` elements currently in the DOM. Called automatically on DOMContentLoaded.

\## Accessibility

\\\* Drawers receive \`role="dialog"\` (modal) or \`role="region"\` (passive) with \`aria-modal="true"\` \\\* The first heading inside the drawer is linked via \`aria-labelledby\` \\\* A live region announces "Dialog opened" / "Dialog closed" (or "Notification" for passive drawers) to screen readers \\\* Focus is moved to the first focusable element inside the drawer on open (passive drawers skip focus management) \\\* Tab and Shift+Tab are trapped within the drawer to prevent focus from escaping behind the overlay \\\* Focus returns to the trigger element when the drawer is dismissed \\\* All drawer content is hidden from screen readers when \`data-state\` is \`closed\` \\\* Overlays are marked \`aria-hidden="true"\` — they are decorative \\\* Drag handles receive a descriptive \`aria-label\`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.nickarce.com/etch-drawer/drawer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
