Style
GPUI styles an Element where it is built. The Styled trait supplies chainable methods for layout, spacing, color, borders, and text. Many names deliberately correspond to Tailwind CSS utilities: flex items-center gap-2 px-3 becomes .flex().items_center().gap_2().px_3() in Rust. This is a useful way to read and write GPUI layouts, but the values are typed Rust values rather than CSS classes.
use gpui_kit::*;
use gpui_kit::component::ActiveTheme as _;
div()
.flex()
.items_center()
.gap_2()
.px_3()
.py_2()
.bg(cx.theme().background)
.text_color(cx.theme().foreground)
.child("Search results")
The builder consumes and returns an element on each call. Rendering can build a fresh tree from current state; persistent application state belongs in an Entity or keyed element state. A style chain describes this frame’s presentation, not a stylesheet or a retained component instance. See RenderOnce for frame-local component construction.
#Build a first layout
Start with the region that owns the available space, then decide which child has a fixed width and which child can grow. Replace examples/hello_world/src/main.rs with this complete program, then run cargo run -p hello_world from the repository root:
use gpui_kit::*;
use gpui_kit::component::{h_flex, v_flex, ActiveTheme as _};
use gpui_kit::prelude::FluentBuilder as _;
struct StyleExample;
impl Render for StyleExample {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let compact = window.viewport_size().width < px(600.);
let layout = if compact { v_flex() } else { h_flex() };
let documents = (1..=60).map(|number| {
div().p_2().child(format!("Document {number:02}"))
});
layout
.items_stretch()
.size_full()
.bg(cx.theme().background)
.text_color(cx.theme().foreground)
.child(
v_flex()
.when(!compact, |nav| nav.w_64().flex_shrink_0())
.p_3()
.gap_2()
.child("Navigation")
.child("Overview · Documents"),
)
.child(
v_flex()
.flex_1()
.min_w_0()
.min_h_0()
.child(h_flex().p_3().child("Documents"))
.child(
v_flex()
.id("document-list")
.flex_1()
.min_h_0()
.overflow_y_scroll()
.child(v_flex().p_3().gap_2().children(documents)),
),
)
}
}
fn main() {
gpui_kit::application().run(|cx| {
gpui_kit::init(cx);
gpui_kit::open_window(WindowOptions::default(), cx, |_, cx| {
cx.new(|_| StyleExample)
})
.expect("failed to open window");
});
}
In a wide window, navigation occupies a fixed w_64() rail and the document pane fills the rest. Narrow the content area below 600 logical pixels: the same navigation moves above the document pane, while the document list remains independently scrollable. Scroll to Document 60, then resize the window in both directions. The header stays in place while the list scrolls. This threshold is an explicit Rust condition evaluated when the view renders, not a Tailwind responsive prefix. The navigation in this small exercise is illustrative text; a real application should use reachable navigation controls.
h_flex() makes a row and centers its children on the cross axis. .items_stretch() overrides that default so both panes occupy the row’s height. v_flex() makes a column whose children stretch across its width. The fixed navigation pane does not shrink in the wide layout; the document pane takes the remaining width. The scroll area takes the remaining height below the header. The window-sized root gives .size_full() a resolved height; the min_h_0() calls let its flexible descendants shrink into a scroll viewport.
#Decide where each size belongs
| Need | Put it on | Why |
|---|---|---|
| Space between siblings | The parent with .gap_3() | Gap separates children without adding padding at the outer edge. |
| Space inside a surface | The surface with .p_3() | Padding moves its content inward and participates in its layout size. |
| A fixed rail beside flexible content | Rail .w_64().flex_shrink_0(); content .flex_1().min_w_0() | The rail keeps its width while the content may shrink below its natural text width. |
| A header above scrolling content | Column with a height; scroll child .flex_1().min_h_0() | The child can shrink into the available height, creating a real scroll viewport. |
| Half the parent width | .w(relative(0.5)) on the child | The fraction resolves against the relevant parent dimension during layout. |
w_full() and h_full() mean the full available dimension. A percentage height still needs a definite height upstream. Min and max sizes constrain the result; they do not give an otherwise unbounded scroll area a viewport. For a long single-line label in a row, combine .flex_1().min_w_0().truncate() on the label container. .truncate() only changes text overflow; it cannot force an inflexible sibling to give up width.
#Choose clipping, scrolling, or positioning
.overflow_hidden() clips content; it does not make that content scrollable. On a stateful element, .overflow_y_scroll() creates vertical scrolling once the element has a bounded height. GPUI Kit’s .overflow_y_scrollbar() adds a visible scrollbar and wraps the original element as its scroll area; it is an extension from ScrollableElement, not a Styled method. Keep one owner for each scroll region, and put content padding inside that region if its scrollbar should sit at the pane edge. See Coding Guides for scroll ownership and measurement.
Normal flex children consume layout space. Use .relative() on a container and .absolute().top_0().right_0() on a badge when the badge should overlay content without consuming a row or column slot. Offset setters position an absolute child; they do not make an ordinary flex child absolute. Later siblings normally paint over earlier siblings; general Styled has no z_index(...) method.
#Theme and scale
Use semantic colors and radius from cx.theme() for application surfaces. GPUI Kit components already apply their normal theme appearance; style their instances for local layout or an intentional refinement. The named spacing and size helpers are rem based: _1 is 0.25rem, _2 is 0.5rem, _3 is 0.75rem, and _4 is 1rem. In a GPUI Kit Root, the active theme’s base font size sets the window rem size, so a font size or zoom change also changes rem based geometry. Use typed setters such as .gap(rems(0.625)) when the scale has no suitable step; reserve px(...) for a dimension that truly needs pixels. See Geometry for length types and Fonts for the theme’s rem setup.
#Troubleshoot the result
| Symptom | Check |
|---|---|
| The rail does not move above the documents when the window narrows | Resize the drawable content area below 600 logical pixels. The condition reads window.viewport_size().width during render; changing only display scale does not cross this logical-pixel threshold. |
Document 60 cannot be reached | Place the pointer over the document list and scroll there. Keep .id("document-list").overflow_y_scroll() on the bounded list region, with .flex_1().min_h_0() on it and its containing column. |
| The header moves when scrolling | Ensure the header is a sibling of the list viewport, not a child inside the scrolling element. |
| A pane header disappears at the top | h_flex() centers children by default; stretch the row’s children or give that pane full height. |
| A title overflows instead of truncating | Release the flexible child’s minimum width with .min_w_0() and bound the text width. |
| A list grows past the window instead of scrolling | Give its ancestors a resolved height, let the flexible child shrink with .min_h_0(), and put scrolling on the intended viewport. |
| A scrollbar sits inside the pane edge | Check which element owns scrolling and whether padding wraps the scroll owner. |
| Layout changes after theme zoom | Recheck rem based dimensions and any cached measurements that assumed the old rem size. |
#Common Styled methods
GPUI uses underscores where Tailwind uses hyphens. Where a matching concept exists, the first column links to its official Tailwind CSS reference in a new tab. The names are GPUI methods; call them on a Styled value, such as div().gap_2(). These tables cover the distinct Styled operations and the generic setters generated by its macros. Numeric variants follow the families described below, rather than occupying thousands of near-identical rows. Linked pages explain the corresponding styling concept; they do not imply identical behavior in GPUI and a browser.
#Display and visibility
| Method | Description |
|---|---|
block | Use block layout. |
flex | Use Flexbox layout. |
grid | Use Grid layout. |
hidden | Remove the element from layout and painting. |
invisible | Keep its layout space but do not paint it. |
visible | Restore painting while retaining the element’s layout. |
#Flexbox and Grid
| Method | Description |
|---|---|
flex_row | Place flex children along a row. |
flex_col | Place flex children along a column. |
flex_wrap | Allow flex children to wrap. |
flex_nowrap | Keep flex children on one line. |
items_start | Align children to the start of the cross axis. |
items_center | Center children on the cross axis. |
items_stretch | Stretch children along the cross axis. |
self_center | Center this child on its parent’s cross axis. |
self_stretch | Stretch this child on its parent’s cross axis. |
justify_center | Center children on the main axis. |
justify_between | Put free space between children on the main axis. |
content_between | Distribute wrapped lines along the cross axis. |
flex_basis | Set a typed initial main axis size. |
flex_1 | Grow and shrink with a zero flex basis. |
flex_auto | Grow and shrink from the item’s automatic basis. |
flex_grow_1 | Allow a flex child to grow. |
flex_shrink_0 | Prevent a flex child from shrinking. |
grid_cols | Set a numbered column template. |
grid_rows | Set a numbered row template. |
col_span | Span a specified number of grid columns. |
row_span | Span a specified number of grid rows. |
flex_row_reverse | Reverse row order. |
flex_col_reverse | Reverse column order. |
flex_wrap_reverse | Wrap flex lines in reverse order. |
items_end | Align children to the cross-axis end. |
items_baseline | Align children’s text baselines. |
self_start | Align this item to the cross-axis start. |
self_end | Align this item to the cross-axis end. |
self_flex_start | Align this item to flex start. |
self_flex_end | Align this item to flex end. |
self_baseline | Align this item’s text baseline. |
justify_start | Pack children at the main-axis start. |
justify_end | Pack children at the main-axis end. |
justify_around | Distribute space around children. |
justify_evenly | Distribute equal spaces along the main axis. |
content_normal | Use the default cross-axis line packing. |
content_start | Pack wrapped lines at cross-axis start. |
content_center | Center wrapped lines on the cross axis. |
content_end | Pack wrapped lines at cross-axis end. |
content_around | Distribute space around wrapped lines. |
content_evenly | Distribute equal space between wrapped lines. |
content_stretch | Stretch wrapped lines along the cross axis. |
flex_initial | Use an automatic basis and shrink without growing. |
flex_none | Prevent both flex growth and shrinkage. |
flex_grow | Set a numeric flex growth factor. |
flex_grow_0 | Prevent flex growth. |
flex_shrink | Set a numeric flex shrink factor. |
flex_shrink_1 | Allow flex shrinkage. |
grid_cols_min_content | Create columns with min-content minimums. |
grid_cols_max_content | Create columns with max-content limits. |
grid_rows_min_content | Create rows with min-content minimums. |
grid_rows_max_content | Create rows with max-content limits. |
col_start | Set the starting grid column line. |
col_start_auto | Use automatic column start placement. |
col_end | Set the ending grid column line. |
col_end_auto | Use automatic column end placement. |
col_span_full | Span the full grid column range. |
row_start | Set the starting grid row line. |
row_start_auto | Use automatic row start placement. |
row_end | Set the ending grid row line. |
row_end_auto | Use automatic row end placement. |
row_span_full | Span the full grid row range. |
#Space and size
| Method | Description |
|---|---|
gap_2 | Set row and column gaps to 0.5rem. |
gap | Set a typed gap on both axes. |
gap_x_2 | Set the column gap to 0.5rem. |
gap_y_2 | Set the row gap to 0.5rem. |
p_4 | Set padding on all sides to 1rem. |
p | Set typed padding on all sides. |
px_3 | Set horizontal padding to 0.75rem. |
py_2 | Set vertical padding to 0.5rem. |
mt_4 | Set top margin to 1rem. |
m | Set typed margins on all sides. |
m_auto | Set automatic margins on all sides. |
w | Set a typed width. |
w_full | Fill the available width. |
h | Set a typed height. |
h_full | Fill the available height. |
min_w | Set a typed minimum width. |
min_w_0 | Permit width to shrink to zero. |
max_w | Set a typed maximum width. |
size | Set typed width and height together. |
size_4 | Set width and height to 1rem. |
aspect_square | Keep a 1:1 width to height ratio. |
mt | Set a typed top margin. |
mb | Set a typed bottom margin. |
mx | Set typed horizontal margins. |
my | Set typed vertical margins. |
ml | Set a typed left margin. |
mr | Set a typed right margin. |
px | Set typed horizontal padding. |
py | Set typed vertical padding. |
pt | Set a typed top padding. |
pb | Set a typed bottom padding. |
pl | Set a typed left padding. |
pr | Set a typed right padding. |
gap_x | Set a typed column gap. |
gap_y | Set a typed row gap. |
min_h | Set a typed minimum height. |
max_h | Set a typed maximum height. |
aspect_ratio | Set a numeric width to height ratio. |
#Position and overflow
| Method | Description |
|---|---|
relative | Keep normal layout placement and establish a positioned ancestor. |
absolute | Position relative to an ancestor using inset offsets. |
inset | Set a typed offset on all four sides. |
inset_0 | Set top, right, bottom, and left offsets to zero. |
top | Set a typed top offset. |
top_0 | Set the top offset to zero. |
overflow_hidden | Clip overflowing content on both axes. |
overflow_x_hidden | Clip horizontal overflow only. |
overflow_y_hidden | Clip vertical overflow only. |
bottom | Set a typed bottom offset. |
left | Set a typed left offset. |
right | Set a typed right offset. |
scrollbar_width | Reserve a typed scrollbar width for scrolling layout. |
#Color and borders
| Method | Description |
|---|---|
bg | Set a typed background fill. |
text_color | Set a typed foreground color. |
border_1 | Set a one pixel border on all sides. |
border_t_1 | Set a one pixel top border. |
border_color | Set a typed border color. |
border_dashed | Draw borders with a dashed style. |
rounded | Set a typed corner radius. |
rounded_lg | Use the named large corner radius. |
rounded_full | Round corners as far as the size permits. |
border | Set a typed border width on all sides. |
border_t | Set a typed top border width. |
border_b | Set a typed bottom border width. |
border_l | Set a typed left border width. |
border_r | Set a typed right border width. |
border_x | Set typed left and right border widths. |
border_y | Set typed top and bottom border widths. |
rounded_t | Set typed radii on the top corners. |
rounded_b | Set typed radii on the bottom corners. |
rounded_l | Set typed radii on the left corners. |
rounded_r | Set typed radii on the right corners. |
rounded_tl | Set a typed top-left radius. |
rounded_tr | Set a typed top-right radius. |
rounded_bl | Set a typed bottom-left radius. |
rounded_br | Set a typed bottom-right radius. |
#Typography
| Method | Description |
|---|---|
font_family | Set a font family by name. |
font_weight | Set a typed font weight. |
italic | Use italic text. |
text_size | Set a typed font size. |
text_xs | Use the extra small text size. |
text_sm | Use the small text size. |
text_lg | Use the large text size. |
text_left | Align text to the left. |
text_center | Center text within its line. |
text_right | Align text to the right. |
line_height | Set a typed line height. |
whitespace_normal | Allow normal text wrapping. |
whitespace_nowrap | Prevent text from wrapping. |
text_ellipsis | Truncate overflowing text at the end with an ellipsis. |
truncate | Clip single line text and add an ellipsis. |
line_clamp | Limit text to a chosen number of lines. |
text_base | Use the base text size. |
text_xl | Use the extra large text size. |
text_2xl | Use the 2× large text size. |
text_3xl | Use the 3× large text size. |
text_align | Set a typed text alignment. |
not_italic | Use upright text. |
underline | Underline the text. |
line_through | Strike through the text. |
text_decoration_none | Remove text decoration. |
text_decoration_color | Set text decoration color. |
text_decoration_solid | Use a solid text decoration line. |
text_decoration_wavy | Use a wavy text decoration line. |
text_decoration_0 | Set decoration thickness to zero. |
text_decoration_1 | Set decoration thickness to one pixel. |
text_decoration_2 | Set decoration thickness to two pixels. |
text_decoration_4 | Set decoration thickness to four pixels. |
text_decoration_8 | Set decoration thickness to eight pixels. |
text_overflow | Set typed text overflow behavior. |
font_features | Set OpenType font features. |
#Effects and cursor
| Method | Description |
|---|---|
shadow_none | Remove box shadows. |
shadow_sm | Apply the named small box shadow. |
shadow_md | Apply the named medium box shadow. |
opacity | Set opacity with a floating point value. |
cursor_pointer | Use the pointing hand cursor on hover. |
cursor_text | Use a text insertion cursor on hover. |
shadow | Set a typed list of box shadows. |
shadow_2xs | Apply the named 2× extra small shadow. |
shadow_xs | Apply the named extra small shadow. |
shadow_lg | Apply the named large shadow. |
shadow_xl | Apply the named extra large shadow. |
shadow_2xl | Apply the named 2× extra large shadow. |
cursor | Set a typed mouse cursor. |
cursor_default | Use the default cursor. |
cursor_move | Use a move cursor. |
cursor_not_allowed | Use the not-allowed cursor. |
cursor_context_menu | Use a context-menu cursor. |
cursor_crosshair | Use a crosshair cursor. |
cursor_vertical_text | Use a vertical-text cursor. |
cursor_alias | Use an alias cursor. |
cursor_copy | Use a copy cursor. |
cursor_no_drop | Use a no-drop cursor. |
cursor_grab | Use a grab cursor. |
cursor_grabbing | Use a grabbing cursor. |
cursor_ew_resize | Use a horizontal resize cursor. |
cursor_ns_resize | Use a vertical resize cursor. |
cursor_nesw_resize | Use a northeast to southwest resize cursor. |
cursor_nwse_resize | Use a northwest to southeast resize cursor. |
cursor_col_resize | Use a column resize cursor. |
cursor_row_resize | Use a row resize cursor. |
cursor_n_resize | Use an upward resize cursor. |
cursor_e_resize | Use a rightward resize cursor. |
cursor_s_resize | Use a downward resize cursor. |
cursor_w_resize | Use a leftward resize cursor. |
#GPUI-specific methods
These APIs have no direct Tailwind utility. Their names remain unlinked so the table does not suggest a false correspondence.
| Method | Description |
|---|---|
font | Replace the text font with a typed GPUI Font. |
min_size | Set typed minimum width and height together. |
max_size | Set typed maximum width and height together. |
text_bg | Set the background color of text runs, rather than the element box. |
text_ellipsis_start | Truncate at the start to preserve the end of text. |
text_ellipsis_middle | Truncate in the middle to preserve both ends. |
debug | Draw a debug outline in debug builds. |
debug_below | Draw debug outlines for this element and conforming descendants in debug builds. |
style | Return the mutable StyleRefinement used by the element; this is the trait’s low-level accessor. |
text_style | Return the mutable text refinement within the element style. |
grid_location_mut | Access the mutable grid placement within a StyleRefinement. |
The macros also generate methods for the size (w, h, size, min_size, min_w, min_h, max_size, max_w, max_h), gap (gap, gap_x, gap_y), margin (m, mt, mb, mx, my, ml, mr), padding (p, pt, pb, px, py, pl, pr), and inset (inset, top, bottom, left, right) families. For example, w_64, px_3, and top_0 are real methods. Their shared numeric suffixes are 0, 0p5, 1, 1p5, 2, 2p5, 3, 3p5, every integer from 4 to 12, then 16, 20, 24, 32, 40, 48, 56, 64, 72, 80, 96, 112, and 128. They also include _px, _full, and fractional suffixes such as _1_2; only families that accept auto generate _auto. Non-auto values also have _neg_ forms, such as mt_neg_2. Border sides (border, border_t, border_b, border_l, border_r, border_x, border_y) have pixel-width suffixes 0 through 12, plus 16, 20, 24, and 32. Rounded sides and corners use none, xs, sm, md, lg, xl, 2xl, 3xl, and full. Use these mechanically generated names only when the resulting property makes sense for the layout.
Spacing helpers use a rem based scale: _1 is 0.25rem, _2 is 0.5rem, _3 is 0.75rem, and _4 is 1rem. For a value outside the named helpers, use a typed setter such as .gap(rems(0.625)), .w(px(240.)), or .w(relative(0.5)). relative(0.5) expresses half the available relative size; px(...) expresses pixels. The named scale and method set are GPUI’s implementation, so check the actual API rather than assuming every Tailwind class exists.
#What a style call changes
Every Styled element exposes fn style(&mut self) -> &mut StyleRefinement. A call such as .px_3() writes the relevant optional padding fields; .bg(...) writes the background field. Fields left unset do not replace existing values when refinements are merged. The resolved Style holds both layout data and presentation data.
Styled calls → StyleRefinement → resolved Style
├─ layout fields → Taffy → bounds
└─ color, text, shadow, cursor → GPUI paint and interaction
In an element’s request_layout phase, GPUI passes layout fields such as display, size, padding, gap, flex alignment, position, and grid placement, together with child layout IDs, to Taffy. Taffy computes the geometry. GPUI then uses the bounds in prepaint and the Paint phase for drawing and hit testing. Taffy does not implement GPUI’s text shaping, hover listeners, Actions, or painting.
StyleRefinement itself implements Styled, so a state style closure can use the same utility methods. Interaction variants such as .hover(|style| style.bg(...)) belong to InteractiveElement, and need an interactive element. Conditional builder calls are different: .when(...) chooses a chain step while this frame is built.
#Fluent composition and trait boundaries
The fluent surface combines several traits. Styled supplies style methods. ParentElement supplies .child(...). InteractiveElement supplies .id(...), returning a Stateful<Div> that supports identity dependent methods such as .overflow_y_scroll(). InteractiveElement also supplies state style refinements such as .hover(...). Every IntoElement implements FluentBuilder; IntoElement alone does not grant styling, children, or interaction. If a method is missing, check the receiver’s trait and whether a preceding call changed its type.
FluentBuilder method | Effect |
|---|---|
map | Transform the value and optionally change its return type. |
when | Apply a builder step when a boolean is true. |
when_else | Choose between two steps that both return the same builder type. |
when_some | Apply a step and pass the value inside an Option. |
when_none | Apply a step when a referenced Option is empty. |
use gpui_kit::*;
use gpui_kit::component::ActiveTheme as _;
div()
.id("result-row")
.px_3()
.py_2()
.when(selected, |row| row.bg(cx.theme().selection))
.when_some(subtitle, |row, text| row.child(text))
.child(title)
Conditional closures consume and return the builder. The condition is evaluated during the current render; it is not a subscription. .map(...) can return a different type. Use an Entity to own state that changes across frames.
For example, .when(selected, ...) evaluates selected while building this frame; .hover(|style| ...) installs a style refinement for pointer hover. A scroll call needs a stateful element, so give the intended scroll owner an .id(...) first. An arbitrary component implementing IntoElement cannot be assumed to accept .child(...) or .bg(...); inspect its own builder API or wrap it in a div() that owns those styles.
GPUI Kit adds StyledExt for neutral helpers such as h_flex, v_flex, and refine_style, and ThemeStyled for theme driven appearance such as popover_style(cx). These are extensions on top of GPUI’s Styled, not Tailwind utilities. When implementing a custom element, return its StyleRefinement from style() and apply the resolved style during layout and paint. Implement only the child and interaction traits that the element can actually support.
The correspondence with Tailwind is deliberately scoped: there are no CSS selectors, cascade, responsive prefixes, or promise that every Tailwind utility has a GPUI method. Use GPUI’s typed layout helpers for geometry, GPUI Kit theme tokens for product appearance, and explicit element or entity APIs for behavior.