Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Themes

The TUI is colored by semantic roles, not raw hexes. Every render site resolves a role (accent, text_primary, selection_bg, …) through the active theme, so one switch restyles the whole interface at runtime. A theme is one file, 43 keys, done.

Switching themes

CommandWhat it does
/themeOpens the interactive picker: ↑/↓ (or j/k) move the cursor and preview live — the whole UI recolors under the arrow keys. Enter applies and persists, Esc (or q) cancels and reverts to whatever was active when the picker opened.
/theme listLists built-in themes and user presets, then re-scans ~/.opencrabs/themes/. Files that fail validation are listed with a human-readable rejection reason — never silently ignored.
/theme set <name>Applies a theme immediately and writes it to your config. Lookup is case-insensitive across built-ins and user presets; an unknown name tells you to run /theme list.
/theme resetReturns to the default (crab-dark) and clears the config key.

To pin a theme without the picker, set it in config.toml:

[tui]
theme = "nord"

The value is applied at boot before the first frame. An empty or unknown name falls back to crab-dark. User theme files are re-scanned on every /theme invocation, so editing a preset file hot-loads it without restarting the TUI.

Built-in themes

There are 39 built-in themes: 8 hand-tuned presets plus a curated pack of 31 converted from popular upstream palettes (see Imported themes). crab-dark is the default and matches the historical OpenCrabs look.

PresetVibe
crab-darkThe house palette: burnt-orange accent, cool grays, deep ink
draculaThe classic purple-blue dark theme with orange accent
alucardWarm dark, muted contrast (Castlevania’s good-boy Dracula)
monokaiSublime’s iconic warm dark with pink/yellow
catppuccin-mochaPastel dark, soft and low-contrast
catppuccin-latteThe same pastels on light surfaces
solarized-lightEthan Schoonlif’s light base
solarized-darkThe original dark base

The curated pack adds: aura, ayu, carbonfox, catppuccin-frappe, catppuccin-macchiato, cobalt2, cursor, everforest, flexoki, github, gruvbox, gruvbox-light, kanagawa, material, matrix, mercury, nightowl, nord, one-dark, opencode, orng, osaka-jade, palenight, rosepine, rosepine-dawn, synthwave84, tokyonight, tokyonight-storm, vercel, vesper, zenburn.

Swatches show four anchor roles per theme (accent, body text, success, ink background), read from the shipped sources. Regenerate with python3 docs/scripts/gen_theme_gallery.py.

ThemeTypeaccenttextsuccessinkSource
crab-darkdefault #d76414 #c8c8d2 #50c878 #14141ebuilt-in
draculahand-built #ffb86c #f8f8f2 #50fa7b #282a36built-in
alucardhand-built #a34d14 #1f1f1f #14710a #1f1f1fbuilt-in
monokaihand-built #fd971f #f8f8f2 #a6e22e #272822built-in
catppuccin-mochahand-built #fab387 #cdd6f4 #a6e3a1 #1e1e2ebuilt-in
catppuccin-lattehand-built #fe640b #4c4f69 #40a02b #4c4f69built-in
solarized-lighthand-built #cb4b16 #657b83 #859900 #073642built-in
solarized-darkhand-built #cb4b16 #839496 #859900 #002b36built-in
auracurated pack #a277ff #edecee #61ffca #0f0f0fopencode, dark, 2026-09-11
ayucurated pack #e6b450 #bfbdb6 #7fd962 #0b0e14opencode, dark, 2026-09-11
carbonfoxcurated pack #ff7eb6 #f2f4f8 #25be6a #161616opencode, dark, 2026-09-11
catppuccin-macchiatocurated pack #f5bde6 #cad3f5 #a6da95 #24273aopencode, dark, 2026-09-11
cobalt2curated pack #2affdf #ffffff #9eff80 #193549opencode, dark, 2026-09-11
cursorcurated pack #88c0d0 #e4e4e4 #3fa266 #181818opencode, dark, 2026-09-11
everforestcurated pack #d699b6 #d3c6aa #a7c080 #2d353bopencode, dark, 2026-09-11
flexokicurated pack #8b7ec8 #cecdc3 #879a39 #100f0fopencode, dark, 2026-09-11
githubcurated pack #39c5cf #c9d1d9 #3fb950 #0d1117opencode, dark, 2026-09-11
gruvboxcurated pack #8ec07c #ebdbb2 #b8bb26 #282828opencode, dark, 2026-09-11
kanagawacurated pack #d27e99 #dcd7ba #98bb6c #1f1f28opencode, dark, 2026-09-11
materialcurated pack #89ddff #eeffff #c3e88d #263238opencode, dark, 2026-09-11
matrixcurated pack #c770ff #62ff94 #62ff94 #0a0e0aopencode, dark, 2026-09-11
mercurycurated pack #8da4f5 #dddde5 #77c599 #171721opencode, dark, 2026-09-11
nightowlcurated pack #c792ea #d6deeb #c5e478 #011627opencode, dark, 2026-09-11
nordcurated pack #8fbcbb #eceff4 #a3be8c #2e3440opencode, dark, 2026-09-11
one-darkcurated pack #56b6c2 #abb2bf #98c379 #282c34opencode, dark, 2026-09-11
opencodecurated pack #9d7cd8 #eeeeee #7fd88f #0a0a0aopencode, dark, 2026-09-11
orngcurated pack #fff7f1 #eeeeee #6ba1e6 #0a0a0aopencode, dark, 2026-09-11
osaka-jadecurated pack #549e6a #c1c497 #549e6a #111c18opencode, dark, 2026-09-11
palenightcurated pack #89ddff #a6accd #c3e88d #292d3eopencode, dark, 2026-09-11
rosepinecurated pack #ebbcba #e0def4 #31748f #191724opencode, dark, 2026-09-11
synthwave84curated pack #b084eb #ffffff #72f1b8 #262335opencode, dark, 2026-09-11
tokyonightcurated pack #ff966c #c8d3f5 #c3e88d #1a1b26opencode, dark, 2026-09-11
vercelcurated pack #8e4ec6 #ededed #46a758 #000000opencode, dark, 2026-09-11
vespercurated pack #ffc799 #ffffff #99ffe4 #101010opencode, dark, 2026-09-11
zenburncurated pack #93e0e3 #dcdccc #7f9f7f #3f3f3fopencode, dark, 2026-09-11
catppuccin-frappecurated pack #e5c890 #c6d0f5 #a6d189 #303446alacritty-theme, 2026-09-11
gruvbox-lightcurated pack #b57614 #3c3836 #98971a #fbf1c7alacritty-theme, 2026-09-11
rosepine-dawncurated pack #d38d2f #575279 #286983 #faf4edalacritty-theme, 2026-09-11
tokyonight-stormcurated pack #ff9e64 #a9b1d6 #9ece6a #24283balacritty-theme, 2026-09-11

The 43 roles

A user theme is a flat TOML file mapping every role to a #RRGGBB hex. Role names double as the TOML keys. What each one colors:

GroupKeyColors
AccentsaccentPrimary emphasis: prompts, active states, the crab orange
accent_tealPrimary action / selected accent
accent_softPassive, informational accent (soft white)
Texttext_primaryBody text
text_secondarySecondary text
text_mutedMuted labels
text_dimDeepest text tier
Chrome graysgrayBorders, passive labels, info lines (the workhorse)
gray_midSecondary borders, quiet panel text
gray_detailDetail rows under primary rows (tool list)
gray_dimInactive borders (unfocused input frame)
gray_darkDeeper inactive chrome
gray_baseDarkest neutral foreground (plan-widget borders)
gray_lightBright neutral text
gray_softSoft label text
gray_mutedMuted body text (plan widget)
Positivesuccess“ok” states, active pane borders
analytics_greenMission-control bars and values
green_checkChecked-markdown checkboxes
NegativeerrorPrimary failures
error_softError titles
error_fadedError bodies
WarningwarningAmber input hints
warning_mutedMuted amber
amber_mutedProposed-skill inbox badges
Teal tiersteal_vividSelection/active in input + tools
teal_brightDialogs
teal_mutedCompleted/skipped plan tasks
teal_calmPlan progress bars
Blue tiersblue_slateHelp text, input placeholders
blue_steelInformational accents (sessions, files, projects)
blue_linkMarkdown links
blue_skyProjects
blue_softChat accents
blue_vividOnboarding voice brand accent
blue_codeInline code (markdown)
Surfacesselection_bgSelected text in the composer
surface_panelMission-control panel background
surface_qrQR panel background (onboarding)
surface_codeCode-block background
surface_code_altCode-block background, alternate shade
InkinkThe main background canvas
Miscpurple_softProposed-brain-dedup inbox badges

Decorative animation cycles (like the rotating project badge colors) are deliberately not roles: they stay preset-agnostic.

Writing your own theme

Drop a file at ~/.opencrabs/themes/<name>.toml. The file stem is the theme name. All 43 keys are required; here is a complete valid theme (nord, from the curated pack):

accent = "#8fbcbb"
accent_teal = "#8fbcbb"
accent_soft = "#eceff4"
text_primary = "#eceff4"
text_secondary = "#8b95a7"
text_muted = "#767b84"
text_dim = "#9ca0a8"
gray = "#434c5e"
gray_mid = "#9ca0a8"
gray_detail = "#91959e"
gray_dim = "#7e838c"
gray_dark = "#6b707a"
gray_base = "#434c5e"
gray_light = "#d0d3d9"
gray_soft = "#afb3ba"
gray_muted = "#8d929a"
success = "#a3be8c"
analytics_green = "#a3be8c"
green_check = "#a3be8c"
error = "#bf616a"
error_soft = "#bf616a"
error_faded = "#6f4853"
warning = "#d08770"
warning_muted = "#9f6e62"
amber_muted = "#87625a"
teal_vivid = "#8fbcbb"
teal_bright = "#8fbcbb"
teal_muted = "#6d8c90"
teal_calm = "#5a7177"
blue_slate = "#698f9e"
blue_steel = "#88c0d0"
blue_link = "#81a1c1"
blue_sky = "#88c0d0"
blue_soft = "#648896"
blue_vivid = "#88c0d0"
blue_code = "#88c0d0"
selection_bg = "#505660"
surface_panel = "#3b4252"
surface_qr = "#343a45"
surface_code = "#434c5e"
surface_code_alt = "#494e59"
ink = "#2e3440"
purple_soft = "#81a1c1"

Validation rules

Validation is loud, never silent. A theme file is rejected, with the reason shown by /theme list, when it:

  1. misses any of the 43 roles — the error names the missing key;
  2. carries an unknown key — strict typo guard, there is no partial-schema mode;
  3. uses a malformed value — only #RRGGBB or RRGGBB, case-insensitive;
  4. fails the contrast floor — eight reading-critical (foreground, background) pairs must hit 2.5:1 WCAG contrast: text_primary/text_secondary/accent/success/error/ warning on ink, blue_code on surface_code, and gray_mid on surface_panel. Deliberately dim tiers (text_muted, text_dim, …) are excluded — dim is their job;
  5. collides with a built-in name — user files can never shadow a shipped preset.

Everything else is free for the runtime: each valid theme gets its ANSI-256 fallback tier derived automatically, so terminals without truecolor still render it (nearest index in the 6×6×6 cube, grayscale ramp for neutrals).

Theme enforcement (v0.5.3)

Every widget in the TUI now resolves its colours through the theme engine — the last hardcoded ANSI sites are gone, and a suite-level guard fails the build if a new raw ANSI colour site appears (#1634). Themes can also declare an optional canvas background (the source background carries through catalog conversion), so a theme can restyle the whole terminal canvas, not just the widgets on it.

Imported themes

The 31-theme curated pack was generated by the in-tree converter (tui::theme_catalog) from two upstream formats:

  • alacritty-theme TOML ([colors.primary] background/foreground plus the 16 ANSI colors) — the alacritty/alacritty-theme catalog, 176 themes;
  • opencode theme JSON (defs + theme token maps with dark/light variants, hex strings, ANSI integers, defs references and "none") — the sst/opencode built-in themes, 33 assets.

Both convert through a fixed 43-role mapping (semantic token first, ANSI color or deterministic bg/fg mix as fallback), and every generated file is pre-certified by the same validator user themes face — a converted file that doesn’t validate never ships. Each pack file carries a # source: provenance header naming the upstream repo, file, and fetch date; the gallery table shows it. Upstream palettes belong to their authors; the pack imports their colors, not their names’ goodwill.

The converter is generation-time tooling: the TUI itself only knows the embedded presets and your drop-in files.