Drawer
A slide-over panel anchored to a window edge.
1use herogpui::components::drawer::{Drawer, DrawerCloseTrigger, DrawerPlacement};Usage
1use herogpui::prelude::{Drawer, DrawerCloseTrigger, DrawerPlacement};
2use gpui::prelude::*;
3
4stretch_col(
5 [
6 ("dr-left", "Left", DrawerPlacement::Left),
7 ("dr-right", "Right", DrawerPlacement::Right),
8 ("dr-top", "Top", DrawerPlacement::Top),
9 ("dr-bottom", "Bottom", DrawerPlacement::Bottom),
10 ]
11 .into_iter()
12 .map(|(key, label, placement)| {
13 let open = self.demo_overlay(key);
14 overlay_demo(
15 open,
16 key,
17 label,
18 Drawer::new()
19 .id(key)
20 .is_open(open)
21 .placement(placement)
22 .title(format!("From the {label}"))
23 .is_dismissible(true)
24 .child(DrawerCloseTrigger::new())
25 .child(gpui::div().child("The panel slides in along its edge."))
26 .on_open_change(bool_cb(cx.listener(
27 move |this, v: &bool, _, cx| {
28 this.set_demo_flag(key, *v);
29 cx.notify();
30 },
31 )))
32 .into_any_element(),
33 cx,
34 )
35 })
36 .collect()
37)Live HeroGPUI compiled to WebAssembly. Select any example without loading another WASM instance.
Anatomy
Rust types
DrawerDrawerCloseTrigger
Drawer is assembled from these builders. The API reference lists each one.
Customization
Appearance builders and theme tokens Drawer uses.
Styling
| Style | Description |
|---|---|
util::window_overlay + trap_tab | Covers the window even inside clipped or positioned containers, paints above later page content, and blocks pointer input to the page beneath. Outside presses dismiss through the panel's bounds. |
DrawerPlacement::Bottom/Top anchored match | Vertical placements pin the sheet to the bottom or top edge. |
DrawerPlacement::Left/Right anchored match | Horizontal placements pin the sheet to the left or right edge. |
panel p(px(24.)) + overlay bg + overlay_shadow | Sheet surface: overlay background and shadow with a uniform 24px inset. |
w_full + max_h(viewport * 0.85) | The full-width and 85% viewport cap match; does not round the sliding-edge corners. |
SIDE_EXTENT px(384.) + max_w(viewport * 0.85) | The 384px side extent matches w-96 with the 85vw cap; a desktop is past the sm step, so the smaller width never applies. |
anim::Motion::BACKDROP_IN / BACKDROP_OUT | The scrim fade exists, but reuses the shared modal backdrop motion (150/100ms ease-out) rather than the drawer's own timing. |
Backdrop::Opaque -> colors.backdrop | Default scrim colour. |
Backdrop::Blur -> colors.backdrop alpha 0.6 | Gpui has no backdrop filter, so Blur renders a lighter scrim instead. |
Backdrop::Transparent -> transparent_black | No scrim at all. |
header flex_col + gap(px(12.)) | Title column with a 12px gap; draws it only when a title is present and has no margin to reset. |
text_size(px(16.)) + FontWeight::MEDIUM | 16px medium foreground title; align-middle has no gpui text equivalent. |
flex_1 min_h(0) + overflow_y_scroll + 14/20px muted | Scrolling body with 14px/20px muted text; the -m/p pair is a focus-ring allowance and the inset is zero. Adds a 10px gap leaves to authors, and gpui has no overscroll-behavior containment. |
footer flex items_center justify_end + gap(px(8.)) | End-aligned action row with an 8px gap; the margin is supplied by the sibling rule. |
handle h(px(4.)) w(px(36.)) + hairline_radius + separator | Drag affordance bar, 4x36px separator with a hairline radius; inverts the bar inset to the moving edge for top placement instead of v3's padding swap. |
handle .pt(px(8.)) on DrawerPlacement::Top | Top-placement inset handling; moves the handle's gap to the top edge instead of v3's bottom-padding swap, so the sheet inset stays uniform. |
CloseButton absolute top(px(16.)) right(px(16.)) | Close button pinned 16px from the top end. |
body mt(px(8.)) + footer mt(px(20.)) | Sibling spacing: 8px below the header and 20px above the footer. |
anim::Motion::DRAWER_IN + entering_from(edge) | Enter timing is v3's own drawer token and ease, with no scale. |
anim::Motion::DRAWER_OUT + exiting_to(edge) | Exit drops to the drawer's 200ms token at the same ease. |
entering_from / exiting_to with explicit DrawerPlacement -> anim::Edge match | The sheet slides in and out by its own dimension from the anchored edge. |
DRAG_ACTIVATION 8. / DRAG_DISMISS_FRACTION 0.3 / DRAG_VELOCITY 0.5 | Drag-to-dismiss constants match useDrawerDrag verbatim; body presses and interactive descendants are excluded from the drag. |
API reference
Builders
| Builder | Type | Default | Description |
|---|---|---|---|
ParentElement:: | AnyElement | — | Trigger and backdrop elements; the port's ParentElement::extend feeds the body instead, with the backdrop built in and the trigger left to the caller. |
is_open + on_open_change | UseOverlayStateReturn | — | Controlled overlay state; takes the two builders the hook would feed rather than the hook return. |
backdrop( | Backdrop | Backdrop:: | Backdrop overlay style; all three variants render, but gpui has no backdrop-filter, so Blur draws a lighter scrim rather than a blur. |
is_dismissible( | bool | true | Close on backdrop click; gates outside-press and drag dismissal on it. The composed close trigger is not the backdrop: like it renders and closes regardless of this flag. |
is_keyboard_dismiss_disabled( | bool | false | Disable ESC key to close. |
is_open( | bool | — | Controlled open state. |
on_open_change( | Fn( | — | Open state change handler; fires with false on every dismissal path. |
placement( | DrawerPlacement | DrawerPlacement:: | Edge the drawer slides from. |
title + footer_child + extend body | AnyElement | — | Dialog content; header, body and footer compose through title, ParentElement::extend and footer_child instead of nested children. |
title( | AnyElement | — | Header content; renders its header only when a title is set, and both share the monolithic prop. |
ParentElement:: | AnyElement | — | Body content; ParentElement::extend feeds the scrollable body, but v3's per-part prop surface is not exposed. |
footer_child( | el | — | Footer content; repeated footer_child calls stack actions in the end-aligned row. |
new( | AnyElement | — | The drawer composes this part as a child: without children it draws the built-in CloseButton wired to the drawer's dismissal paths, and custom children replace the CloseButton's glyph while the press stays automatically wired to close — hands the composed part v3's slot="close" chaining. |
Parts
| Part | Description |
|---|---|
Drawer | Overlay root that owns open state, the focus trap and every dismissal path. |
Drawer | Scrim behind the sheet, dimmed but press-less; outside presses dismiss through the panel's own bounds. |
Drawer | Full-window positioning wrapper with the four placement alignments. |
Drawer | The sheet itself: overlay surface, 24px inset, drag and scroll surfaces. |
Drawer | Title column; the monolithic port renders it only when a title is set, without a composable children slot. |
Drawer | Title text; accepts a string via the title builder instead of composed children. |
Drawer | Native-scrolling content area fed by ParentElement::extend children. |
Drawer | End-aligned action row fed by repeated footer_child calls. |
Drawer | Separator bar that starts the drag-to-dismiss gesture. |
DrawerCloseTrigger | Composed part pinned 16px from the top end: the default content is the built-in CloseButton wired to the drawer's dismissal paths regardless of is_dismissible, and custom children replace the glyph while staying wired to close. With no dismissal callback to wire the part draws nothing. |
States
| Builder | State | Description |
|---|---|---|
CloseButton tab_stop_handle ring + trap_tab | Focus | Applied to the trigger and close button; the built-in close button rings, while the trigger is not a drawer part here. |
CloseButton hover surface | Hover | Applied to the close button on hover. |
CloseButton .active centered root-bounds shrink | Active | Applied to the trigger and close button when pressed; the close button uses a centered root-bounds shrink while fixed child content remains unscaled, and the absent trigger has no surface. |
OverlayPhase::Open branch + Motion::DRAWER_IN | Entering | Applied during the drawer opening animation. |
overlay_phase::Exiting + Motion::DRAWER_OUT | Exiting | Applied during the drawer closing animation; the sheet stays mounted for its 200ms slide. |
DrawerPlacement anchored match + anim::Edge | Placement | Top, bottom, left and right anchor the sheet and choose the slide edge. |