The Tilo brand’s colours, as docs/brand and the website use them.
Identity, not theme: the mark keeps them under every theme, the way an
app icon does.
Finishes: what each surface role is made of, how its edges are drawn
and how it stands off what is below it (docs/architecture/theming.md,
“Material, shape and depth”).
Density metrics: the control heights, hit targets, panel widths and icon
sizes HIG §9.6 fixes so two panels built by two people come out the same
size. Spacing says how far apart things sit; metrics say how big the things
are.
Motion tokens — the one shared timing + spring vocabulary the whole UI
(toolkit widgets AND shell chrome) animates against, so same-class
gestures share a feel.
How the shared components answer the pointer: the hover and press
response, as a theme role (docs/design-language.md, “Lifts”;
docs/architecture/theming.md, the response role).
The selector finish: what a docked strip of selectors (the window
group’s tab strip) stands on, and the lit rim and hover film of the items
in it. A theme sets it in its document (material.selector); every
selector paints through crate::chrome::paint_selector.
A theme’s own surface shader: a short WGSL function a finish names, validated when the theme is
read (docs/architecture/theming.md, “A surface shader”).
The one colour scheme every syntax-highlighting surface paints with: the
code editor widget, block-editor code fences, highlighted text inputs and
any app that renders source (a code editor, a diff, a snippet preview).
The motion a widget may play: the Motion tokens with the person’s
reduced-motion setting applied. Read through crate::Theme::motion;
it is the one place reduced motion is decided, so every widget and the
shell’s chrome follow the same rule (HIG §9.7, design-language “Motion”):
Elevation (docs/design-language.md, “Surfaces”): a window tile casts
one soft shadow that lifts it off the desktop ground; a floating thing
casts two, a tight contact layer that defines its edge and a wide one
that reads as distance. Bars, cards and selected controls cast none: a
selection is a lift (crate::tokens::Intents::selection).
Tuning for direct-manipulation gestures — the numbers that decide how
a dragged surface resists, catches and coasts. Shared for the same
reason the springs are: two controls that resist differently read as
two unrelated machines.
What controls paint with, by intent rather than by hue
(docs/design-language.md, “Colour roles”). Nothing outside these roles
gets colour: the accent is for primary actions, links, the focus ring,
the caret, the text selection and the selected row’s tint; the on state
is for a switch, a checked box or radio, an active tile, a “connected”
dot. Hover, resize grips, window edges, icons at rest, sliders and
progress stay neutral.
What is drawn straight onto media (video, a camera viewfinder, a photo):
the letterbox around a frame, the fade under text that sits on the
picture, and that text. Media is dark or light regardless of the theme,
so these are the same in every theme; only the hue of the neutral
material follows it. Controls over media are a floating Panel on the
material instead, inked from the theme.
One colour per participant: PRESENCE_HUES at one OKLCH lightness and
chroma, so they differ in hue only and no participant looks more
important than another. Where sRGB cannot carry the full chroma at a hue
(a light cyan) only that hue’s chroma drops; lightness never does.
How selectors are finished. Selection itself (which state lifts how
far) is not the theme’s to change: the active item is always one surface
step of its ground; this only says what the strip is made of, how the
light catches an active item and how much of the lift a hover shows.
Palette used by terminal-style widgets. Exposes default fg/bg and a 16-slot
ANSI base palette. Higher-index colors (256/truecolor) are out of scope —
consumers still derive those from the 16 anchors or from their own mapping.
A tinted pill (a status badge, a presence chip), resolved against the
ground it paints on. Every field is opaque rather than a token plus
alpha — measuring the token instead of the composite is how a chip ends
up as tinted ink on a tinted wash.
Which hue the accent takes: primary actions, the selected row’s lift
tint, links, the focus ring, the caret and the text selection
(docs/design-language.md, “Colour roles”). An “on” state is never the
accent; it is the on-state green (crate::tokens::Intents::on_state).
What started a transition. Motion follows the input: a shortcut is a
frequent expert action with a known result, so animating it adds latency
to every repetition, while pointer and finger motion explains where
things went.
A reading face the system ships, chosen by name in a theme’s type role or
by a reader in an app that sets long-form text (Books). Each is set
through the Reading role’s family, so its optical sizes, features and
script fallbacks work like the default’s.
How two neighbours relate, which decides how far apart they sit
(HIG §7b). Layout containers take one (Column::with_rhythm,
Row::with_rhythm) and resolve it against the theme they are laid out
with, so the gap follows density, ui_scale and a custom spacing scale.
A named step on the Spacing scale, for APIs that resolve a gap at
layout time from the Theme they are handed (Spacer::token) rather than
taking an unscaled pixel value from the caller.
The body size, in logical pixels at 1×, that every role is designed
against. Role sizes are body × role_px / REFERENCE_BODY_PX, so they land
on whole pixels at the stock size.
The presence hue nearest to colour’s, for a colour that arrives from
outside (a host’s accent, a roster’s packed colour). A grey has no hue
and gives None.