Repository navigation
feat: custom themes and the theme registry (#82, #138) - #148
Merged
Merged
Conversation
Seven findings from the final whole-branch review: - README.md/README.it.md: attribute each contrast figure to what it was actually measured against — dracula's green vs white, nord's zebra stripe vs the terminal's own dark text on top of it, not both vs white. - internal/themes/resolve.go: TestResolveErrors gained a case with a valid Light and an invalid Dark, so the Dark half of a color pair is no longer unvalidated in the suite's eyes. - internal/themes/themes.go: reworded the package doc's leaf-package clause as the counterfactual it always was, now that internal/config does import internal/themes. - internal/tui/app_test.go: translated the repo's one Italian test failure message to English. - CHANGELOG.md: split the Added entry so an unknown theme and an invalid color are described by the message each actually produces. - README.it.md: fixed a formal-plural slip on the hex-quoting bullet. - internal/tui/theme_test.go: TestBoxAndBorderShareTheirColor now asserts all four border sides instead of only the top one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #138.
Advances #82 — boxes 1 and 3 (load a theme from YAML, ship two built-in themes). #82 stays open on its third box, configurable keybindings, which is a separate tranche: it touches 51 bindings and shares nothing with color but the config file.
Design:
docs/superpowers/specs/2026-08-01-custom-themes-design.md· Plan:docs/superpowers/plans/2026-08-01-custom-themes.mdWhat you can do now
Three built-ins —
default,dracula,nord. A theme of your own names only the tokens it changes; the rest come fromdefault, so the common case is two lines. A value is one color for both backgrounds, or a{light:, dark:}pair.Everything invalid stops startup
Not a fallback — an error, before the TUI opens, naming the theme, the token and the value. The reason is measured: lipgloss never complains. An invalid color renders as no color, with no error and no panic, and
999is not rejected but emitted as\x1b[38;5;999m, which no terminal reads. Nothing downstream would ever tell the user, so this is the only place a typo can be caught.Every theme you define is checked, not only the selected one — a typo in a theme you are not using today is still a typo, and the day you switch is the worst moment to find it.
The dark themes are shipped faithfully, and the READMEs say what that costs
draculaandnordare dark-terminal palettes and are not adapted to a light background. Measured: dracula's green is 1.37:1 against white, and nord's zebra stripe is 2.09:1 against the dark text painted on top of it — a dark stripe under dark text, well under the 3:1 floor. On a light terminal, parts of the UI genuinely cannot be read.defaultremains the adaptive one. Both READMEs carry the numbers rather than a vague warning.The zebra stripe survives a custom theme
v1.9 gave
subtlean explicit 16-color value because the automatic downconvert collapses it onto the background and the stripe vanishes. A user-written theme would have undone that silently. It is now derived: between ANSI 8 and 7 — the only two shades at 16 colors — whichever is perceptually closer, with no threshold constant. That reproduces the two hand-picked values exactly (236→8,254→7), so the default theme renders byte-identically at every profile, and a test pins it.#138: two frames, one weight
The report drew its table frame with
th.Borderand its empty-state box withth.Box, which had no border color — so a report with no hours showed a differently-weighted frame from one with hours.th.Boxnow takes the same color. Exactly two goldens moved, both rendered at truecolor; the fifteen others run undertermenv.Ascii, where the change is byte-identical.Architecture
A new leaf package,
internal/themes, owns the palette, the built-ins, the YAML shape and the validation.internal/configkeeps the raw YAML,internal/cliresolves before the program starts — the last point where a config error still reaches stderr instead of a running TUI — andinternal/tuireceives a palette it did not build.Newgained an argument at 44 call sites.Verification
Every number in the docs and comments was measured by running code, and re-measured independently by a second and often third party. That discipline earned its keep: the final review caught that a contrast figure in both READMEs was attributed to the wrong reference color, that the
darkhalf of every color pair was unvalidated with the whole suite still green, and that a package doc comment asserted an import relationship that had since become false. Nine mutations were run across the branch; eight were caught by existing tests, and the one that was not is now covered.Gate clean at HEAD:
gofmt -l .·go vet ./...·staticcheck ./...·go build ./...·go test ./... -race. Exactly two goldens regenerated, both looked at.🤖 Generated with Claude Code