Dashboards use the shared semantic theme renderer only when all of these are true:
- stdout is a terminal
TERMis set and is notdumb- the locale is UTF-8
- the terminal is at least 60 columns wide
NO_COLORis unset
Pipes, redirects, narrow terminals, non-UTF-8 locales, and dumb terminals receive deterministic plain text.
| Setting | Effect |
|---|---|
ZSH_UI_THEME=terminal |
Select the dashboard palette; built-ins are catppuccin-mocha, catppuccin-latte, nord, gruvbox-dark, and terminal |
ZSH_FZF_THEME= |
Inherit ZSH_UI_THEME; set a built-in name or custom for an fzf-only override |
ZSH_FZF_LAYOUT=compact |
Select the compact, roomy, or minimal finder layout |
ZSH_FZF_EXTRA_OPTS= |
Append an intentional final user option layer to shared fzf defaults |
ZSH_UI_GLYPHS=auto |
Select auto, nerd, unicode, or ascii dashboard and finder glyphs independently of color |
ZSH_UI_CUSTOM_COLORS |
Provide all semantic roles as a validated associative array for the custom theme |
NO_COLOR=1 |
Force dashboards to plain output and fzf to its no-color presentation |
NO_NERD_FONT=1 |
Downgrade Nerd Font tiers to ordinary Unicode without touching color |
zhelp --plain |
Force the stable plain help view |
Set theme variables before sourcing init.zsh. terminal is the default. Invalid names and incomplete or malformed custom palettes fall back to it without evaluating input as shell code. The terminal palette prefers terminal-default backgrounds and ANSI accents.
NO_COLOR does not disable fuzzy interaction. It removes repository-managed fzf colors, prevents color-forced file previews, and remains the final option even when inherited or extra options request colors.
| Theme | Background intent | Notes |
|---|---|---|
catppuccin-mocha |
dark | Compatibility palette (former default) |
catppuccin-latte |
light | Explicit light-background choice; background brightness is never guessed |
nord |
dark | Muted cool palette |
gruvbox-dark |
dark | Warm, higher-contrast palette |
terminal |
terminal-owned | Default; uses default terminal foreground/background and named ANSI accents |
The fixed palette values are adapted from the MIT-licensed Catppuccin, Nord, and Gruvbox projects. The resolver emits RGB when COLORTERM is truecolor or 24bit, xterm-256 values when TERM contains 256color, and deterministic ANSI colors otherwise. It uses no terminal query or source-time subprocess.
Glyph selection is independent of palette and color depth. Automatic mode preserves the package and status icons shown by Nerd Fonts in UTF-8 locales; machines without a Nerd Font can opt into ordinary Unicode with NO_NERD_FONT=1 or ZSH_UI_GLYPHS=unicode:
| Mode | Behavior |
|---|---|
auto |
Nerd Font icons in UTF-8 locales; ordinary Unicode when NO_NERD_FONT=1; ASCII outside UTF-8 |
nerd |
Private-use Nerd Font icons plus Unicode structure symbols |
unicode |
Ordinary Unicode only, with no private-use glyphs |
ascii |
ASCII pointers, markers, separators, and status symbols only |
NO_NERD_FONT=1 records that the terminal lacks private-use glyphs and downgrades even an explicit nerd tier to Unicode. Verify the resolved tier and its sample symbols with ztheme current before settling on a mode.
Selection, focus, success, warning, and danger retain text, pointer, marker, label, or status-word cues rather than relying only on color. NO_NERD_FONT affects symbols, not color; NO_COLOR affects repository-managed color, not picker availability. The no-color options explicitly reset the footer to terminal-default foreground to work around fzf 0.68.0’s incomplete no-color theme.
Define a custom palette before sourcing init.zsh, then select it for the shared UI and optionally for fzf:
typeset -gA ZSH_UI_CUSTOM_COLORS=(
base 1e1e2e
surface 313244
selected 45475a
border 6c7086
gutter 1e1e2e
text cdd6f4
muted a6adc8
accent cba6f7
query a6e3a1
match f38ba8
focus f5e0dc
info 89b4fa
success a6e3a1
warning f9e2af
danger f38ba8
)
typeset -g ZSH_UI_THEME=custom
typeset -g ZSH_FZF_THEME=''
All 15 keys are required, extra keys are rejected, and every value must be exactly six hexadecimal digits without #. Validation is atomic: a missing, extra, or malformed value makes startup fall back to the terminal theme rather than partially applying the palette. Theme names and values are data only; the configuration does not evaluate them, source a theme directory, scan the filesystem, or download palettes.
| Layout | Requested frame | Preview at 100+ columns | Preview below 100 columns |
|---|---|---|---|
compact |
fixed 60%, one rounded frame, 0,1 padding |
right 50% |
down 40% |
roomy |
fixed 80%, one rounded frame, 1,2 padding |
right 55% |
down 45% |
minimal |
fixed 45%, one rounded frame, 0,1 padding |
right 45% |
down 35% |
Compact and minimal deliberately use a stable percentage frame rather than shrinking to one- or two-item content. This policy applies to exported defaults for widgets and completions too, and avoids adaptive-height allocation losing rows when a responsive preview moves below the list. The percentages are requests, not exact frame sizes: fzf’s default --min-height=10+ can enlarge them to account for frame controls, and terminal height caps the result. Additional headers, margins, and inherited overrides can change that geometry.
With the default shared frame and a one- or two-item list, the native tests measure these frame heights, both with a below-list preview and with preview-free exported defaults:
| Terminal rows | Compact | Minimal | Roomy |
|---|---|---|---|
| 12 | 12 | 12 | 12 |
| 16 | 14 | 14 | 16 |
| 24 | 14 | 14 | 19 |
These cases retain selectable rows, but a short window can be filled by the frame. There is no visibility guarantee below 12 terminal rows or with arbitrary extra controls and overrides. Enlarge the terminal or hide the preview with Ctrl+P when space is limited. Every profile uses a single rounded outer box. The input and footer share the base finder background instead of drawing filled inner boxes. The input and optional header have one lower divider, the footer has one upper divider, and the list does not draw a nested box. The picker label sits in the outer border and Search sits in the input divider, avoiding the stacked top rules produced by fzf’s full:line height-mode preset.
The initial preview appears below the list through 99 columns and to the right from exactly 100 columns in every layout. While a wide picker is open, fzf can move its preview below the list on resize when its preview-column threshold is crossed (approximately this boundary with the default frame and padding). This affects presentation only. Candidate generation, selected values, and actions do not change. Palette, layout, glyphs, and the secure generated-integration cache are separate: changing presentation refreshes exported options without regenerating fzf --zsh.
fzf options are composed in this order:
- repository-managed structure and semantic palette;
- captured inherited
FZF_DEFAULT_OPTS; ZSH_FZF_EXTRA_OPTSas the intentional final global override;- widget, completion, zoxide, or direct-picker arguments for their own workflow;
- a final
--no-colorwheneverNO_COLORis set.
Inherited widget, general/path/directory completion, and _ZO_FZF_OPTS values are likewise captured once and appended to their managed presentation. Re-sourcing does not duplicate or recapture composed values. Because inherited and explicit layers are intentionally allowed to override managed fzf options, ztheme current reports whether an external option layer exists; it does not attempt to parse arbitrary user option strings.
ztheme inspects or switches the shared dashboard and finder palette without invoking an external program:
ztheme list # list built-ins and mark active/default themes
ztheme current # show theme, layout, glyph tier with a sample, depth, and option layers
ztheme show nord # show semantic role values or terminal swatches
ztheme use nord # switch dashboards and future fzf launches now
ztheme reset # restore the terminal theme now
ztheme export nord # print settings to copy into ~/.zshrc
use and reset affect only the current shell session and refresh future fzf and zoxide picker launches; a picker that is already open is unchanged. use switches the dashboard palette and preserves an explicit ZSH_FZF_THEME fzf-only override, so a deliberate finder choice survives unrelated dashboard switches. reset restores the default terminal theme and clears any such override so fzf inherits the UI theme again. Inspection with show or export does not change the active custom palette. Invalid names, invalid custom palettes, and failed finder refreshes return nonzero while preserving the committed palette, selectors, finder exports, and signatures. Edited ZSH_UI_CUSTOM_COLORS remain a proposal until a switch succeeds.
To persist a built-in choice, copy the output of ztheme export <name> above the source init.zsh line in the machine-local ~/.zshrc. The command prints text only and never edits that file. Exporting a validated custom theme also prints its complete role array in stable order.
dusage, bigfiles, and path sanitize filesystem- or environment-controlled labels before rendering. Named controls such as newline, tab, escape, and bell become visible escapes; other C0, DEL, and C1 bytes use forms such as \x7f. Sanitization happens before measuring or truncating, keeps each value on one logical line, and preserves printable Unicode.
Rich output is for people, not parsers. Pipe a command or use its explicit plain option when output will be consumed by another program.