Seven Coats of Paint

7 themes, 4 font combos, all generated at compile time. Because a training app should look good at 5am.

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.