Shipping 23 themes and 23 app icons in a weekend
The asset pipeline, and why the icon matters more than the theme.
Themes were meant to be a Saturday afternoon job. They took the weekend, and the interesting part was not the colours.
The setup
The app already used semantic tokens rather than raw colours: background, panel, text, muted, line, accent, and a ladder of accent opacities from 8% to 85%. Nothing in the interface refers to a hex value. That decision, made early for boring reasons, is why 23 themes was a weekend rather than a month.
A theme is therefore one record: a name, a background, an accent, and the derived ladder. Midnight, Graphite, Matcha, Cobalt, Amber, Rosewood, Paper, Terminal, Slate, Plum, Sand, Nord, and eleven more. The derivation is code, so a new theme is about forty seconds of work once the two anchor colours are chosen.
The trap was contrast. Generating the opacity ladder mechanically produces a Terminal theme where the dim text is invisible and a Paper theme where the accent is too pale to read against white. Two of the 23 needed hand tuning on four tokens each, and Paper needed its own inverted ladder because darkening a near-black accent behaves nothing like lightening a bright one.
The pipeline
Three scripts, none of them clever.
The first reads the theme table and emits a CSS custom property block per theme. Output is checked into the repository rather than generated at build time, because a diff you can read is worth more than a build step you have to trust.
The second renders a contrast report: every text token against every background token, with the ratio and a pass or fail. Two themes failed on the first run. The report is now part of the check that runs before a release.
The third renders the icon set.
Why the icon took longer than the themes
An app icon is not a theme swatch. It is 1024 by 1024, it has to read at 60 pixels on a Home Screen, and it is the only part of the app other people see.
The mark is a five by five dot matrix, the same grid the whole product is built from. At full size it is obvious. At 60 pixels, three of the first designs turned into a grey smudge, because a dot grid with gaps loses its structure the moment the gaps drop below a pixel.
What survived: fewer dots, larger dots, and more contrast between the accent and the background than looks right at full size. The version that reads best on a phone looks slightly crude on a website, and that is the correct trade, because nobody chooses an app from a 1024 pixel render.
Four concepts made it through, and the reasoning is on the App Store assets page: a diagonal for progress, a caret for the developer-tool reading, a cropped heat block for the strongest silhouette, and the wordmark glyph inverted for dark Home Screens.
The thing I got wrong
I assumed the theme was the feature and the icon was decoration.
The messages after the release said otherwise. People wrote about the icon. Which one they picked, whether it matched their dock, whether the Terminal green clashed with their wallpaper. Almost nobody mentioned the theme inside the app, and the ones who did mentioned it because the icon they picked did not match it.
That makes sense in hindsight. You see the icon every time you pick up your phone. You see the theme for the eleven seconds a day you are logging habits. The icon is the part of the app that lives on your Home Screen, and the Home Screen is a thing people arrange deliberately.
So the ordering changed: icons ship with the theme, matched by name, and picking a theme offers the matching icon in the same sheet. Alternate icons are a two line entry in the app configuration and one asset each, which makes them one of the cheapest features per unit of user affection I have shipped.
Numbers
Twenty three themes, 23 matching icons, two weekend days, one contrast script that now runs on every release, and 4.1 MB added to the bundle. The icon assets are 3.4 MB of that.
Fourteen hours of actual work, of which roughly nine went to the icons and two to the contrast failures. The colour table itself, the part that looked like the feature, took under an hour once the token layer existed.
Eight themes are in the free tier. The rest come with Pro, which is 25 € a year or 34,99 € once, and that split exists because the app has no ads and no data resale and something has to pay for the weekend.
What I would do differently
Build the contrast report before the themes rather than after. I wrote 23 themes and then discovered two of them were unreadable, which is the wrong order and cost an hour of re-tuning.
And design the icon at 60 pixels first, then scale up. Every mark that survives that constraint looks fine large. The reverse is not true, and I found that out three designs in.