Skip to content

Latest commit

 

History

History
308 lines (222 loc) · 10 KB

File metadata and controls

308 lines (222 loc) · 10 KB

Creating / Modifying a Theme

This guide explains the single-source theme workflow in smplOS.

Goal

Use one file as the source of truth for core theme behavior:

  • colors
  • borders
  • gaps
  • rounding
  • blur
  • opacity
  • app-theme selectors

That file is:

  • src/shared/themes/<theme>/colors.toml

Everything else is either generated from it, or selected by it.


Theme Directory Layout

Each theme lives in:

  • src/shared/themes/<theme-name>/

Core source-of-truth

  • colors.toml (edit this first)

Generated from templates (do not hand-edit)

  • btop.theme
  • dunstrc.theme
  • eww-colors.scss
  • eww-colors.yuck
  • fish.theme
  • foot.ini
  • hyprland.conf
  • hyprlock.conf
  • logseq-custom.css
  • smplos-launcher.rasi

Generated by:

  • src/generate-theme-configs.sh

Optional app preset files

These are app-specific preset payloads used by selectors in colors.toml:

  • neovim.lua
  • vscode.json

Other hand-authored files

  • icons.theme
  • light.mode (optional marker for light themes)
  • tide.theme
  • backgrounds/
  • preview.png

Step-by-Step: Create a New Theme

1) Create folder

mkdir -p src/shared/themes/my-theme

2) Copy a base theme

cp -r src/shared/themes/tokyo-night/* src/shared/themes/my-theme/

3) Edit colors.toml

Every variable describes what the color does, not what it looks like.

Backgrounds

Key Purpose
background Main window/app background
bg_light Raised surfaces -- cards, sidebars, code blocks
bg_lighter Further raised surfaces -- scrollbars, tertiary panels
surface Containers, notification backgrounds, selection areas

Foregrounds

Key Purpose
foreground Primary text
fg_dim Secondary/dimmed text (EWW widget labels, subtle UI text)
fg_alt Alternative foreground tone (terminal ANSI "white" slot)
muted Tertiary text -- comments, inactive items, dividers, chrome
autosuggestion Fish/shell autosuggestion text (defaults to muted if omitted)

Interactive

Key Purpose
accent Primary accent -- links, active borders, highlights
accent_alt Secondary accent -- keywords, alternate highlights
cursor Terminal cursor color
selection_foreground Text color inside selections
selection_background Selection highlight background

Status

Key Purpose
danger Errors, critical alerts, destructive actions
success Success indicators, confirmations
warning Warnings, caution states
info Informational indicators

Bright Variants

Used for terminal bright palette slots, btop gradient midpoints, and link hover states.

Key Source it pairs with
danger_bright danger
success_bright success
warning_bright warning
accent_bright accent
accent_alt_bright accent_alt
info_bright info

Terminal Palette Overrides (optional)

The terminal palette (ANSI slots 0-15) is auto-derived from semantic names:

Slot Default source Override key
0 surface term_0
1 danger term_1
2 success term_2
3 warning term_3
4 accent term_4
5 accent_alt term_5
6 info term_6
7 fg_alt term_7
8 muted term_8
9 danger_bright term_9
10 success_bright term_10
11 warning_bright term_11
12 accent_bright term_12
13 accent_alt_bright term_13
14 info_bright term_14
15 fg_dim term_15

Most themes never need overrides. Use term_N only when a terminal slot should differ from its semantic source (e.g. hackerman's accent is green but terminal "blue" slot should still be blue-ish).

Decoration, Layout, and App Selectors

These are unchanged from before:

  • Decoration: rounding, blur_size, blur_passes, opacity_active, opacity_inactive, term_opacity_active, term_opacity_inactive, popup_opacity, messenger_opacity, browser_opacity
  • Layout: gaps_in, gaps_out, border_size, border_active (optional), border_inactive (optional)
  • App selectors: app_theme_nvim, app_theme_vscode, app_theme_logseq

Example:

app_theme_nvim = "tokyo-night"
app_theme_vscode = "tokyo-night"
app_theme_logseq = "tokyo-night"

4) Generate theme files

cd src
bash generate-theme-configs.sh

5) Apply and test

theme-set my-theme

Step-by-Step: Modify Existing Theme

1) Edit one file

  • src/shared/themes/<theme>/colors.toml

2) Regenerate generated files

cd src
bash generate-theme-configs.sh

3) Apply

theme-set <theme>

How app selectors work

theme-set reads these keys from the active theme's colors.toml:

  • app_theme_nvim
  • app_theme_vscode
  • app_theme_logseq

Then it resolves that theme ID from:

  1. ~/.config/smplos/themes/<id> (user override)
  2. ~/.local/share/smplos/themes/<id> (stock)

So one theme can reuse another theme's app presets without duplicating settings.


What each important file means

  • colors.toml -- canonical theme definition (semantic variable names)
  • hyprland.conf -- generated vars consumed by compositor config
  • eww-colors.scss -- generated EWW token values
  • foot.ini -- generated terminal palette (auto-derived from semantic names)
  • neovim.lua → app preset payload for LazyVim/Lazy.nvim colorscheme
  • vscode.json → app preset payload (theme name + extension/vsix)
  • logseq-custom.css → generated Logseq custom css layer

Opacity keys in colors.toml

Each key controls a distinct window class. Values are never applied twice — no compounding.

Key Default What it affects
opacity_active "1.0" Compositor opacity for all regular apps (focused). Set below 1.0 for a theme-wide frosted-glass look.
opacity_inactive "1.0" Compositor opacity for all regular apps (unfocused). Must equal opacity_active — see CAUTION below.
term_opacity_active "0.85" st-wl background-only alpha (focused). Text is always 100% opaque — only background pixels carry this alpha. Set low (e.g. "0.60") for a heavily frosted glass terminal with perfectly sharp text.
term_opacity_inactive "0.70" st-wl background alpha when the terminal loses focus. Creates a gentle fade on the background, not on text.
browser_opacity "1.0" Brave, Firefox, Chrome, Chromium, etc.
messenger_opacity "0.85" Signal, Telegram, Slack, Discord, Teams, WhatsApp
popup_opacity "0.85" smplOS Rust popup apps (start-menu, notif-center, kb-center, disp-center)

Terminals and the Rust apps own their own alpha channel internally (st ALPHA_PATCH / Slint ARGB surfaces). The compositor passes their pixels through at 1.0 override so values don’t compound. Terminal background transparency is driven entirely by term_opacity_active / term_opacity_inactive — the compositor never touches their surface.

Per-app messenger overrides are available as commented-out lines in windows.conf — uncomment and adjust without touching colors.toml or rebuilding.


⚠️ CAUTION: opacity_inactive must equal opacity_active

Never set opacity_inactive lower than opacity_active. Here is exactly what happens when you do:

Hyprland applies opacity_inactive to every window’s entire composited surface when it loses keyboard focus — pixel-by-pixel, before the window is drawn on screen. This means:

  • All app text dims. The compositor multiplies every pixel by the inactive factor. At opacity_inactive = 0.50 with opacity_active = 0.55 (the old catppuccin values), every unfocused window dropped to half brightness — including text the app rendered as fully opaque.
  • Terminal text dims despite the pixel-level fix. st-wl uses ALPHA_PATCH to draw text cells at alpha 0xFF (fully opaque), correctly separating text from background transparency. But this happens inside the buffer. Hyprland then multiplies the whole buffer by opacity_inactive after composition. Even a perfectly opaque text pixel becomes 1.0 × 0.50 = 0.50 opacity on screen.
  • The terminal’s 1.0 override rule in terminals.conf protects st-wl, but no such override exists for regular GTK/Qt/Electron apps — they have no way to self-protect.
  • term_opacity_active/inactive are safe because they are implemented inside st-wl at the pixel level — background cells get the alpha, text cells stay at 0xFF. They do not interact with compositor opacity at all.

Rule: set opacity_inactive = opacity_active. If you want drama:

# Catppuccin-style: strong frosted glass, zero focus-dimming
opacity_active   = "0.55"
opacity_inactive = "0.55"  # ← same value — no dimming on focus change
blur_size        = "14"
blur_passes      = "3"

The transparency is constant. Windows never dim on focus change. Apps that self-manage their surface (terminals, Rust popups) are exempt regardless.


Rules

  • Edit generated files only for debugging; permanent changes go into:
    • colors.toml
    • template files in src/shared/themes/_templates/
  • Keep colors.toml complete (no missing core keys).
  • Use semantic names that describe what a color does, never generic indices. The terminal palette is auto-derived -- you never need to think about ANSI slot numbers.
  • Prefer selector-based reuse instead of copy-pasting app presets.

Quick troubleshooting

  • Theme applied but old visuals remain:
    • run theme-set <theme> again
    • ensure generated files were regenerated after template changes
  • App theme did not change:
    • verify app_theme_* keys in colors.toml
    • verify selected preset theme has neovim.lua / vscode.json
  • EWW icon colors wrong:
    • theme-set re-bakes SVG icons from icons/status templates