Support transparent backgrounds in themes - #97
Conversation
Theme colors now accept "transparent", "none", or "default", letting terminal emulators with background opacity (Ghostty, kitty, etc.) show the wallpaper through slk's panels. Transparent values resolve to a nil color, which lipgloss renders as "no color". The hand-rolled color conversions are guarded so nothing paints literal black for nil: message-pane background fills emit the ANSI default-background reset (SGR 49), foregrounds emit SGR 39, derived tints (compose insert bg, selection rows) stay transparent when mixed against a transparent background, and attachment color bars fall back to a neutral gray. Fully backwards compatible: existing hex/ANSI theme values are unchanged, and no built-in theme uses the new keywords. 💘 Generated with Crush Assisted-by: Crush:claude-fable-5
gammons
left a comment
There was a problem hiding this comment.
Overall
Solid, well-scoped change. The core insight — that nil (not lipgloss.NoColor{}) is the correct sentinel because NoColor.RGBA() reports opaque black — is correct and clearly documented. Backwards compatibility is preserved (guarded if overrides.X != "" calls, no built-in theme uses the keywords), and the tests cover case-insensitivity plus both nil directions in mixColors.
I verified the PR's central claim that every hand-rolled color conversion is guarded by grepping all .RGBA() call sites: the only theme-color consumers are tint.go, render.go, and blockkit/color.go (the halfblock.go one operates on image pixels, not theme colors), and all three are now nil-guarded. Coverage is complete. Build and the styles/messages/blockkit suites pass locally.
Issues worth addressing
1. (Design) Selection highlight and compose-insert background silently vanish on transparent themes
When Background is transparent, mixColors returns nil for any derived tint:
SelectionTintColor(tint.go:39,44) → the selected-message row gets no background → the "which row is selected" affordance disappears.ComposeInsertBG(styles.go:382) → insert-mode compose box loses its background cue.
This may be an acceptable tradeoff, but it's a real UX regression for exactly the users this feature targets. Consider whether these derived indicators should fall back to a solid color (e.g. Accent) rather than going transparent, so selection stays visible. At minimum this should be a conscious, documented decision. This is the one item I'd want an answer on before merge.
2. (Low) nil sentinel collision in SelectionTintColor
selectionBgFocused/selectionBgUnfocused use nil to mean "not computed yet" (tint.go:38,43). With a transparent Background, mixColors legitimately returns nil, so the cache never populates and mixColors re-runs on every call. Harmless today (the bg == nil branch returns early), but it defeats the caching and is a latent footgun. A separate computed bool flag would be cleaner.
3. (Low) "default" as a synonym for transparent is ambiguous
transparent/none clearly signal "no color," but a user reading background = "default" in TOML could reasonably read it as "the theme's default color" rather than "the terminal's default background." Consider dropping "default" or documenting it explicitly.
4. (Low, docs) wiki/Configuration.md isn't updated
The theme section documents color values but doesn't mention the new transparent/none/default keywords, so the feature is undiscoverable outside the PR body. Worth a one-line addition. (There's no color validation in config.go, so nothing to update there.)
5. (Nit) Magic constant in colorString
The #333333 fallback (blockkit/color.go:50) duplicates the default Border value. A named const (e.g. subduedAttachmentBar) would document intent.
Verdict
Approvable. None of the above are blockers, but #1 is worth a quick answer before merge — it's the difference between "transparent works" and "transparent works but you can't see what row you've selected."
|
hi @brettbash see my comments. the first issue is really the one that should be addressed before this can be merged. |
Summary
"transparent","none", or"default"so terminal emulators with background opacity (Ghostty, kitty, alacritty, etc.) can show the wallpaper through slk's UI instead of painting opaque panels.NoColor.RGBA()reports opaque black). Those are guarded:Usage
Compatibility
Fully backwards compatible — existing hex/ANSI values parse exactly as before, and no built-in theme uses the new keywords. Covered by new tests in
styles_test.goandtint_test.go; full suite passes with-race.