Drawer
Detail without leaving.
About
Slide-in panel for record detail without leaving the list. It slides in from any edge over the navy scrim and the list stays visible behind it, or it fills the page. The host sets open; Escape, the scrim and the corner X ask to close and can be refused. Reach for it when the list gives the record its context.
Usage
Basic
The panel is role="dialog", named from heading, the page behind it is inert, and the list stays put behind the scrim. Focus opens on the first field in the body, on the body itself when it scrolls, or on the panel, and returns to the opener on close.
Due Sep 7, 2026
Balance $6,420.00
Initial focus
initial-focus is a selector resolved inside the drawer for the control that opens with focus, the same rule as sui-dialog; on a sui-button it lands on the inner button. Here it points at the note rather than the first field. A miss falls back to the first field, the scrolling body or the panel.
Four edges
position picks the edge. Right and left are sized by width, top and bottom by height, and both are capped at 90% of the viewport.
Full screen
position="full" covers the viewport with the same header, footer and close control, and fades in instead of sliding. width and height are ignored.
1 to 30 days $12,860.00
31 to 60 days $6,420.00
Over 60 days $18,300.00
Composed header
The header slot replaces heading and subtitle and supplies the accessible name, so the title can carry a status tag.
Issued Jul 2, 2026
Balance $18,300.00
Cancelable close
suiClose fires before the drawer closes and says why: escape, scrim or close-button. Here a stray scrim click is refused with preventDefault while the note is unsaved; Escape and the X still close.
Keyboard
| Key | Action |
|---|---|
| Tab Shift+Tab | Cycles through the controls in the panel and never leaves it. A panel opened from the drawer, a select list or a popover, counts as inside; focus that lands behind the scrim by any other route is brought back. |
| Escape | Asks to close with reason escape. Only the topmost open surface answers it: a popover, confirm popup or select list open inside the drawer takes it first and the drawer stays. |
| Enter Space | On the corner X, asks to close with reason close-button. |
| ArrowDown ArrowUp PageDown PageUp | Scroll the body when it overflows and holds focus, which is where a prose drawer opens. |
API
<sui-drawer>
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
heading | heading | string | '' | Drawer title (the reference title prop; renamed for the native attribute). Supplies the accessible name unless a header slot is given. |
height | height | number | 300 | Panel height in px for bottom and top (capped at 90% of the viewport). |
initialFocus | initial-focus | stringstring | undefined | A selector for the control that takes focus when the drawer opens, resolved inside the drawer: #memo, or .save for a sui-button, whose inner button then takes it. Without it focus lands on the first field in the body, else on the body itself when it scrolls, so the arrow and page keys scroll the drawer and not the page behind it, else on the panel; never on the corner X. The same rule as sui-dialog. A selector that matches nothing, or a disabled control, falls through to the same order. | |
open | open | boolean | false | Shows or hides the drawer. The host sets it; on a close request the element clears it itself unless the suiClose listener calls preventDefault(), so plain HTML works. When the drawer closes, a popup still open inside it, a select's list or a popover, closes with it and asks through its own suiClose with reason outside; a popup that has focus takes Escape first and the drawer stays. The element never opens itself, so there is no suiOpen; watch open. While open, and while it fades out, the drawer paints in the browser's top layer, above the rest of the page, so a transformed, clipping or stacked ancestor cannot trap or bury it. It stays in place in the element, so a listener on an ancestor, a <form> around the element and inherited styles reach the slotted content as they would anywhere else. Everything outside the drawer is inert and the page does not scroll until it closes; a node the page adds outside it while it is open goes inert too. The panel is role="dialog" with no aria-modal: the inert outside is what makes it modal to assistive technology, and with aria-modal VoiceOver would drop the toast region outside it. Toasts keep announcing, and Tab goes on from the drawer's last control into the toast stack and back. An element the page marks data-sui-keep-live (another library's dropdown appended to the body, a chat widget) stays usable with everything in it: it never goes inert, focus may move into it and back, and Tab reaches it the same way. The drawer paints in the top layer, so a marked panel that should show above it must paint there too, as a popover does. On a viewport too short for the panel with its header and footer pinned (a phone on its side, a page zoomed to 400%), the whole panel scrolls instead of only its body, so the actions stay in reach. |
position | position | "bottom" | "full" | "left" | "right" | "top" | 'right' | Edge the panel slides in from. full covers the whole viewport instead, with the same header and close control and no rounded corners; width and height are then ignored. |
returnFocus | return-focus | stringstring | undefined | A selector for the control that takes focus when the drawer closes, resolved in the document (or the shadow root) the element sits in when it closes, so it can name a row the drawer's own action created. Without it focus returns to the control that had it when the drawer opened; when that control has left the page or can no longer take focus, it goes to the nearest control in its place. The same rule as sui-dialog. A selector that matches nothing, or a disabled control, falls through to the same order. | |
subtitle | subtitle | stringstring | undefined | Secondary line under the title (customer, status). | |
width | width | number | 380 | Panel width in px for right and left (capped at 90% of the viewport). |
Events
| Event | Detail | React | Vue | Description |
|---|---|---|---|---|
suiClose | SuiCloseReason"cancel" | "close-button" | "escape" | "outside" | "pick" | "scrim" | "trigger" | onSuiClose | @sui-close | Fires before the drawer closes itself; detail is 'escape' | 'scrim' | 'close-button'. Cancelable: preventDefault() keeps it open. Refusing one reason, such as scrim while a form is dirty, leaves the others working. The corner X is always there: unlike sui-dialog there is no hide-close. It does not bubble, so a listener on the drawer never hears a select or a popover closing inside it. |
Slots
| Slot | Description |
|---|---|
(default) | Drawer body. |
footer | Action buttons, right-aligned on the sunken footer. |
header | Replaces heading / subtitle when the title needs a status tag, an icon or a document number chip. It supplies the accessible name. |
Types
SuiCloseReason typeWhy a dismissible surface is closing. Detail of the cancelable suiClose. A consumer expresses "a stray backdrop click must not close this" by calling preventDefault() on the reason it does not accept, which is why none of these needs a prop of its own. | |||
|---|---|---|---|
| No members. |
Theming
Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.
Color6
Typography2
Spacing and size1
Radius1
Elevation1
Motion3
Icon1
Every name above is listed with its value and its role in Foundations.