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
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:
| Method | What 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 / muted | The base page tokens. |
border / separator / focus / link / backdrop | The 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_border | The 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.
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:
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