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

NeedPut it onWhy
Space between siblingsThe parent with .gap_3()Gap separates children without adding padding at the outer edge.
Space inside a surfaceThe surface with .p_3()Padding moves its content inward and participates in its layout size.
A fixed rail beside flexible contentRail .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 contentColumn 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 childThe 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

SymptomCheck
The rail does not move above the documents when the window narrowsResize 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 reachedPlace 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 scrollingEnsure the header is a sibling of the list viewport, not a child inside the scrolling element.
A pane header disappears at the toph_flex() centers children by default; stretch the row’s children or give that pane full height.
A title overflows instead of truncatingRelease the flexible child’s minimum width with .min_w_0() and bound the text width.
A list grows past the window instead of scrollingGive 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 edgeCheck which element owns scrolling and whether padding wraps the scroll owner.
Layout changes after theme zoomRecheck 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

MethodDescription
blockUse block layout.
flexUse Flexbox layout.
gridUse Grid layout.
hiddenRemove the element from layout and painting.
invisibleKeep its layout space but do not paint it.
visibleRestore painting while retaining the element’s layout.

#Flexbox and Grid

MethodDescription
flex_rowPlace flex children along a row.
flex_colPlace flex children along a column.
flex_wrapAllow flex children to wrap.
flex_nowrapKeep flex children on one line.
items_startAlign children to the start of the cross axis.
items_centerCenter children on the cross axis.
items_stretchStretch children along the cross axis.
self_centerCenter this child on its parent’s cross axis.
self_stretchStretch this child on its parent’s cross axis.
justify_centerCenter children on the main axis.
justify_betweenPut free space between children on the main axis.
content_betweenDistribute wrapped lines along the cross axis.
flex_basisSet a typed initial main axis size.
flex_1Grow and shrink with a zero flex basis.
flex_autoGrow and shrink from the item’s automatic basis.
flex_grow_1Allow a flex child to grow.
flex_shrink_0Prevent a flex child from shrinking.
grid_colsSet a numbered column template.
grid_rowsSet a numbered row template.
col_spanSpan a specified number of grid columns.
row_spanSpan a specified number of grid rows.
flex_row_reverseReverse row order.
flex_col_reverseReverse column order.
flex_wrap_reverseWrap flex lines in reverse order.
items_endAlign children to the cross-axis end.
items_baselineAlign children’s text baselines.
self_startAlign this item to the cross-axis start.
self_endAlign this item to the cross-axis end.
self_flex_startAlign this item to flex start.
self_flex_endAlign this item to flex end.
self_baselineAlign this item’s text baseline.
justify_startPack children at the main-axis start.
justify_endPack children at the main-axis end.
justify_aroundDistribute space around children.
justify_evenlyDistribute equal spaces along the main axis.
content_normalUse the default cross-axis line packing.
content_startPack wrapped lines at cross-axis start.
content_centerCenter wrapped lines on the cross axis.
content_endPack wrapped lines at cross-axis end.
content_aroundDistribute space around wrapped lines.
content_evenlyDistribute equal space between wrapped lines.
content_stretchStretch wrapped lines along the cross axis.
flex_initialUse an automatic basis and shrink without growing.
flex_nonePrevent both flex growth and shrinkage.
flex_growSet a numeric flex growth factor.
flex_grow_0Prevent flex growth.
flex_shrinkSet a numeric flex shrink factor.
flex_shrink_1Allow flex shrinkage.
grid_cols_min_contentCreate columns with min-content minimums.
grid_cols_max_contentCreate columns with max-content limits.
grid_rows_min_contentCreate rows with min-content minimums.
grid_rows_max_contentCreate rows with max-content limits.
col_startSet the starting grid column line.
col_start_autoUse automatic column start placement.
col_endSet the ending grid column line.
col_end_autoUse automatic column end placement.
col_span_fullSpan the full grid column range.
row_startSet the starting grid row line.
row_start_autoUse automatic row start placement.
row_endSet the ending grid row line.
row_end_autoUse automatic row end placement.
row_span_fullSpan the full grid row range.

#Space and size

MethodDescription
gap_2Set row and column gaps to 0.5rem.
gapSet a typed gap on both axes.
gap_x_2Set the column gap to 0.5rem.
gap_y_2Set the row gap to 0.5rem.
p_4Set padding on all sides to 1rem.
pSet typed padding on all sides.
px_3Set horizontal padding to 0.75rem.
py_2Set vertical padding to 0.5rem.
mt_4Set top margin to 1rem.
mSet typed margins on all sides.
m_autoSet automatic margins on all sides.
wSet a typed width.
w_fullFill the available width.
hSet a typed height.
h_fullFill the available height.
min_wSet a typed minimum width.
min_w_0Permit width to shrink to zero.
max_wSet a typed maximum width.
sizeSet typed width and height together.
size_4Set width and height to 1rem.
aspect_squareKeep a 1:1 width to height ratio.
mtSet a typed top margin.
mbSet a typed bottom margin.
mxSet typed horizontal margins.
mySet typed vertical margins.
mlSet a typed left margin.
mrSet a typed right margin.
pxSet typed horizontal padding.
pySet typed vertical padding.
ptSet a typed top padding.
pbSet a typed bottom padding.
plSet a typed left padding.
prSet a typed right padding.
gap_xSet a typed column gap.
gap_ySet a typed row gap.
min_hSet a typed minimum height.
max_hSet a typed maximum height.
aspect_ratioSet a numeric width to height ratio.

#Position and overflow

MethodDescription
relativeKeep normal layout placement and establish a positioned ancestor.
absolutePosition relative to an ancestor using inset offsets.
insetSet a typed offset on all four sides.
inset_0Set top, right, bottom, and left offsets to zero.
topSet a typed top offset.
top_0Set the top offset to zero.
overflow_hiddenClip overflowing content on both axes.
overflow_x_hiddenClip horizontal overflow only.
overflow_y_hiddenClip vertical overflow only.
bottomSet a typed bottom offset.
leftSet a typed left offset.
rightSet a typed right offset.
scrollbar_widthReserve a typed scrollbar width for scrolling layout.

#Color and borders

MethodDescription
bgSet a typed background fill.
text_colorSet a typed foreground color.
border_1Set a one pixel border on all sides.
border_t_1Set a one pixel top border.
border_colorSet a typed border color.
border_dashedDraw borders with a dashed style.
roundedSet a typed corner radius.
rounded_lgUse the named large corner radius.
rounded_fullRound corners as far as the size permits.
borderSet a typed border width on all sides.
border_tSet a typed top border width.
border_bSet a typed bottom border width.
border_lSet a typed left border width.
border_rSet a typed right border width.
border_xSet typed left and right border widths.
border_ySet typed top and bottom border widths.
rounded_tSet typed radii on the top corners.
rounded_bSet typed radii on the bottom corners.
rounded_lSet typed radii on the left corners.
rounded_rSet typed radii on the right corners.
rounded_tlSet a typed top-left radius.
rounded_trSet a typed top-right radius.
rounded_blSet a typed bottom-left radius.
rounded_brSet a typed bottom-right radius.

#Typography

MethodDescription
font_familySet a font family by name.
font_weightSet a typed font weight.
italicUse italic text.
text_sizeSet a typed font size.
text_xsUse the extra small text size.
text_smUse the small text size.
text_lgUse the large text size.
text_leftAlign text to the left.
text_centerCenter text within its line.
text_rightAlign text to the right.
line_heightSet a typed line height.
whitespace_normalAllow normal text wrapping.
whitespace_nowrapPrevent text from wrapping.
text_ellipsisTruncate overflowing text at the end with an ellipsis.
truncateClip single line text and add an ellipsis.
line_clampLimit text to a chosen number of lines.
text_baseUse the base text size.
text_xlUse the extra large text size.
text_2xlUse the 2× large text size.
text_3xlUse the 3× large text size.
text_alignSet a typed text alignment.
not_italicUse upright text.
underlineUnderline the text.
line_throughStrike through the text.
text_decoration_noneRemove text decoration.
text_decoration_colorSet text decoration color.
text_decoration_solidUse a solid text decoration line.
text_decoration_wavyUse a wavy text decoration line.
text_decoration_0Set decoration thickness to zero.
text_decoration_1Set decoration thickness to one pixel.
text_decoration_2Set decoration thickness to two pixels.
text_decoration_4Set decoration thickness to four pixels.
text_decoration_8Set decoration thickness to eight pixels.
text_overflowSet typed text overflow behavior.
font_featuresSet OpenType font features.

#Effects and cursor

MethodDescription
shadow_noneRemove box shadows.
shadow_smApply the named small box shadow.
shadow_mdApply the named medium box shadow.
opacitySet opacity with a floating point value.
cursor_pointerUse the pointing hand cursor on hover.
cursor_textUse a text insertion cursor on hover.
shadowSet a typed list of box shadows.
shadow_2xsApply the named 2× extra small shadow.
shadow_xsApply the named extra small shadow.
shadow_lgApply the named large shadow.
shadow_xlApply the named extra large shadow.
shadow_2xlApply the named 2× extra large shadow.
cursorSet a typed mouse cursor.
cursor_defaultUse the default cursor.
cursor_moveUse a move cursor.
cursor_not_allowedUse the not-allowed cursor.
cursor_context_menuUse a context-menu cursor.
cursor_crosshairUse a crosshair cursor.
cursor_vertical_textUse a vertical-text cursor.
cursor_aliasUse an alias cursor.
cursor_copyUse a copy cursor.
cursor_no_dropUse a no-drop cursor.
cursor_grabUse a grab cursor.
cursor_grabbingUse a grabbing cursor.
cursor_ew_resizeUse a horizontal resize cursor.
cursor_ns_resizeUse a vertical resize cursor.
cursor_nesw_resizeUse a northeast to southwest resize cursor.
cursor_nwse_resizeUse a northwest to southeast resize cursor.
cursor_col_resizeUse a column resize cursor.
cursor_row_resizeUse a row resize cursor.
cursor_n_resizeUse an upward resize cursor.
cursor_e_resizeUse a rightward resize cursor.
cursor_s_resizeUse a downward resize cursor.
cursor_w_resizeUse 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.

MethodDescription
fontReplace the text font with a typed GPUI Font.
min_sizeSet typed minimum width and height together.
max_sizeSet typed maximum width and height together.
text_bgSet the background color of text runs, rather than the element box.
text_ellipsis_startTruncate at the start to preserve the end of text.
text_ellipsis_middleTruncate in the middle to preserve both ends.
debugDraw a debug outline in debug builds.
debug_belowDraw debug outlines for this element and conforming descendants in debug builds.
styleReturn the mutable StyleRefinement used by the element; this is the trait’s low-level accessor.
text_styleReturn the mutable text refinement within the element style.
grid_location_mutAccess 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 methodEffect
mapTransform the value and optionally change its return type.
whenApply a builder step when a boolean is true.
when_elseChoose between two steps that both return the same builder type.
when_someApply a step and pass the value inside an Option.
when_noneApply 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.