Overlays

Drawer

A slide-over panel anchored to a window edge.

Rust
1use herogpui::components::drawer::{Drawer, DrawerCloseTrigger, DrawerPlacement};

Usage

Rust
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

Drawer styling
StyleDescription
util::window_overlay + trap_tabCovers 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 matchVertical placements pin the sheet to the bottom or top edge.
DrawerPlacement::Left/Right anchored matchHorizontal placements pin the sheet to the left or right edge.
panel p(px(24.)) + overlay bg + overlay_shadowSheet 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_OUTThe scrim fade exists, but reuses the shared modal backdrop motion (150/100ms ease-out) rather than the drawer's own timing.
Backdrop::Opaque -> colors.backdropDefault scrim colour.
Backdrop::Blur -> colors.backdrop alpha 0.6Gpui has no backdrop filter, so Blur renders a lighter scrim instead.
Backdrop::Transparent -> transparent_blackNo 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::MEDIUM16px medium foreground title; align-middle has no gpui text equivalent.
flex_1 min_h(0) + overflow_y_scroll + 14/20px mutedScrolling 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 + separatorDrag 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::TopTop-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 matchThe sheet slides in and out by its own dimension from the anchored edge.
DRAG_ACTIVATION 8. / DRAG_DISMISS_FRACTION 0.3 / DRAG_VELOCITY 0.5Drag-to-dismiss constants match useDrawerDrag verbatim; body presses and interactive descendants are excluded from the drag.

API reference

Builders

Drawer builders
BuilderTypeDefaultDescription
ParentElement::extend body onlyAnyElementTrigger 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_changeUseOverlayStateReturnControlled overlay state; takes the two builders the hook would feed rather than the hook return.
backdrop(Backdrop)BackdropBackdrop::OpaqueBackdrop 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)booltrueClose 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)boolfalseDisable ESC key to close.
is_open(bool)boolControlled open state.
on_open_change(callback)Fn(isOpen: bool) -> ()Open state change handler; fires with false on every dismissal path.
placement(DrawerPlacement)DrawerPlacementDrawerPlacement::BottomEdge the drawer slides from.
title + footer_child + extend bodyAnyElementDialog content; header, body and footer compose through title, ParentElement::extend and footer_child instead of nested children.
title(value)AnyElementHeader content; renders its header only when a title is set, and both share the monolithic prop.
ParentElement::extendAnyElementBody content; ParentElement::extend feeds the scrollable body, but v3's per-part prop surface is not exposed.
footer_child(el)elFooter content; repeated footer_child calls stack actions in the end-aligned row.
new() + ParentElement childrenAnyElementThe 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

Drawer parts
PartDescription
DrawerOverlay root that owns open state, the focus trap and every dismissal path.
DrawerScrim behind the sheet, dimmed but press-less; outside presses dismiss through the panel's own bounds.
DrawerFull-window positioning wrapper with the four placement alignments.
DrawerThe sheet itself: overlay surface, 24px inset, drag and scroll surfaces.
DrawerTitle column; the monolithic port renders it only when a title is set, without a composable children slot.
DrawerTitle text; accepts a string via the title builder instead of composed children.
DrawerNative-scrolling content area fed by ParentElement::extend children.
DrawerEnd-aligned action row fed by repeated footer_child calls.
DrawerSeparator bar that starts the drag-to-dismiss gesture.
DrawerCloseTriggerComposed 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

Drawer states
BuilderStateDescription
CloseButton tab_stop_handle ring + trap_tabFocusApplied to the trigger and close button; the built-in close button rings, while the trigger is not a drawer part here.
CloseButton hover surfaceHoverApplied to the close button on hover.
CloseButton .active centered root-bounds shrinkActiveApplied 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_INEnteringApplied during the drawer opening animation.
overlay_phase::Exiting + Motion::DRAWER_OUTExitingApplied during the drawer closing animation; the sheet stays mounted for its 200ms slide.
DrawerPlacement anchored match + anim::EdgePlacementTop, bottom, left and right anchor the sheet and choose the slide edge.