phux docs

Themes

A theme sets the colours of phux's own chrome: the status bar, the sidebar, pane borders and titles, pickers, and popups. Programs in your panes keep your terminal's colours. phux themes use the colors.toml format of Omarchy, the Arch Linux desktop setup, so any Omarchy theme works as it is.

phux theme install https://github.com/someone/omarchy-nord-theme
phux theme set nord

phux theme set writes the choice into your config file and repaints every attached client.

Installing a theme

phux theme install takes a git URL or a local directory. The directory must hold colors.toml at its root. Nothing in a theme is ever run:

$ phux theme install https://github.com/someone/omarchy-nord-theme
installed nord (Dark) -> `phux theme set nord`

The theme’s name comes from the URL the way Omarchy names it: the last path segment without .git, an omarchy- or phux- prefix, or a -theme suffix. Installing the same name again replaces it. Themes are kept in $XDG_DATA_HOME/phux/themes, or ~/.local/share/phux/themes.

Plugins can ship themes too. An enabled plugin’s themes appear in the list beside the ones you installed; an installed theme wins over a plugin theme with the same name.

$ phux theme list
* nord
  phux-dark (plugin com.phux.demo.theme-pack)
  tokyo-night

* marks the selected theme. phux theme remove NAME deletes an installed theme; to drop a plugin’s theme, disable the plugin.

Selecting a theme

Two keys in [theme] select a whole palette. Set one of them:

[theme]
name = "nord"                     # an installed or plugin theme
# file = "~/.local/state/omarchy/current/theme/colors.toml"

file follows any colors.toml on disk. Pointing it at Omarchy’s current theme, as above, makes phux follow the Omarchy theme switcher; run phux config reload after switching to repaint.

phux theme set NAME and phux theme set --file PATH write these keys for you, remove the other one so exactly one selects, and signal a reload. Without a running server, attached clients pick it up the next time you attach.

phux theme show prints the selected theme’s resolved colours, or a named theme’s with phux theme show NAME, along with the foreground on background contrast ratio. It flags a ratio below 4.5:1, the WCAG AA minimum for body text.

Writing a theme

A theme is a directory with a colors.toml. Each key is a colour as #rrggbb:

mode = "dark"

accent = "#bef264"
selection = "#293628"
muted = "#7c8696"

background = "#0f1218"
lighter_background = "#171b23"

foreground = "#c9d1dc"
dark_foreground = "#9aa4b2"
bright_foreground = "#f4f7fb"

red = "#f87171"
yellow = "#fde047"
green = "#86efac"
cyan = "#67e8f9"
blue = "#7aa2f7"
magenta = "#c4b5fd"

You only need the base colours. phux fills in the rest the way Omarchy does: the bright_* colours and the darker backgrounds are derived when you omit them, legacy keys like bg, fg, and color0 through color15 are understood, and a theme with no accent uses its blue. mode is dark or light; without it, a light.mode file beside colors.toml or a light background makes the theme light.

A theme that leaves out a colour phux can’t derive fails to load, and the error names the missing key:

phux: error: theme /path/to/colors.toml: missing `red` (and nothing to derive it from)

How theme colours map onto the chrome

Theme colourPaints
accentTitles, the active tab, the focused pane’s border and label, finished agents
foregroundRecessive text: hints, section headers, idle agents, unfocused pane labels
bright_foregroundBody text on panels
mutedRules, borders, unfocused pane dividers
selectionThe selected row’s background
green, yellow, redKey chords and working agents; agents that need you; errors
lighter_background (dark) or dark_background (light)The sidebar, status bar, and popup panels

Overriding one colour

Every other key in [theme] overrides one colour slot of the chrome, on top of the selected theme or the shipped palette. Slots set here always win:

[theme]
name = "nord"
accent = "#ff9e64"
surface = "reset"

A colour is a name ("cyan"), hex ("#7aa2f7"), an ANSI palette index ("12"), or "reset" for your terminal’s own default. phux config init writes every slot with its shipped value, commented out.

SlotPaints
accentModal titles, the query caret, the active tab and focused frame
titleSection and tab titles
chordKey chords in help and the which-key popup
actionAction labels; reset uses your terminal’s foreground
dimRecessive text: hints and sub-lines
borderRules and modal borders
section_headerHeadings inside help and pickers
errorError and alarm text
textBody text on a filled panel
surfaceThe sidebar, status bar, and modal fill; reset is transparent
shadowThe drop shadow under floating modals; reset turns it off
selection_fgText of the selected row and the copy-mode strip
selection_bgBackground of the selected row and the copy-mode strip
attention“Needs you”: the asked marker and blocked agents
sidebar_sectionThe sidebar’s section headers
agent_idleAn agent with nothing that needs you
agent_workingAn agent making progress
agent_blockedAn agent waiting on you
agent_doneAn agent that finished
dividerPane borders away from the focused pane
divider_focusThe focused pane’s border (drawn bold)
pane_titleAn unfocused pane’s label on its top border
pane_title_focusThe focused pane’s label (drawn bold)

Status-bar widgets can use the same colours by name: fg = "theme.accent" in a widget style follows the slot. See Status bar and sidebar.

Keep the contrast. Every shipped slot that paints text or a rule clears 4.5:1 against surface, in three steps: rules, then recessive text, then the accent plus bold for focus. Retinting one slot is fine. Dropping a step below that floor makes chrome some people can’t see at all.

Debugging

  • phux theme list shows what is installed and which theme is selected.
  • phux theme show prints the resolved colours and the contrast ratio.
  • phux config show shows what [theme] holds after layering.
  • phux config check flags a slot name phux doesn’t know (with the nearest real slot) and a colour it can’t parse.
$ phux config check
theme.acent: unknown name: unknown theme slot `acent` (did you mean `accent`?); the override is ignored
theme.dim: bad value: `lime` is not a colour (use a name like `cyan`, an index 0-255, or `#rrggbb`); the slot keeps its default
2 problems

phux itself ignores either mistake and the slot keeps its colour, so run the check after editing [theme]. A [theme] name that matches no installed theme makes phux config reload refuse the file.