Thirty-one themes, one set of variables
13 June 2026
Themes became resources. List one in data/app.json and its palette ships with the app; there are thirty-one of them now.
The trick is that there is nothing to theme. A theme is a :root block setting the same handful of variables every time: $bg, $surface, $elevated, $text, $muted, $border, $primary, $on-primary, $focus, plus $success, $warn and $error. Application CSS never names a colour, only a variable, so a palette is genuinely swappable rather than a set of overrides fighting the defaults.
.card {
background: $surface
border: 1px solid $border
color: $text
}
That is the whole contract. Write CSS this way and every theme works, including ones written after your app.
Because a theme compiles to a stylesheet of its own, switching one at runtime is a link swap rather than a rebuild. This site does exactly that: the picker in the corner returns an apply() that removes the current theme stylesheet and adds another, with the choice remembered per page kind, so the guide can stay dark while the homepage stays green.
Contrast was the part that needed real attention rather than taste. Every palette has to keep text legible on both the surface and the primary colour, which is why $on-primary exists as its own variable instead of being assumed to be white.
Engine 1.0. See the CSS chapter.