pub struct Theme {Show 54 fields
pub appearance: Appearance,
pub bg: Hsla,
pub surface: Hsla,
pub surface_raised: Hsla,
pub surface_card: Hsla,
pub surface_dialog: Hsla,
pub surface_overlay: Hsla,
pub element_hover: Hsla,
pub element_active: Hsla,
pub border: Hsla,
pub border_strong: Hsla,
pub text: Hsla,
pub text_muted: Hsla,
pub text_faint: Hsla,
pub text_dim: Hsla,
pub solid: Hsla,
pub on_solid: Hsla,
pub accent: Hsla,
pub accent_strong: Hsla,
pub on_accent: Hsla,
pub danger: Hsla,
pub danger_muted: Hsla,
pub warning: Hsla,
pub warning_muted: Hsla,
pub success: Hsla,
pub busy: Hsla,
pub success_muted: Hsla,
pub surface_raised_hover: Hsla,
pub band: Hsla,
pub input_bg: Hsla,
pub selection: Hsla,
pub cursor: Hsla,
pub caret: Hsla,
pub ring: Hsla,
pub danger_strong: Hsla,
pub code_text: Hsla,
pub code_wash: Hsla,
pub syntax: SyntaxPalette,
pub diff_add: Hsla,
pub diff_del: Hsla,
pub diff_hunk_bg: Hsla,
pub vibrancy_alpha: f32,
pub vibrancy: bool,
pub glass: bool,
pub material: MaterialSpec,
pub glass_regular: SurfaceSpec,
pub glass_clear: SurfaceSpec,
pub popover_surface: SurfaceStyle,
pub glass_magnify: f32,
pub glass_dispersion: f32,
pub font_sans: SharedString,
pub font_mono: SharedString,
pub font_sans_fallback: SharedString,
pub font_mono_fallback: SharedString,
}Expand description
The app theme. Two concrete instances — Theme::dark and Theme::light.
Fields§
§appearance: AppearanceWhich appearance these tokens were built for.
bg: HslaMain content panel. Dark: the deepest plane (#060606). Light: pure white — long-form content reads best on an unbroken white field.
surface: HslaShell / sidebar surface. Dark: one step up from bg. Light: one step
down (grey) — chrome recedes from the content plane in both, which is
the direction a naive invert gets backwards.
surface_raised: HslaRaised surface: opaque pills and chips that sit proud of the panel.
Dark: lighter than surface. Light: white, separated by border +
shadow rather than by lightness.
surface_card: HslaInline card resting on the main panel (auth gate, empty-state cards).
surface_dialog: HslaModal dialog, floating over a Theme::scrim.
surface_overlay: HslaPopover, menu and command-palette surface — the highest plane.
element_hover: HslaHover wash for interactive rows and buttons, on glass and off it alike:
../desktop’s --color-hover.
element_active: HslaActive/selected wash, one rung over the hover — --color-active.
border: HslaHairline border.
border_strong: HslaStronger border for focused/raised edges.
text: HslaPrimary text. ~17.5:1 on its own background in both appearances.
text_muted: HslaMuted text: timestamps, secondary labels. ~7.5–8:1.
text_faint: HslaFaint text: placeholders, disabled. ~4.5:1 — AA for body copy.
text_dim: HslaOne notch below text_muted — the diff file-path tone. It exists as its
own token rather than being folded into text_muted because the dark
value was sampled (#989898) and folding it would shift that label, which
is a palette change dressed up as a refactor.
solid: HslaThe maximum-contrast solid fill: near-white on dark, near-black on light. This is the primary button plate.
on_solid: HslaLabel/icon color on top of Self::solid — its inverse.
accent: HslaAccent — the emphasis weight for text and icons.
Neutral by default, and deliberately. A component library that ships a hue puts that hue in every app that installs it, and bezel had an indigo running through spinners, pagination, date selection and list markers before anyone chose it. The default is now the same lightness with the chroma at zero.
This is the token to brand:
let mut theme = Theme::for_appearance(appearance);
theme.accent = my_brand_accent(appearance);See set_palette, which is what
makes an override survive an appearance switch.
accent_strong: HslaStronger accent for fills that carry Self::on_accent text. Neutral by
default, it is the maximum-contrast plate — a mid grey would not carry a
label the way the indigo it replaced did.
on_accent: HslaLabel color on top of Self::accent_strong.
danger: HslaDanger — red (errors, stop button).
danger_muted: HslaSofter danger for secondary/inline error copy.
warning: HslaWarning — amber (offline notices, awaiting-input).
warning_muted: HslaSofter warning for secondary copy.
success: HslaSuccess / online — emerald.
busy: HslaWorking / streaming indicator — pink.
success_muted: HslaSofter success for text on a success-tinted chip.
surface_raised_hover: HslaHover tone for an opaque raised pill. Hover must brighten the plate in dark mode, never swap it for a translucent wash (that made pills go see-through — user-reported); in light mode it darkens instead, same idea.
band: HslaRecessed band behind a palette/picker header or footer strip. Translucent so the glass still reads through.
input_bg: HslaThe composer pill and other input plates.
Its own token because “lifted” inverts between appearances. On dark, a faint white wash over near-black reads as raised. The literal light translation — a faint black wash on white — reads as recessed, a dent rather than a plate, which is why the prompt looked like bare text on a smudge. Light mode lifts the way light UIs actually do: pure white, with the border and shadow carrying the elevation.
selection: HslaText-selection highlight in the composer and inputs.
cursor: HslaTerminal block cursor.
caret: HslaText caret — Self::accent’s lightness.
Measured macOS 26, 2026-08-31: NSColor.textInsertionPointColor is the
accent in both appearances, where NSColor.textColor is white and
black. So a caret is not the next glyph before you type it, which is
what this used to carry; the platform gives it its own role, and a brand
tints it here the way the system accent tints it there.
ring: HslaKeyboard focus ring — a hairline, so it marks the control without restating the label inside it.
danger_strong: HslaDestructive-action button fill (danger plate, carries Self::on_accent).
code_text: HslaInline-code text. Neutral: code is already set apart by the mono face and its wash, and a hue on top reads as a link rather than as code.
code_wash: HslaInline-code wash behind Self::code_text.
syntax: SyntaxPaletteShared paint-only syntax palette.
diff_add: HslaDiff: added lines.
diff_del: HslaDiff: deleted lines.
diff_hunk_bg: HslaDiff: hunk-header wash (bluish grey).
vibrancy_alpha: f32How opaque the tint over the blurred window is.
vibrancy: boolWhether the window composites translucent, so the desktop reaches what is painted over it — AppKit’s vibrancy.
glass: boolWhether components paint glass — translucent popovers and cards, and the lens. An opaque window can still carry it.
material: MaterialSpecThe surfaces this theme can paint. Blur belongs to the look, not to the caller: Apple exposes no blur parameter on either family, only the thickness or the variant.
glass_regular: SurfaceSpec§glass_clear: SurfaceSpec§popover_surface: SurfaceStyleWhat the popover surfaces — menus, dialogs, sheets, tooltips — mount on. They take no theme of their own, so this is where the choice lives; a component that owns its surface names its own style instead.
glass_magnify: f32Lens displacement amplitude, signed; negative inverts it.
glass_dispersion: f32Per-channel spread of that displacement — the chromatic fringe.
font_sans: SharedStringUI font family — the name the text system resolves, not the bytes. Point it at your own family once you have registered that font with the text system; a family nothing registered falls through to the fallback below.
font_mono: SharedStringMonospace family for code/terminal.
font_sans_fallback: SharedStringExplicit system fallbacks, for callers that want to skip the lookup.
font_mono_fallback: SharedStringImplementations§
Source§impl Theme
impl Theme
Sourcepub fn branded(brand: &Brand, appearance: Appearance) -> Self
pub fn branded(brand: &Brand, appearance: Appearance) -> Self
The shipped palette for an appearance, rotated onto a brand. What
Theme::install builds, without installing it — for previewing the
appearance you are not currently painting.
Source§impl Theme
impl Theme
Sourcepub fn vibrancy_tint(&self) -> Hsla
pub fn vibrancy_tint(&self) -> Hsla
The frost tint painted over the blurred window background (macOS glass),
at Brand::vibrancy_alpha. Dark: darker than
surface, matched to the reference vibrancy scrim hsl(0 0% 3%).
Light: the material’s own measured tone. Opaque, this IS the surface tone.
Sourcepub fn window_bg(&self) -> Hsla
pub fn window_bg(&self) -> Hsla
The app’s root fill: the frost where glass is on, the opaque panel
where it is not. What a root element paints instead of
Self::bg, so a window that opens blurred is not
then covered over by the paint that made the blur pointless.
Sourcepub fn glass_overlay(&self) -> Hsla
pub fn glass_overlay(&self) -> Hsla
The translucent tint floating cards paint over their backdrop blur
(see bezel::material). Dark: the reference
.glass-surface menu tint verbatim — oklch(0.33 0 0 / 34%). The
previous surface_overlay at 65% was tuned back when the tint had to
approximate the composited recipe without a real blur; kept over the
blur it buried the backdrop’s colour and menus read as flat grey slabs
next to the hue-inheriting chrome (user report). At 34% the blurred
backdrop carries the card and the mid-grey only lifts it off the
plane. Light: heavier — a translucent white tint left menu text
ghosting over whatever sat behind the popover, so light coverage
steps up to keep rows on a known background.
Sourcepub fn input_glass_bg(&self) -> Hsla
pub fn input_glass_bg(&self) -> Hsla
The composer pill / question panel fill. Light’s input_bg is opaque
white (the elevation ladder on an opaque page) — over glass it read as
a solid slab in front of the frosted blur, so it thins to a
translucent tint there (0.6 and then 0.45 both still read too bright
over the 0.80 frost — lowered on user request). Dark’s 3% white wash
is already glass-native.
Sourcepub fn card_glass_bg(&self) -> Hsla
pub fn card_glass_bg(&self) -> Hsla
Section-card fill — the group box, and the in-panel cards built like it.
Each appearance plates in the direction it has room in: dark lifts on a
white wash (../desktop’s --color-card), light lands a near-opaque
white card on the grey frost, at the coverage Self::glass_overlay
already needs to keep rows on a known background. An opaque platform has
no frost beneath the card, so it takes the grey below its white page.
Sourcepub fn window_background_appearance(&self) -> WindowBackgroundAppearance
pub fn window_background_appearance(&self) -> WindowBackgroundAppearance
How the platform should composite the window behind our paint.
This is a method rather than a constant because it has to be re-applied after
every theme swap: gpui’s macOS backend tears the NSVisualEffectView
out of the hierarchy whenever the value is anything but Blurred, and
the re-apply in appearance::apply is what restores vibrancy when the
user switches back to dark. See zed’s crates/zed/src/main.rs, which
runs the same loop on every settings change.
Source§impl Theme
impl Theme
Sourcepub fn install(appearance: Appearance, cx: &mut App)
pub fn install(appearance: Appearance, cx: &mut App)
Install the palette for appearance as the gpui global and point the
context-free paint helpers at it. The only way the appearance should
change — setting the global directly leaves
current_appearance stale.
Which palette that is comes from set_palette, so an app with its own
colours keeps them across a light/dark switch.
Sourcepub fn install_custom(theme: Theme, cx: &mut App)
pub fn install_custom(theme: Theme, cx: &mut App)
Install a palette the caller built: brand colours, one retuned token, or
a wholesale replacement. Theme is a plain struct with public fields, so
the usual shape is Theme::light() with a few fields overwritten.
Use this rather than cx.set_global(theme). The context-free paint
helpers (ink, hairline, wash, …) read current_appearance
and not the global, so a palette installed around this one leaves them
painting for whatever appearance was last installed — light washes over a
dark palette, and nothing to point at.
One-shot: appearance::apply rebuilds the palette whenever the
appearance changes, so what is installed here is replaced on a light/dark
switch. For colours that survive that, register a builder with
set_palette instead.
Source§impl Theme
impl Theme
Sourcepub const VIBRANCY_ALPHA: f32
pub const VIBRANCY_ALPHA: f32
The alpha Brand::vibrancy_alpha starts from.
Matched by eye to a reference Electron app’s dark glass: its scrim is
0.76 over hsl(0 0% 3%), but sits on the under-window vibrancy
MATERIAL, which pre-darkens the blur; a bare backdrop blur has no such
layer, so ours runs heavier to land on the same perceived tone.
Opaque off macOS: Linux and Windows get no compositor-blur guarantee, and a merely transparent window would show raw desktop through the sidebar. An app that knows its compositor sets the brand field anyway.
Sourcepub const HEADER_HEIGHT: f32 = 44.0
pub const HEADER_HEIGHT: f32 = 44.0
Main-panel header height (the reference h-11) — in-card headers (changes pane).
Sourcepub const TITLEBAR_HEIGHT: f32 = 38.0
pub const TITLEBAR_HEIGHT: f32 = 38.0
The unified window titlebar (traffic lights + cluster + tabs). Content
rides Self::TITLEBAR_TOP_PAD lower than center so the air above
matches the perceived gap to the inset card below (border + card body).
Sourcepub const TITLEBAR_TOP_PAD: f32 = 2.0
pub const TITLEBAR_TOP_PAD: f32 = 2.0
Downward shift of titlebar content within the bar.
Sourcepub const TRAFFIC_LIGHT_INSET: f32
pub const TRAFFIC_LIGHT_INSET: f32
Leading room the macOS traffic lights need where AppKit puts them —
zed’s TRAFFIC_LIGHT_PADDING on the macOS 26 SDK (71.0 before it), and
the same 78 ../desktop measured for its Tauri window. An app that
moves the lights with TitlebarOptions::traffic_light_position owns
this number too.
Sourcepub const STATUS_STRIP_HEIGHT: f32 = 24.0
pub const STATUS_STRIP_HEIGHT: f32 = 24.0
Reserved status strip under the content outlet (the reference h-6) — the
WorkingIndicator row; reserving it keeps the composer from shifting.
Sourcepub const TRANSCRIPT_FADE_BAND: f32 = 24.0
pub const TRANSCRIPT_FADE_BAND: f32 = 24.0
Height of the gradient that fades the transcript into the panel background at its bottom edge. The transcript’s last row must pad itself past this band so settled content (message text, the hover-revealed timestamp) never sits inside the fade when scrolled to the bottom.
Sourcepub const BUTTON_HEIGHT: f32 = 24.0
pub const BUTTON_HEIGHT: f32 = 24.0
Button, text field and select-trigger height. Measured 2026-09-01:
NSButton, NSTextField and NSPopUpButton all report 24 at
.regular — Body’s 16pt line box with 4 above and below.
Sourcepub const CONTROL_HEIGHT_SMALL: f32 = 20.0
pub const CONTROL_HEIGHT_SMALL: f32 = 20.0
The same controls at .small.
Sourcepub const BASE_RADIUS: f32 = 8.0
pub const BASE_RADIUS: f32 = 8.0
Button, text field and select-trigger radius — the crate’s most-used
corner after the derived ones, and unnamed until the concentric pass
separated the eight sites that chose 8.0 from the ones that only
arrived at it as 12 − 4.
Every other corner is a ratio of this one, so
Brand::radius moves the whole set together.
Sourcepub const SPACE: f32 = 8.0
pub const SPACE: f32 = 8.0
The gap between siblings. Measured on macOS 26, 2026-08-31:
NSStackView().spacing, visual format’s -, and
constraint(equalToSystemSpacingAfter:multiplier: 1) all report 8.
Carried by ui::stack::row and ui::stack::column, so a call site
that wants the standard gap writes no number at all — SwiftUI’s shape,
where VStack(spacing:) takes the system’s when given nothing.
Sourcepub const CONTENT_MARGIN: f32 = 20.0
pub const CONTENT_MARGIN: f32 = 20.0
The margin from content to its container’s edge. Same measurement,
visual format’s |-.
Sourcepub fn bubble_radius() -> f32
pub fn bubble_radius() -> f32
Message bubble corner radius.
Sourcepub fn surface_radius() -> f32
pub fn surface_radius() -> f32
Floating-surface corner radius — popovers, menus, the command palette, group boxes.
A glass surface paints this on its border and hands the same number
to bezel::ui::surface’s backdrop blur. The two must agree: a blur cut
to a different radius frosts square corners outside a round border, and
it shows only on glass and only at the corners. So the radius is named
once and read at both ends, rather than written twice sixty lines apart
— which is how three independent 12.0s came to exist here.
Sourcepub fn panel_radius() -> f32
pub fn panel_radius() -> f32
Panel / card corner radius.
Button, text field and select-trigger radius.
Sourcepub fn control_radius() -> f32
pub fn control_radius() -> f32
Small control radius (chips, tags, steppers) — a size down from
Self::button_radius, for things that sit inside a control rather
than being one.
Sourcepub const fn inset_radius(outer: f32, inset: f32) -> f32
pub const fn inset_radius(outer: f32, inset: f32) -> f32
The concentric child of a surface: a row inset by inset inside a
container of radius outer keeps its corners parallel to the
container’s, rather than looking pasted onto it.
This is SwiftUI’s ContainerRelativeShape rule done as arithmetic. gpui
has no container shape to inherit at paint time, so the relationship is
stated where the child is defined instead of resolved at runtime —
which means a container that changes its padding carries its rows with
it, and the derived value never becomes a constant of its own.
Source§impl Theme
impl Theme
Sourcepub fn dark() -> Self
pub fn dark() -> Self
Build the dark theme. The surface tones are sampled straight from the
reference screenshots of the original app (docs/reference): main panel
#060606, shell/sidebar #0d0d0d.
Sourcepub fn light() -> Self
pub fn light() -> Self
Build the light theme.
Neutrals are the same oklch scale read from the other end, but the roles are reassigned rather than mirrored (see the module docs): content plane white, chrome grey, raised surfaces white-plus-shadow. Text tones are picked to reproduce the dark theme’s contrast ratios, and accents drop from the 400 to the 600 step at identical hue so they clear WCAG AA on white instead of glowing.
Sourcepub fn for_appearance(appearance: Appearance) -> Self
pub fn for_appearance(appearance: Appearance) -> Self
Build the theme for an appearance.
Trait Implementations§
impl Global for Theme
Auto Trait Implementations§
impl Freeze for Theme
impl RefUnwindSafe for Theme
impl Send for Theme
impl Sync for Theme
impl Unpin for Theme
impl UnsafeUnpin for Theme
impl UnwindSafe for Theme
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> ReadGlobal for Twhere
T: Global,
impl<T> ReadGlobal for Twhere
T: Global,
Source§impl<R, P> ReadPrimitive<R> for P
impl<R, P> ReadPrimitive<R> for P
Source§fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
fn read_from_little_endian(read: &mut R) -> Result<Self, Error>
ReadEndian::read_from_little_endian().