Drawer
FreeA slide-in panel from the left or right edge of the viewport, used for secondary content, detail views, filters, navigation, and settings that supplement the main page without fully replacing it. Includes a backdrop, close button, focus trap, and scroll lock identical to Modal. Animates in with a slide transition for spatial context. Available in right, left, and bottom edge variants.
6 variants
Variants
| Variant | Description | When to use |
|---|---|---|
| Right | Panel slides in from the right edge, the most common placement. | Use for detail panels, editing side-sheets, and supplementary content. |
| Left | Panel slides in from the left edge. | Use for navigation drawers and filter panels on desktop, or mobile nav menus. |
| Bottom | Panel slides up from the bottom edge. | Use on mobile for filters, actions, and supplementary content. |
Usage Guidelines
Trap focus within the drawer while it is open.
Open a drawer from within another drawer — stack complexity confuses users.
Return focus to the trigger element when the drawer closes.
Use a drawer for critical confirmations — use a Modal or Confirmation Dialog instead.
Provide a clear close button and allow Escape to dismiss.
Allow the drawer to exceed 480px width on desktop without good reason — it dominates the view.
Use a title that describes the drawer's content or purpose.
Use drawers for primary content — they are supplementary by nature.
Code
<div class="lex-drawer-backdrop" aria-hidden="true"></div>
<aside class="lex-drawer lex-drawer--right" role="dialog" aria-modal="true"
aria-labelledby="drawer-title">
<div class="lex-drawer__header">
<h2 class="lex-drawer__title" id="drawer-title">Filters</h2>
<button class="lex-icon-button lex-icon-button--tertiary"
aria-label="Close drawer">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
</button>
</div>
<div class="lex-drawer__body">
<p>Drawer content goes here.</p>
</div>
<footer class="lex-drawer__footer">
<button class="lex-button lex-button--secondary lex-button--md">Reset</button>
<button class="lex-button lex-button--primary lex-button--md">Apply</button>
</footer>
</aside>.lex-drawer-backdrop {
position: fixed;
inset: 0;
background: var(--lex-drawer-backdrop-bg, rgba(0, 0, 0, 0.5));
z-index: var(--lex-z-drawer-backdrop, 999);
}
.lex-drawer {
position: fixed;
top: 0;
bottom: 0;
width: var(--lex-drawer-width, 400px);
max-width: 90vw;
background: var(--lex-drawer-bg, var(--lex-bg-surface-raised));
box-shadow: var(--lex-drawer-shadow, var(--lex-shadow-xl));
display: flex;
flex-direction: column;
z-index: var(--lex-z-drawer, 1000);
transition: transform 250ms ease;
}
.lex-drawer--right {
right: 0;
transform: translateX(0);
}
.lex-drawer--right[data-state="closed"] {
transform: translateX(100%);
}
.lex-drawer--left {
left: 0;
transform: translateX(0);
}
.lex-drawer--left[data-state="closed"] {
transform: translateX(-100%);
}
.lex-drawer__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--lex-space-16) var(--lex-space-24);
border-bottom: 1px solid var(--lex-border-default);
}
.lex-drawer__title {
font-size: var(--lex-font-size-lg);
font-weight: 600;
margin: 0;
}
.lex-drawer__body {
padding: var(--lex-space-24);
overflow-y: auto;
flex: 1;
}
.lex-drawer__footer {
display: flex;
justify-content: flex-end;
gap: var(--lex-space-8);
padding: var(--lex-space-16) var(--lex-space-24);
border-top: 1px solid var(--lex-border-default);
}// Using Lexicon CSS classes with React
export function FilterDrawer({ open, onClose }: { open: boolean; onClose: () => void }) {
return (
<Drawer open={open} onClose={onClose} side="right" title="Filters">
<Drawer.Body>
<p>Drawer content goes here.</p>
</Drawer.Body>
<Drawer.Footer>
<Button variant="secondary" onClick={onClose}>Reset</Button>
<Button variant="primary">Apply</Button>
</Drawer.Footer>
</Drawer>
);
}Design Tokens
| Token | Value (dark) | Value (light) | CSS property |
|---|---|---|---|
| --lex-drawer-bg | — | — | bg |
| --lex-drawer-width | — | — | width |
| --lex-drawer-shadow | — | — | shadow |
| --lex-drawer-backdrop | — | — | backdrop |
Accessibility
- Use role="dialog" with aria-modal="true" — same ARIA pattern as Modal.
- Provide aria-labelledby pointing to the drawer title element.
- Focus must be trapped inside the drawer — Tab and Shift+Tab cycle within.
- On open, move focus to the first focusable element or the close button.
- On close, return focus to the element that triggered the drawer.
- Body scroll must be locked while the drawer is open.
- Backdrop click and Escape key must both close the drawer.