Drawer
Slide-out panel with backdrop overlay, keyboard dismissal, and left/right positioning for off-canvas navigation, filters, and detail views.
<arc-drawer> Overview
>
Drawer is a slide-out panel that emerges from the left or right edge of the viewport, overlaying page content behind a semi-transparent backdrop. It is the standard pattern for housing navigation menus on mobile, exposing filter panels in data-heavy applications, or showing contextual detail views without navigating away from the current page.
When the Drawer opens it locks body scroll, preventing the user from accidentally interacting with background content. A smooth CSS transform animation slides the panel into view while the backdrop fades in, creating a clear spatial relationship between the panel and the page behind it. Closing is handled automatically via the built-in close button, pressing the Escape key, or clicking the backdrop — all of which fire an `arc-close` event so your application state stays in sync.
Choose Drawer over Modal when the supplementary content is navigation-oriented, when users need to glance back at the main page while interacting with the panel, or when the content is tall and benefits from full-height scrolling. For blocking decisions that require explicit user action, use Modal instead.Guidelines
When to use
- Use for mobile navigation menus that slide in from the left edge
- Use for filter or settings panels in data-heavy dashboards
- Provide a clear, short heading that tells the user what the panel contains
- Keep the drawer width reasonable — the default 300px works for most navigation use cases
- Use `position="right"` for detail views and contextual information panels
- Always listen for the `arc-close` event to keep your open state in sync
When not to use
- Do not use a Drawer for critical confirmations — use Modal instead
- Do not nest a Drawer inside another Drawer
- Do not put complex multi-step forms in a Drawer — consider a full page or Modal
- Do not auto-open a Drawer on page load without a clear user-initiated trigger
- Do not remove the backdrop — users expect click-outside-to-close behavior
- Do not place unrelated content in the drawer heading area — keep it for the title and close button
Features
- Slides in from the left or right edge via CSS transforms with configurable `position` prop
- Semi-transparent backdrop overlay dims the page and captures click-to-close
- Escape key dismissal with automatic keyboard listener management
- Body scroll lock while open prevents background content interaction
- Built-in header bar with heading text and close button
- Scrollable body region for long content like navigation trees or filter forms
- Fires `arc-close` custom event on any dismiss action for state synchronization
- Max-width capped at 85vw so the drawer never fully obscures the page
- CSS custom property theming via `--bg-surface`, `--border-subtle`, and `--text-primary`
- Accessible with `role="dialog"`, `aria-modal="true"`, and `aria-label` from heading
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<arc-button id="open-nav-drawer">Menu</arc-button>
<arc-drawer id="nav-drawer" heading="Navigation" position="left">
<nav style="display:flex; flex-direction:column; gap:4px; padding:8px;">
<a href="/dashboard" style="padding:10px 12px; border-radius:6px; color:var(--text-primary); text-decoration:none;">Dashboard</a>
<a href="/projects" style="padding:10px 12px; border-radius:6px; color:var(--text-secondary); text-decoration:none;">Projects</a>
<a href="/activity" style="padding:10px 12px; border-radius:6px; color:var(--text-secondary); text-decoration:none;">Activity</a>
<a href="/settings" style="padding:10px 12px; border-radius:6px; color:var(--text-secondary); text-decoration:none;">Settings</a>
</nav>
</arc-drawer>
<script>
const openBtn = document.querySelector('#open-nav-drawer');
const drawer = document.querySelector('#nav-drawer');
openBtn.addEventListener('click', () => { drawer.open = true; });
drawer.addEventListener('arc-close', () => { drawer.open = false; });
</script> import { useState } from 'react';
import { Button, Drawer } from '@arclux/arc-ui-react';
function MobileNav() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Menu</Button>
<Drawer open={open} heading="Navigation" position="left" onArcClose={() => setOpen(false)}>
<nav style={{ display: 'flex', flexDirection: 'column', gap: 4, padding: 8 }}>
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/activity">Activity</a>
<a href="/settings">Settings</a>
</nav>
</Drawer>
</>
);
} <script setup>
import { ref } from 'vue';
import { Button, Drawer } from '@arclux/arc-ui-vue';
const open = ref(false);
</script>
<template>
<Button @click="open = true">Menu</Button>
<Drawer :open="open" heading="Navigation" position="left" @arc-close="open = false">
<nav style="display:flex; flex-direction:column; gap:4px; padding:8px;">
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/activity">Activity</a>
<a href="/settings">Settings</a>
</nav>
</Drawer>
</template> <script>
import { Button, Drawer } from '@arclux/arc-ui-svelte';
let open = $state(false);
</script>
<Button onclick={() => open = true}>Menu</Button>
<Drawer {open} heading="Navigation" position="left" on:arc-close={() => open = false}>
<nav style="display:flex; flex-direction:column; gap:4px; padding:8px;">
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/activity">Activity</a>
<a href="/settings">Settings</a>
</nav>
</Drawer> import { Component } from '@angular/core';
import { Button, Drawer } from '@arclux/arc-ui-angular';
@Component({
imports: [Button, Drawer],
template: `
<arc-button (click)="open = true">Menu</arc-button>
<arc-drawer [open]="open" heading="Navigation" position="left" (arcClose)="open = false">
<nav style="display:flex; flex-direction:column; gap:4px; padding:8px;">
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/activity">Activity</a>
<a href="/settings">Settings</a>
</nav>
</arc-drawer>
`,
})
export class MobileNavComponent {
open = false;
} import { createSignal } from 'solid-js';
import { Button, Drawer } from '@arclux/arc-ui-solid';
function MobileNav() {
const [open, setOpen] = createSignal(false);
return (
<>
<Button onClick={() => setOpen(true)}>Menu</Button>
<Drawer open={open()} heading="Navigation" position="left" onArcClose={() => setOpen(false)}>
<nav style={{ display: 'flex', 'flex-direction': 'column', gap: '4px', padding: '8px' }}>
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/activity">Activity</a>
<a href="/settings">Settings</a>
</nav>
</Drawer>
</>
);
} import { useState } from 'preact/hooks';
import { Button, Drawer } from '@arclux/arc-ui-preact';
function MobileNav() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Menu</Button>
<Drawer open={open} heading="Navigation" position="left" onArcClose={() => setOpen(false)}>
<nav style={{ display: 'flex', flexDirection: 'column', gap: 4, padding: 8 }}>
<a href="/dashboard">Dashboard</a>
<a href="/projects">Projects</a>
<a href="/activity">Activity</a>
<a href="/settings">Settings</a>
</nav>
</Drawer>
</>
);
} API
-
openbooleanfalse - Controls the visible state of the drawer. Set to `true` to slide the panel into view and activate the backdrop; set to `false` to run the exit animation, remove the backdrop, and restore body scroll.
-
position'left' | 'right''left' - Which edge of the viewport the drawer slides in from. Use `left` for primary navigation menus and `right` for contextual detail panels, filter sidebars, or settings trays.
-
headingstring'' - Text displayed in the drawer header bar. Also used as the `aria-label` for the dialog panel, ensuring screen readers announce the panel purpose when it opens.
Events
-
arc-close - Fired when the drawer closes via backdrop click or escape key
See Also
- Modal General-purpose focus-trapping overlay with backdrop blur, slide-up animation, and ESC-to-close behavior for forms, settings, and rich content that needs full user attention.
- Sheet A sliding overlay panel that emerges from the bottom or right edge of the viewport, with a blurred backdrop, header, scrollable body, and footer slot.
- Sidebar Collapsible navigation sidebar with grouped sections, heading labels, and active link highlighting. Ideal for documentation sites, admin panels, and any layout that needs persistent vertical navigation.