Drawer
Drawer is a controlled side panel that slides in from the right. Choose it for work that belongs alongside the page; choose a Dialog when the decision must block the page until it is resolved.
Examples
Details with a fixed action
Put the action that completes the side task in footer. The panel keeps the page behind it, so
people can retain its context while they review or change something small.
Read-only details
Omit footer when there is no action to pin. That removes the footer chrome entirely instead of
leaving an empty divider at the bottom of the panel.
When to use
Use a Drawer for details or a short task that benefits from the page staying visible: inspecting an activity record, editing a small set of fields, or reviewing context before an action.
Reach for something else when:
- A decision must stop progress — use Dialog, a centred modal that makes the blocking choice explicit.
- The task needs its own destination, history or shareable URL — use a route. A drawer leaves the current page behind it; a full page does not.
- The message only confirms something that already happened — use Toast or an inline Alert.
Accessibility
- The panel is
role="dialog"witharia-modal="true", named bytitlethrougharia-labelledby.titleis the heading, not the native HTML tooltip attribute. - On open, focus moves into the panel and stays trapped there. On close, focus returns to the element that opened it; body scroll is locked while it is open.
- Escape, the close button and a backdrop click call
onClose. The backdrop is a pointer-only convenience; keyboard users close with Escape or the close button. - The close button's accessible name defaults to the English string "Close". Pass
closeLabelto translate it for localized products. - The component is portaled to
document.bodyby default. Passcontaineronly when your app needs a different portal host.
Props
| Name | Type | Required | Description |
|---|---|---|---|
open | boolean | Required | Controls visibility. `true` mounts the portaled overlay and drawer panel. |
onClose | () => void | — | Called when the user dismisses the drawer with Escape, the backdrop, or the close button. |
closeLabel | string | — | Accessible name for the close button. Default: `"Close"`. |
title | ReactNode | Required | Heading rendered in the drawer header and used as its accessible name. |
footer | ReactNode | — | Fixed actions rendered in the footer. Omit to remove the footer chrome. |
container | HTMLElement | — | Portal host. Defaults to `document.body`. |
children | ReactNode | Required | Drawer body content. |
Plain HTML
Without React, compose the same classes — you own open state, focus trapping, scroll locking and the Escape path:
<div class="lyra-drawer-overlay">
<div class="lyra-drawer" role="dialog" aria-modal="true" aria-labelledby="drawer-title">
<div class="lyra-drawer__header">
<h2 class="lyra-drawer__title" id="drawer-title">Project details</h2>
<button class="lyra-drawer__close" type="button" aria-label="Close">×</button>
</div>
<div class="lyra-drawer__body">Review the latest deployment before you continue.</div>
<div class="lyra-drawer__footer">
<button class="lyra-btn lyra-btn--primary lyra-btn--md" type="button">Done</button>
</div>
</div>
</div>