Themes and Appearance

Colour themes, custom and imported themes, chrome colours, glyph sets, spacing, dimming and the spotlight.

Four things decide what TUIOS looks like:

PartWhat it controlsHow to set it
ColourThe terminal colours, the accents, the bordersappearance.theme
ShapeThe characters the chrome is drawn with: borders, controls, rules, rail marksappearance.glyphs
SpacingEmpty ground between panes, padding inside dialogsappearance.gap, appearance.panel_padding
TextWhat a window title, a workspace tab and the clock showwindow_title_format, dock_workspace_tab_format, clock_format

Every option on this page can be changed while TUIOS runs, from the settings page (, in window mode), with tuios set-config, or by editing config.toml, which is reloaded when saved.

Try it live: the Make it yours track lets you try themes and glyphs in your browser.

Choosing a theme

TUIOS ships over 300 themes. A theme sets the 16 ANSI colours plus foreground, background and cursor, and TUIOS derives its own interface colours from them.

[appearance]
theme = "catppuccin_mocha"
# Change it in the running session
tuios set-config appearance.theme catppuccin_mocha

# Start with a theme, overriding the config for this run
tuios --theme dracula

# Find one
tuios list-themes --filter gruvbox
tuios --list-themes | fzf --preview 'tuios --preview-theme {}'

Theme ids use underscores: tokyo_night, not tokyo-night.

In the app, open the picker from the palette (Theme picker) or from the Theme row on the settings page. It is searchable, shows a swatch per theme, and applies the selection live as you move. Esc puts back the theme you started with.

An empty theme turns theming off. TUIOS then emits plain colour indices and your terminal's own palette decides what they look like.

Checking a theme

tuios list-themes <id> prints a theme's colours with the contrast each one measures against the theme's background. It tells you whether a palette is readable before you look at it.

tuios list-themes catppuccin_latte
tuios list-themes catppuccin_latte --json | jq -r '.palette.illegible[]'
tuios list-themes --json | jq -r .active

Importing a theme

tuios import-theme converts a kitty, ghostty, alacritty or wezterm colour scheme into a TUIOS theme. The format is read from the file's content, so the extension does not matter.

tuios import-theme ~/.config/kitty/current-theme.conf --name mine
tuios set-config appearance.theme mine

A scheme that sets only some colours imports those, and the rest fall back to the xterm defaults. Wezterm's TOML schemes are read. Its Lua schemes are not.

Writing a theme

A theme is a JSON file in ~/.config/tuios/themes/ (following $XDG_CONFIG_HOME). The file name without .json, lowercased, is its id unless the file sets id.

{
  "id": "my-theme",
  "display_name": "My Theme",
  "fg": "#e0def4",
  "bg": "#191724",
  "cursor": "#e0def4",
  "black": "#26233a",
  "red": "#eb6f92",
  "green": "#31748f",
  "yellow": "#f6c177",
  "blue": "#9ccfd8",
  "purple": "#c4a7e7",
  "cyan": "#ebbcba",
  "white": "#e0def4",
  "bright_black": "#6e6a86",
  "bright_red": "#eb6f92",
  "bright_green": "#31748f",
  "bright_yellow": "#f6c177",
  "bright_blue": "#9ccfd8",
  "bright_purple": "#c4a7e7",
  "bright_cyan": "#ebbcba",
  "bright_white": "#e0def4"
}
  • The colour names are purple and bright_purple, not magenta.
  • A colour is a hex string or an object: {"r": 255, "g": 0, "b": 0, "a": 255}.
  • Every field is optional. fg defaults to #e5e5e5, bg to #000000, cursor to fg, the eight normal colours to the xterm defaults, and each bright_* to its normal colour. Set the bright colours yourself, or bright text looks the same as normal text.
  • The directory is read again when you select a theme or run list-themes, so a file you just wrote can be selected at once. Subdirectories are not read.
  • A file that does not parse is skipped. tuios list-themes reports it under problems with the reason.

Chrome colours

The 16 ANSI colours are what programs in your panes paint with. By default TUIOS also takes its own accents from them: the logo and the selected row from bright_blue, the terminal-mode border from bright_green, and so on. A chrome object sets those accents directly, without changing what programs see:

{
  "id": "amber",
  "chrome": {
    "accent": "#ffb454",
    "accent_bright": "#ffd580",
    "success": "#aad94c",
    "warning": "#ffb454",
    "error": "#ff3333",
    "info": "#59c2ff",
    "surface": "#2b2118"
  }
}
FieldWhat it coloursTaken from when absent
accentLogo, selected row, window-mode pillbright_blue
accent_brightSecondary accent, focused border in window modebright_cyan
successTerminal-mode pill, focused border in terminal mode, success messagesbright_green
warningCopy-mode pill, warningsyellow
errorError messagesred
infoInfo messagesblue
surfaceThe fill of every dialog: palette, pickers, menus, which-keyA fixed dark grey, or on a light theme a step below its background
canvas, panel, cardThe darker and lighter steps around surfaceDerived from surface

Dialogs sit on a fixed grey ramp so they stay readable over any pane content. On a light theme the ramp is built from the theme's background instead, so a dialog is a light panel rather than a dark slab, and the screen behind it fades toward the background rather than toward black. The accent and key colours are darkened at their own hue until they read as text on it (4.5:1), and the status colours until they read as marks (3:1).

surface moves the ramp, and TUIOS picks text colours for it by contrast, so a light surface gets dark text. Text colours cannot be set directly. A field that is not a valid hex colour is ignored on its own.

Border colours

appearance.border_focused_color and appearance.border_unfocused_color override the theme's border colours with a hex value. They live in config.toml, not in the theme. Clear one to hand the border back to the theme.

Colour depth

The chrome is designed for each colour depth a terminal can have, rather than drawn in truecolor and stepped down. TUIOS reads the depth the way other programs do, from TERM, COLORTERM and NO_COLOR. There is no setting for it.

DepthDetected whenWhat the chrome does
truecolorCOLORTERM=truecolor or 24bit, or a terminal known to have it (kitty, Ghostty, WezTerm, Alacritty, foot)The ramp described above
256TERM ends in 256color and COLORTERM is unset: Apple Terminal, mosh, tmux without TcThe greys sit on the xterm grey ramp (232 to 255), so no panel or selected row turns navy. Colours step to the nearest palette entry in OKLab, and a saturated colour never lands on a grey
16TERM=xterm, TERM=linux, or NO_COLOR setNo grounds are painted. Panels use your terminal's background and frame themselves with a line, the selected row and title chips are reverse video, and colours are your palette's slots

At 16 colours a theme's own colours give way to your terminal's palette slots, so set the terminal to the same theme to see them. The review diff is drawn per depth too: tinted lines in truecolor, fixed palette entries at 256 colours, and coloured line numbers with bold, underlined changed words at 16.

mosh passes 24-bit colour but not COLORTERM, so over mosh TUIOS sees 256 colours. If your terminal has truecolor, export COLORTERM=truecolor on the far machine.

Your terminal's colours

With no theme and no pane background, TUIOS asks each client's terminal for its background, foreground and 16 colours, and answers a program in a pane that asks for them (OSC 10, 11 and 4) with those. The chrome is measured against your terminal's background, so a light terminal gets the light ramp. A local client follows your system's light and dark switch as it happens. A client over SSH or in a browser asks once, when it attaches. A terminal that answers nothing, such as mosh, gets plain black and white; set a theme or appearance.pane_background there.

Glyph sets

A glyph set picks the shapes the chrome is drawn with. Four ship: default, unicode (no Nerd Font glyphs), heavy and ascii.

tuios set-config appearance.glyphs heavy
tuios set-config appearance.border_style glyphs
tuios list-glyphs heavy

With no set configured and no --ascii-only, TUIOS picks one from the environment: a locale that is not UTF-8 (C, POSIX, ISO-8859-1, read from the first of LC_ALL, LC_CTYPE and LANG that is set) gets ASCII, and the Linux console (TERM=linux) gets unicode. tuios list-glyphs with no argument says which set that is and why.

border_style = "glyphs" is what makes the border follow the set. The settings page has a searchable Glyph set picker with a live preview. Writing your own set, the roles it can name and the width rules are covered in Configuration.

Spacing and text

tuios set-config appearance.gap 2                 # cells between tiled panes, 0 to 8
tuios set-config appearance.panel_padding 4       # columns inside dialogs, 1 to 6
tuios set-config appearance.clock_format "Mon 3:04PM"
tuios set-config appearance.window_title_format "{index}: {title}"
  • window_title_format accepts {title}, {index} and {cwd}.
  • dock_workspace_tab_format accepts {index} and {name}.
  • clock_format is a Go time layout, such as 15:04 or Mon 3:04PM.

Quieting what you are not using

KeyDefaultMeaning
appearance.dim_unfocused0Fade panes you are not in, as a percent from 0 to 90. 0 is off
appearance.zen_mode"disabled"Hide pane borders: disabled, always, or mouse (hidden while the mouse is still)
appearance.border_style"rounded"rounded, normal, thick, double, block, outer-half-block, inner-half-block, ascii, hidden or glyphs

dim_unfocused dims pane content only. Borders, title bars, the rail and the dock stay as they are. Without a theme it can only dim cells a program coloured itself, because a cell in the terminal's default colour has no colour TUIOS knows. Set a theme first.

Selection and search colours

[appearance.selection] holds the colours a pane marks text with. They are settings rather than part of the theme, because TUIOS chooses them and the program in the pane does not.

KeyDefaultMeaning
bg / fg#45475A / emptySelected text. An empty fg keeps the text's own colour
boldfalseAlso draw selected text bold
search_bg / search_fg#8A6D2F / #F5E7C8Every search match
match_bg / match_fg#E5A93D / #1C1B19The match under the cursor
cursor_bg / cursor_fg#39C5CF / #08222BThe copy mode cursor
flashtrueSweep a band of light over text you just copied. The sweep follows appearance.motion: basic and full draw it, none turns it off
flash_ms420How long the sweep takes, 80 to 3000
flash_style"diagonal"diagonal, diagonal-reverse, horizontal or vertical
flash_coloremptyThe colour of the sweep

The scrollback browser uses the same search and selection colours.

Spotlight

The spotlight lights one area of the screen and dims the rest. It is meant for recordings and demos. Press B in window management mode, Ctrl+B B in any mode, or run Toggle spotlight from the palette. The key was b in earlier versions.

While the spotlight is on, the dock shows a Spotlight chip. Click it to turn the spotlight off. Esc in window mode also turns it off. In terminal mode, Esc goes to the program in the pane, so use Ctrl+B B. See Spotlight.

KeyDefaultMeaning
spotlight.enabledfalseStart with the spotlight on
spotlight.follow"mouse"Follow the mouse or the focused pane's cursor
spotlight.radius10Half the beam's height, in rows
spotlight.dim75Percent of its light an unlit cell loses
spotlight.edge"hard"hard cuts the beam off, soft fades it
spotlight.shakefalseShake the mouse left and right to toggle the beam

The spotlight belongs to one client. Another client attached to the same session sees its own screen unchanged. Over SSH or in the browser, follow = "cursor" and edge = "hard" send far fewer bytes.

On this page