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.

CC BY 4.0 · Documentation prose and original illustrations GPUI Kit has rights to license may be copied or adapted with attribution. License. Credit GPUI Kit, link to this source page and the CC BY 4.0 license, and indicate any changes. Code examples and software source remain under Apache-2.0. Existing Apache permissions remain; third-party material retains its own terms.