Styling
Style HeroGPUI components with typed props, theme tokens, slots, and render closures.
Use documented builders for variants, theme tokens for shared values, GPUI's styling methods for elements you own, and render closures for state-aware content.
Variants carry the intent
Use the documented builder first. Variants, sizes and colors are typed values, so the component API makes the available choices explicit. Use the hierarchy below to compare the meaning of each button variant:
1// Variants and sizes are enums, checked at compile time.
2Button::new("edit")
3 .label("Edit")
4 .variant(Variant::Secondary)
5 .size(Size::Lg)How to style
Use a documented builder for variants, a theme token for shared values, and GPUI's styling methods for elements you own. There are no class strings to pass through.
| Route | Rust | When |
|---|---|---|
| Layout | full_width(true) | Use the builder the component documents, or wrap it in a styled div you own. |
| Colour | cx.role(Color::Accent) | Read a theme token so the value follows light and dark instead of pinning a shade. |
| Radius | util::soft_radius(cx) | One helper per radius step — see the table below. Each component names its own radius. |
| Spacing and type | .px(px(12.)).text_sm() | GPUI's Styled methods on the element you own. Inside a component, they stay there. |
The radius helpers
Each component uses a specific radius step, so util exposes one helper per step rather than a single universal radius:
| Helper | Step | Used by |
|---|---|---|
util::control_radius(cx) | 3xl (24px) | button, toggle button, avatar |
util::soft_radius(cx) | 2xl (16px) | chip, menu and list rows, colour area |
util::small_radius(cx) | xl (12px) | close button, tag, link, tooltip |
util::key_radius(cx) | lg (8px) | Kbd |
util::hairline_radius(cx) | sm (4px) | separator, skeleton |
util::field_radius(cx) | 12px | every form field |
util::container_radius(cx) | min(32px, 3xl) | cards, the table and every floating panel. Surface carries none — `.surface` declares no radius. |
State-based styling
Components expose hover, press and disabled state through the Rust API. Use .hover() for an element you own, the built-in animation helpers for presses, and is_disabled for disabled controls.
1// .hover() styles an element you own; components do the same
2// internally with anim::hover_fade, and a press is anim::pressed.
3let colors = cx.colors();
4let resting = colors.surface.background;
5let hovered = colors.default.soft();
6
7div()
8 .id("row")
9 .bg(resting)
10 .hover(move |s| s.bg(hovered))Render closures
Render closures let you draw a component part from the state or value the component already computed. The closure receives that value, so the caller does not need to re-derive it.
1// The closure is handed the value the component computed.
2Slider::new("volume", 50.)
3 .thumb(|index, value| {
4 div().child(format!("thumb {index}: {value}")).into_any_element()
5 })Wrapper components
To standardize a set of props, return a configured builder from a function. Builders are plain Rust values, so the caller can still set every remaining option.
1/// A save button, everywhere the same.
2fn save_button(id: impl Into<ElementId>) -> Button {
3 Button::new(id).variant(Variant::Primary).child("Save")
4}
5
6// Still a `Button`, so the caller keeps every other prop.
7save_button("save").is_pending(saving).full_width()Style through the Rust API
The Button struct and its variant method select the look, part builders such as CardHeader place the pieces, and theme tokens supply the values.
Control heights and widths
Desktop control heights are 32/36/40 for sm/md/lg. A labelled button has no minimum width: it hugs its content.