Customization

Create a named HeroGPUI theme by overriding semantic colors and layout tokens.

Start from a light or dark Theme and override the semantic colors or layout values your application needs. Theme::builder(id, base) names the result, and derived values follow the base token they came from.

The builder

custom theme
1use gpui::px;
2use herogpui::core::oklch;
3use herogpui::theme::{snow, Theme};
4
5let violet = Theme::builder("violet", Theme::light())
6    .accent(oklch(0.55, 0.23, 295.0))   // hover / soft / focus all derive
7    .role("success", oklch(0.73, 0.19, 150.0), snow())
8    .radius(px(6.))                     // field_radius follows at 1.5x
9    .build();
10
11herogpui::theme::set_theme(violet, cx);

Every method the builder exposes:

Theme builder methods
MethodWhat it sets
id(id)Names the theme — the id `use_theme(id, cx)` activates it by.
appearance(appearance)Light or dark — decides which shadow set and hairline the layout tokens use.
radius(px)`--radius`. `--field-radius` follows at 1.5× unless overridden afterwards.
field_radius(px)`--field-radius` on its own.
border_width(px)`--border-width`, the weight a separator draws.
disabled_opacity(f32)`--disabled-opacity`.
background / foreground / mutedThe base page tokens.
border / separator / focus / link / backdropThe remaining base tokens, each set on its own.
surface(bg, fg)`--surface` / `--surface-foreground` — cards, accordions, disclosure groups.
surface_levels(secondary, tertiary)`--surface-secondary` and `--surface-tertiary` together.
overlay(bg, fg)`--overlay` — tooltips, popovers, modals, menus.
segment(bg, fg)`--segment` — the selected segment of a segmented control.
role(name, color, foreground)Any role's base value and foreground. `"accent"` is the fallback name; `"default"` also re-seeds `field.background`.
accent(color)`--accent` and a foreground derived for readability when you do not supply one. `--focus` tracks it.
field(bg, fg) / field_placeholder / field_borderThe field tokens individually.
build()Returns the `Theme`.

Overriding a base token

Override a base token and its derived values follow. The override replaces the input while preserving the mix weights: a role's hover and soft ratios carry over, --focus keeps tracking --accent, and a foreground override flows into soft_foreground at render time without rebuilding the theme.

Rust
1// Override one base token; every derived value follows.
2let violet = Theme::builder("violet", Theme::light())
3    .accent(oklch(0.55, 0.23, 295.0))
4    .build();
5
6// `accent.hover()` and `accent.soft()` are the same color-mix
7// expressions, so they move with the base color.

Registering the result

set_theme(theme, cx) registers the theme under its id, activates it, and schedules every open window to repaint. The built-ins stay registered, so use_theme("light", cx) switches back — and a theme registered with set_theme is thereafter switchable to by id like any other. See Dark Mode for the switching rules.

Colour maths

The color functions used by the theme crate are public in herogpui-core. Use them to create OKLCH values, mix colors in Oklab and choose readable foregrounds:

Rust
1use herogpui::core::{
2    oklch, oklcha, mix_oklab, soft_mix, with_alpha, readable_color,
3};
4
5let brand    = oklch(0.55, 0.23, 295.0);   // lightness, chroma, hue
6let translucent = oklcha(0.55, 0.23, 295.0, 0.6);
7let hover    = mix_oklab(brand, foreground, 0.10);  // 10% toward the foreground
8let soft     = soft_mix(brand, 0.15);               // 15% over transparent
9let faint    = with_alpha(brand, 0.4);
10let label    = readable_color(brand);               // a readable foreground for it