Skip to content

Support transparent backgrounds in themes - #97

Open
brettbash wants to merge 1 commit into
gammons:mainfrom
brettbash:feat/transparent-backgrounds
Open

Support transparent backgrounds in themes#97
brettbash wants to merge 1 commit into
gammons:mainfrom
brettbash:feat/transparent-backgrounds

Conversation

@brettbash

@brettbash brettbash commented Jul 13, 2026

Copy link
Copy Markdown

Summary

  • Theme colors now accept "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.
  • Transparent values resolve to a nil color. lipgloss already skips styling for nil, but slk has a few hand-rolled color conversions that would otherwise render literal black (since NoColor.RGBA() reports opaque black). Those are guarded:
    • message-pane background fills emit the ANSI default-background reset (SGR 49) and foregrounds emit SGR 39
    • derived tints (compose insert background, selection rows) stay transparent when mixed against a transparent background
    • Block Kit attachment color bars fall back to a neutral gray

Usage

[theme]
background = "transparent"

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.go and tint_test.go; full suite passes with -race.

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 gammons left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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."

@gammons

gammons commented Jul 23, 2026

Copy link
Copy Markdown
Owner

hi @brettbash see my comments. the first issue is really the one that should be addressed before this can be merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants