Skip to main content

Crate theme

Crate theme 

Source
Expand description

The app theme — two concrete appearances, one token set.

Colors are precomputed from an oklch-derived neutral scale (perceptually even lightness steps; the same scale the reference Tailwind theme used) into gpui Hsla. Numbers drive layout, colors are paint: layout constants live in theme/layout.rs as plain numbers and never depend on which color is painted.

§Light is designed, not inverted

Mirroring lightness produces the classic “washed-out inverted” look, for three reasons this module handles explicitly:

  1. Surface order flips meaning. In dark, the main content panel is the darkest plane and raised surfaces get lighter. In light, the content panel is white and the shell/sidebar goes grey — chrome recedes by getting darker, not lighter. Popovers stay white and earn separation from a border and shadow rather than from lightness.
  2. Elevation reverses. On dark, a faint white wash means “raised”. Its literal translation — a faint black wash on white — means “recessed”, so the composer read as a dent instead of a plate. Light lifts with white plus a border and shadow (Theme::input_bg, the elevation ladder). Fill alphas carry over unchanged (INK_FILL_SCALE); only hairlines scale, so a 1px edge survives a bright surround (INK_HAIRLINE_SCALE).
  3. Accents must move down the scale. The dark palette’s 400-level accents (red/amber, and whatever hue an app sets Theme::accent to) are chosen for contrast against near-black; on white they fall to 2–4:1 and fail WCAG AA. Light mode uses the 600-level siblings at the same hue, which restores the contrast ratio the dark token had.

Text tones are chosen so each light token lands within ~0.5 of its dark counterpart’s contrast ratio against its own background — the pairing is verified in tests/theme.rs, not eyeballed.

Installed as a gpui Global at boot; read with Theme::of.

Modules§

appearance
Light/dark switching: what the user asked for, what the OS reports, and the plumbing that turns a change in either into a repaint.

Structs§

Brand
What an app changes about the shipped palette without redesigning it.
MaterialSpec
The frost material, before a thickness picks its opacity.
Metrics
One role as it is actually set: a rung on the ladder, the leading it carries, and the weight it is set in.
SurfaceSpec
One surface’s numbers. out = gain * saturated(backdrop) + tint, over a backdrop blurred at blur.
SyntaxPalette
Paint-only syntax colors. The hues follow the Git history graph’s lane palette (indigo, pink, emerald, amber, red, neutral), while light-mode variants are darkened enough to remain readable as text on white.
Theme
The app theme. Two concrete instances — Theme::dark and Theme::light.
Tint
A hue and how much of it, in oklch terms. chroma: 0.0 is the shipped neutral, so Brand::default reproduces the built-in palette exactly.

Enums§

Appearance
Which appearance the app is painting.
ControlSize
A control’s size — SwiftUI’s ControlSize. A closed pair, not a scale: the two that ship are the row of a form and the chip on an overlay.
Glass
The two shipped glasses — SwiftUI’s Glass.regular and Glass.clear. A closed variant rather than knobs: Apple exposes no numbers on glass either, only the variant and a tint.
HighlightKind
The token category a highlighter paints with, mirroring SyntaxPalette field-for-field so classification can name a kind without knowing its color.
Material
SwiftUI’s frost scale. Measured 2026-08-31: the five thicknesses are ONE material at five opacities — the tone implied by tint / (1 - gain) holds to within 9% across the scale, and the sigma does not move at all (23.1/20.2/21.0pt). So this is a knob, not five looks.
SurfaceStyle
Which surface a caller names. Material and glass are different things with different vocabularies — a material has thickness, a glass has a variant — and they meet only at the numbers they resolve to.
TextStyle
A role in the type ladder — SwiftUI’s Font.TextStyle.

Constants§

BASE_COLORS
The greys a UI is built on, as oklch hue and chroma.
INK_FILL_SCALE
Light-mode alpha multiplier for fills (hover/active washes, chip and pill backgrounds).
INK_HAIRLINE_SCALE
Light-mode alpha multiplier for hairlines (borders, dividers, rings). Opposite of fills: a 1px edge has to hold its own against a bright surround, and the dark palette’s white hairlines are deliberately faint. Scaling up keeps separators legible instead of dissolving into the panel.
SCRIM_ALPHA_DARK
Alpha of the standard modal backdrop in dark mode. Call sites that need a heavier or lighter scrim pass their own dark-mode alpha to scrim.

Traits§

Sizing
The size ladder, on anything styled — SwiftUI’s .controlSize(..), and Typeset’s shape for the metrics that come with a type role.
ThemeExt
The carrier seam for catalog traits: any type holding a Theme exposes it through this one method, and every component group (ui::widgets::Scaffolding, …) extends it, so group methods read the environment through self.theme().
Typeset
The ladder, on anything styled.

Functions§

band
Recessed band behind a palette/picker header or footer strip.
base_text_size
The body size in points. TextStyle::Body’s own size paints the measured ladder.
brand
Read the installed brand (the default before one is set).
card_selected_bg
Selected/keyboard-active treatment for rows and chips INSIDE a floating card (menu rows, the picker rail, segmented chips): --color-active again, tone-flipped by the palette convention so a card that is already the bright plane lifts with black at 6% rather than white.
card_selected_shadows
Selection outline for rows and chips INSIDE a floating card (menu rows, the picker rail, segmented chips): the inset ring alone, in both appearances. Card rows fill with a translucent wash (card_selected_bg), and a drop shadow — a filled rect painted BEHIND the element — shows straight through a translucent fill as a grey plate (the same lesson glass_selected_shadows records for dark glass). The card already carries the elevation shadow; selection inside it only needs the edge.
contrast_ratio
WCAG 2.1 contrast ratio between two opaque colors (1.0 … 21.0).
current_appearance
The appearance the context-free paint helpers are painting for.
flatten
Composite fg (which may be translucent) over an opaque bg, returning the opaque result — the color the eye actually receives.
glass_selected_bg
Selected-state glass treatment (tabs, session rows, space rows): ../desktop’s --color-active — white at 12% on dark, black at 6% on light, translucent enough for the vibrancy to read through (heavier flat washes blocked the glass, user request).
glass_selected_shadows
The selected chip’s bright outline, as an INSET shadow: gpui paints inset shadows ON TOP of the background, edges only — a border with zero layout cost. Drop shadows are filled rects painted BEHIND the element, and behind a 5% fill they showed straight through as an opaque dark plate with a greyed ring (user report) — nothing may paint behind a glass chip.
grey
An exact achromatic tone from an 8-bit channel value (grey(13)#0d0d0d) — for surfaces matched against reference-screenshot samples.
hairline
Translucent hairline ink for borders, dividers and rings: white on dark, black on light at INK_HAIRLINE_SCALE of the alpha.
hsl_to_rgb
HSL (gpui convention, all 0..1) → sRGB components 0..1.
ink
Translucent fill ink for interactive states and chip plates: soft-white on dark, soft-black on light at INK_FILL_SCALE of the alpha.
lightness
The oklch lightness of an achromatic tone: neutral inverted.
mix
Linear per-component mix of two colors (paint helper for the gradient spinner).
neutral
A neutral (chroma 0) oklch tone as Hsla. Chroma 0 means r == g == b exactly, so this goes straight to an achromatic Hsla (skipping the hue math avoids float-noise saturation).
oklch
Convert an oklch color (CSS notation: L 0..1, C, H in degrees) to gpui Hsla.
oklch_to_srgb
oklch → sRGB (each 0..1, clamped/gamut-clipped per channel). Reference: Björn Ottosson’s OKLab definition (the same matrices CSS Color 4 uses).
relative_luminance
WCAG 2.1 relative luminance of an opaque color.
rgb_to_hsl
sRGB (0..1 components) → HSL, all components 0..1 (gpui’s Hsla convention).
scrim
Modal backdrop at alpha_dark (quoted, as everywhere, in dark-mode terms).
set_base_text_size
Set the body size in points; every other role keeps its ratio to it, the way every corner is a ratio of Brand::radius.
set_brand
Install a brand and repaint every window with it.
set_current_appearance
Point the context-free paint helpers at an appearance. Called by Theme::install; exposed for tests that build a theme without an App.
set_palette
Teach bezel how this app builds its palette, so light/dark switching rebuilds your colours instead of replacing them with the built-in ones.
surface_shadows
The elevation shadow a floating surface casts — shadow_lg’s shape, which is where it lived until the surface moved into the material. Painted cut to outside the shape, because the same drop shadow under a translucent fill is the grey plate card_selected_shadows records. GlassSpec::shadow says which looks carry one.
theme_generation
Monotonic id of the current palette.
tint
Re-emit an achromatic tone at hue, carrying as much chroma as its lightness can hold. Alpha rides through untouched.
user_bubble_bg
The user message bubble’s plate: the softened wash at 8% dark, 4% light. At the selection weight the bubble read too strong for settled content (user report), and an opaque plate before that read as a solid slab over glass.
wash
Interactive-state wash: a softened ink that stops short of pure black or white so hover plates read as tinted glass rather than paint.