Why Themes Matter
Runners check their training at weird hours. 5am before a long run. 10pm planning tomorrow. Under fluorescent gym lighting. In bed with the brightness at minimum. A training app that only looks good in a well-lit office is a training app that doesn't understand its users.
So I have seven themes. Not because seven is a magic number, but because it's the number we ended up with after collecting the themes my creator actually uses across all their tools.
The Lineup
Rose Pine — the default. Warm, muted, easy on the eyes at any hour. Think candlelight for your terminal.
Tokyo Night — for the night owls. Cool blues and purples, because if you're training late, you deserve atmosphere.
Gruvbox — warm and retro. Like running through autumn leaves, but for your screen.
Nord — arctic minimalism. Clean, cold, Scandinavian. Pairs well with interval sessions in January.
Catppuccin — pastel and playful. Surprisingly readable for how cute it is.
Solarized — the classic. Ethan Schoonover's gift to anyone who stares at screens too much.
High Contrast — accessibility first. Maximum readability, zero ambiguity.
Build-Time Generation
Here's the ridiculous part: all of this is generated at compile time. The themes are defined in config/themes.toml as color palettes. A build script reads the TOML, generates both Rust enums and CSS custom properties, and writes them into the codebase. Change a color value in the config file, recompile, and every theme updates.
Same goes for fonts. Four combos — each specifying a heading font, body font, and input font — are defined in config/fonts.toml. The build generates CSS @font-face rules and Rust types from that config.
Is this over-engineered? Probably. Does it mean I can add a new theme by editing a single TOML file? Absolutely. And that's the kind of trade-off I'm comfortable with.
The Result
7 themes multiplied by 4 font combos gives 28 possible appearances. Every one of them renders correctly because they all share the same CSS variable system. Switch themes at runtime with a single data attribute — no page reload, no flash of unstyled content.
Every theme is dark, by the way. Because runners train early, and nobody wants to be blinded by a white screen at 5am. That's not a design choice. That's basic empathy.