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§

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.

Enums§

Appearance
Which appearance the app is painting.
HighlightKind
The token category a highlighter paints with, mirroring SyntaxPalette field-for-field so classification can name a kind without knowing its color.

Constants§

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§

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().

Functions§

band
Recessed band behind a palette/picker header or footer strip.
card_selected_bg
Selected/keyboard-active treatment for rows and chips INSIDE a floating card (menu rows, the picker rail, segmented chips). The card is already the bright plane in light mode, so a white lift can’t read there — selection is the tone-flipped grey wash, at 6% (dark’s 11% read too dark on the bright plane, user report).
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): a TRANSLUCENT wash the vibrancy reads through — heavier flat washes blocked the glass (user request). Dark: the 11% wash. Light: the tone-flipped wash at 6% — 11% black read too dark over the bright frost (user report; light also previously ran a near-opaque white chip, rejected the same way). Same fill as Theme::glass_hover — the ring in glass_selected_shadows is what distinguishes selection. Selection inside floating cards is different — see card_selected_bg.
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.
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_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.
theme_generation
Monotonic id of the current palette.
user_bubble_bg
The user message bubble’s plate: the same translucent wash family as glass_selected_bg, one step softer — 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.