# Theming

> TUI appearance: /theme, pager.toml theme keys, color-support detection, screen modes (fullscreen/minimal), and terminal capability constraints.

- Repository: xai-org/grok-build
- GitHub: https://github.com/xai-org/grok-build
- Human docs: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458
- Complete Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/llms-full.txt

## Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/06-theming.md`
- `crates/codegen/xai-grok-pager/src/settings`
- `crates/codegen/xai-grok-pager-render/src`
- `crates/codegen/xai-grok-pager/src/diagnostics.rs`
- `crates/codegen/xai-grok-pager/docs/user-guide/21-terminal-support.md`
- `crates/codegen/xai-grok-pager/docs/user-guide/05-configuration.md`

---

---
title: "Theming"
description: "TUI appearance: /theme, pager.toml theme keys, color-support detection, screen modes (fullscreen/minimal), and terminal capability constraints."
---

Grok Build paints the interactive TUI from a central `Theme` palette in the pager render crate. Theme selection is stored under `[ui]` in `~/.grok/config.toml`, layout and chrome knobs live in `~/.grok/pager.toml`, and at startup every RGB slot is quantized to the terminal’s detected color level (`truecolor` / `256` / `basic` / `none`). Minimal mode bypasses that palette entirely and locks a terminal-native ANSI set so chrome stays readable on any light or dark profile.

## Built-in themes

| Theme | Canonical name | Aliases | Truecolor required |
|-------|----------------|---------|--------------------|
| Grok Night | `groknight` | `grok-night`, `dark` | No |
| Grok Day | `grokday` | `grok-day`, `light`, `day` | No |
| Tokyo Night | `tokyonight` | `tokyo-night`, `tokyo` | Yes |
| Rose Pine Moon | `rosepine-moon` | `rosepine`, `rose-pine`, `rose-pine-moon` | Yes |
| Oscura Midnight | `oscura-midnight` | `oscura` | Yes |
| Auto (meta) | `auto` | `system` | No (resolved to a concrete theme) |

Names are case-insensitive. Default when `[ui].theme` is unset is **Grok Night** (`groknight`).

Truecolor-only themes are hidden from the `/theme` picker on non-truecolor terminals. Applying one by name still works: the live visual is clamped (typically to Grok Night) so the UI does not render a broken palette.

## Switch themes

### `/theme` (alias `/t`)

| Input | Behavior |
|-------|----------|
| `/theme` | Cycle to the next theme in `ThemeKind::available()` for this terminal |
| `/theme tokyonight` | Set a named theme (aliases accepted; stored as the canonical name) |
| `/theme auto` | Enable system-appearance mode |

Arrowing through the picker previews colors in real time without writing disk; Enter commits (`Action::SetTheme`), Escape restores the previous kind. `/theme` is **not available in minimal mode**.

### `config.toml`

```toml
[ui]
theme = "tokyonight"
auto_dark_theme = "tokyonight"   # used only when theme = "auto"
auto_light_theme = "grokday"
compact_mode = false
screen_mode = "fullscreen"       # sticky: "minimal" | "fullscreen"
```

### Settings pane

`/settings` (alias `/config`) → **Appearance** exposes Theme, Auto dark theme, Auto light theme, and Compact mode. Theme rows are hidden in minimal mode (`hidden_in_minimal: true`); compact mode remains available.

## Auto theme (system appearance)

With `theme = "auto"` (or `system`):

| Appearance | Default concrete theme | Override key |
|------------|------------------------|--------------|
| Dark | `groknight` | `[ui].auto_dark_theme` |
| Light | `grokday` | `[ui].auto_light_theme` |

Detection order:

1. Desktop APIs (`dark-light`): macOS `AppleInterfaceStyle`, Linux XDG portal `org.freedesktop.appearance.color-scheme`, Windows personalization registry  
2. **Startup only:** OSC 11 terminal background query (SSH / headless when desktop APIs return unspecified)  
3. No change if both fail  

While running, appearance is polled every **5 seconds** (desktop APIs only — not OSC 11). Live switches re-resolve the concrete theme and recolor the TUI without a restart. `auto_dark_theme` / `auto_light_theme` reject the value `auto` (circular) and fall back to their defaults.

## Color support detection

Color level is detected once and cached:

| Level (`as_str`) | Meaning |
|------------------|---------|
| `truecolor` | 24-bit RGB (SGR 38;2) |
| `256` | Indexed palette (SGR 38;5) |
| `basic` | 16-color ANSI |
| `none` | Monochrome (`NO_COLOR` set, or no color capability) |

**Detection inputs:**

- `NO_COLOR` → force `none`  
- `supports-color` on stdout (`COLORTERM`, `TERM`, terminal-specific env)  
- Non-TTY / piped stdout without `NO_COLOR` → assume `truecolor` (safe for tests)  
- If the crate reports only 256-color but the brand is known truecolor (iTerm2, Ghostty, Kitty, WezTerm, Alacritty, Rio, Warp, VS Code, Windows Terminal, foot) or native Windows ConHost → upgrade to `truecolor` (covers tmux/SSH stripping `COLORTERM`)

Every theme is defined in full RGB. `Theme::current()` quantizes all slots to the detected level; runtime colors (syntax highlight, blends) go through the same `quantize` path. On `basic` (and legacy Windows ConHost below truecolor), chrome gets extra ANSI-16 overrides so dark RGB does not collapse onto a single black slot.

**Enable truecolor (common case):**

```bash
export COLORTERM=truecolor
```

```tmux
# ~/.tmux.conf
set -g default-terminal "tmux-256color"
set -as terminal-features ",*:RGB"
```

Verify inside Grok with `/terminal-setup` (aliases `/terminal-check`, `/terminal-info`): rows `color` and `themes` mirror the detector and `ThemeKind::available()`. Limited color produces an issues note there (and is not spammed on the welcome banner). Apple Terminal is inherently 256-color — truecolor themes stay unavailable.

## Screen modes

Two orthogonal controls: **sticky render mode** (fullscreen TUI vs minimal) and **alt-screen policy** (fullscreen buffer vs inline in the main scrollback).

### Sticky mode: fullscreen vs minimal

| Surface | Fullscreen TUI | Minimal (scrollback-native) |
|---------|----------------|-----------------------------|
| CLI | `grok` / `grok --fullscreen` | `grok --minimal` |
| Slash | `/fullscreen` (alias `/full`) | `/minimal` |
| Config | `[ui] screen_mode = "fullscreen"` | `[ui] screen_mode = "minimal"` |

`/minimal` and `/fullscreen` re-exec the same session with `--resume <id>` and the chosen flag, and write the sticky `[ui].screen_mode`. A CLI flag wins for that invocation and updates the sticky value. Unset `screen_mode` keeps legacy resolution: pager.toml `[terminal] minimal`, then alt-screen policy. `screen_mode` takes precedence over the legacy `[terminal] minimal` key.

**Minimal mode and theming:**

- Locks a fixed terminal-native palette (`Theme::terminal_default()`): body uses `Color::Reset` (terminal default fg/bg); sparse named ANSI-16 accents  
- Ignores `[ui].theme` / `/theme` / theme rows in `/settings` for live paint  
- Avoids dark-on-dark / light-on-light when OS appearance disagrees with the terminal profile  

### Alt-screen policy (fullscreen vs inline)

In `~/.grok/pager.toml`:

```toml
[terminal]
alt_screen = "auto"    # "auto" | "always" | "never"
```

| Value | Behavior |
|-------|----------|
| `auto` (default) | Alt-screen in plain terminals and normal tmux; **inline** in Zellij and tmux control mode |
| `always` | Always enter the alternate screen |
| `never` | Always inline in the main scrollback |

CLI override: `--no-alt-screen` always forces inline (highest precedence). Sticky `screen_mode = "fullscreen"` still respects this policy — Zellij/control-mode keep their automatic inline fallback unless you set `always`.

```text
                    ┌─────────────────────────┐
  --minimal / --fullscreen / [ui].screen_mode │
                    └───────────┬─────────────┘
                                │
              ┌─────────────────┴─────────────────┐
              ▼                                   ▼
        Minimal mode                      Fullscreen TUI path
   terminal_default palette          ThemeKind + color quantize
   /theme unavailable                /theme + auto appearance
                                              │
                                   [terminal].alt_screen
                                   / --no-alt-screen
                                              │
                              ┌───────────────┼───────────────┐
                              ▼               ▼               ▼
                           alt-screen      inline         auto:
                           buffer       main scrollback   Zellij / tmux-CC
                                                          → inline
```

## Compact mode

`/compact-mode` toggles denser layout (less outer and prompt padding). Persisted as `[ui].compact_mode` in `config.toml` (not in `pager.toml`). Auto-enabled while the terminal is **20 rows or shorter**.

## Cursor color

On theme apply, the pager sets the terminal cursor to the theme’s `accent_user` via **OSC 12** (`\x1b]12;rgb:RR/GG/BB\x07`). On exit it restores the profile default with **OSC 112**. Under `NO_COLOR`, OSC 12 is skipped. Minimal’s terminal-native palette does not drive a custom cursor color the same way.

## Syntax highlighting

Bundled TextMate themes (embedded in the binary; not user-replaceable):

| Active theme kind | `.tmTheme` asset |
|-------------------|------------------|
| GrokNight, RosePineMoon, OscuraMidnight, Auto (fallback path) | `grok-night.tmTheme` |
| TokyoNight | `tokyo-night.tmTheme` |
| GrokDay | `grok-day.tmTheme` |

Highlight colors are quantized through the same color-support pipeline as UI chrome.

## `pager.toml` appearance keys

Path: `~/.grok/pager.toml` (under `$GROK_HOME` when set). Overrides layout, scroll, block chrome, animation, and terminal policy. Theme **name** is not set here — use `[ui].theme` in `config.toml`. Specify only overrides; defaults apply for omitted keys.

### Terminal

```toml
[terminal]
alt_screen = "auto"
# minimal = false              # legacy; prefer [ui].screen_mode
# minimal_live_rows = 10
# minimal_max_commit_rows = 2000
```

### Layout, scrollbar, scroll, display

```toml
[scrollback.layout]
outer_vpad = 1
outer_hpad_left = 2
outer_hpad_right = 2
block_pad_left = 2
block_pad_right = 2

[scrollback.scrollbar]
enabled = true
gap_left = 0
gap_right = 0
# scrollbar_bg = "none"
# scrollbar_fg = "none"

[scrollback.scroll]
margin = 0
min_page_fraction = 0
follow_indicator = "center"   # "center" | "none"
follow_auto_select = true
follow_by_overscroll = true
anchor_on_fold = true
respect_manual_folds = false  # opt-in pin for manual folds while streaming

[scrollback.display]
sticky_headers = true
tab_width = 4
expandable_indicator = true
expandable_indicator_char = "›"
collapsed_accent_char = "❙"
dim_accent = 0.5
line_under_last_entry = false
selection_buttons = false
```

### Animation, prompt, todos

```toml
[animation]
fps = 30          # 1–60
wave_rows = 32

[prompt]
collapse_unfocused = true
mouse_hover = true
show_prefix = true

[todo]
badge_format = "default"   # "default" | "colon" | "comma"
```

### Block styling (representative)

```toml
[scrollback.blocks.edit]
indent = true
vpad = false
# expanded_by_default = true   # unset → follows [ui].collapsed_edit_blocks
hunk_separator = "…"
dual_line_numbers = false

[scrollback.blocks.thinking]
accent_enabled = true
animate = true
truncate_lines = 3
bg_blend = 70
header = true
header_bright = false

[scrollback.blocks.tool]
muted_collapsed = true
dim_details = true
bullet = "diamond"   # none | dot | small-circle | circle | small-triangle | triangle | diamond

[scrollback.blocks.execute]
first_lines = 2
last_lines = 3
accent_enabled = true
header_style = "label"   # "shell" | "label"
muted_command_collapsed = true

[scrollback.blocks.prompt]
vpad = true
bg = "light"             # "none" | "light" | "dark"
show_prefix = true
min_lines = 2
```

Optional top-level `disable_plugins = true` hides plugin/hooks UI chrome.

## Theme color slots

Each concrete theme fills a fixed set of roles consumed by scrollback blocks, chrome, markdown, and diffs. The theme system owns quantization; callers should not hard-code RGB outside `Theme`.

| Group | Slots |
|-------|--------|
| Backgrounds | `bg_base`, `bg_light`, `bg_dark`, `bg_highlight`, `bg_hover`, `bg_terminal`, `bg_visual` |
| Accents | `accent_user`, `accent_assistant`, `accent_thinking`, `accent_tool`, `accent_system`, `accent_error`, `accent_success`, `accent_running`, `accent_skill`, `accent_plan`, `accent_verify`, `accent_feedback`, `accent_remember`, `accent_model` |
| Text / gray | `text_primary`, `text_secondary`, `gray_dim`, `gray`, `gray_bright` |
| Semantic | `command`, `path`, `running`, `warning`, `fuzzy_accent` |
| Borders / scrollbar | `selection_border`, `hover_border`, `prompt_border`, `prompt_border_active`, `scrollbar_bg`, `scrollbar_fg` |
| Paste | `paste_bg`, `paste_fg`, `paste_dim` |
| Diff | `diff_delete_bg`, `diff_delete_fg`, `diff_insert_bg`, `diff_insert_fg`, `diff_equal_fg`, `diff_gutter_fg` |
| Markdown | `md_heading_h1`–`md_heading_h6`, `md_code`, `md_code_bg`, `md_text`, `md_muted`, `md_task_checked`, `md_task_unchecked`, `link_fg` |

## Theme resolution flow

```mermaid
flowchart TD
  subgraph config [Config surfaces]
    CT["~/.grok/config.toml [ui].theme"]
    AD["auto_dark_theme / auto_light_theme"]
    PT["~/.grok/pager.toml layout / alt_screen"]
  end

  subgraph runtime [Runtime]
    TK["ThemeKind parse + aliases"]
    SA["System appearance poll 5s"]
    AV["ThemeKind::available()"]
    CL["color_support detect OnceLock"]
    Q["Theme::quantized + runtime quantize"]
    TN["terminal_native lock → terminal_default"]
  end

  CT --> TK
  AD --> SA
  TK -->|auto| SA
  SA --> TK
  TK --> AV
  AV -->|truecolor filter| Q
  CL --> Q
  TN -->|minimal| Q
  PT --> UI[Scrollback / animation / alt-screen paint]
  Q --> UI
```

## Diagnostics and constraints

| Signal | What to check |
|--------|----------------|
| `/terminal-setup` `color` row | `truecolor` / `256` / `basic` / `none` |
| `/terminal-setup` `themes` row | `all` or `2/5: groknight, grokday` (example when truecolor missing) |
| Washed-out / wrong colors | `COLORTERM`, tmux `RGB` / `default-terminal` |
| Truecolor themes missing from picker | Expected below truecolor; Apple Terminal cannot unlock them |
| Fullscreen not entering | Zellij, tmux control mode, `alt_screen = "never"`, or `--no-alt-screen` |
| Theme slash ignored | Minimal mode — switch with `/fullscreen` first |
| Monochrome UI | `NO_COLOR` set |

Broader terminal capability issues (clipboard OSC 52, mouse, Kitty keyboard) are covered on the troubleshooting page; theming-specific color notes live in `/terminal-setup`’s limited-color warnings.

## Config key summary

| Key | File | Default / notes |
|-----|------|-----------------|
| `[ui].theme` | `config.toml` | unset → `groknight`; `auto` / concrete name |
| `[ui].auto_dark_theme` | `config.toml` | unset → `groknight` |
| `[ui].auto_light_theme` | `config.toml` | unset → `grokday` |
| `[ui].compact_mode` | `config.toml` | `false`; auto on ≤20 rows |
| `[ui].screen_mode` | `config.toml` | unset = legacy; sticky `"minimal"` \| `"fullscreen"` |
| `[terminal].alt_screen` | `pager.toml` | `"auto"` |
| `[terminal].minimal` | `pager.toml` | legacy; prefer `screen_mode` |
| `NO_COLOR` | env | disables color |
| `COLORTERM=truecolor` | env | advertises 24-bit RGB |
| `--minimal` / `--fullscreen` | CLI | sticky mode + relaunch |
| `--no-alt-screen` | CLI | force inline |

## Related pages

<CardGroup>
  <Card title="Configure Grok" href="/configure-grok">
    config.toml / pager.toml precedence, feature flags, and `grok inspect`.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema-oriented keys for `[ui]`, pager sections, and env counterparts.
  </Card>
  <Card title="Slash commands" href="/slash-commands">
    `/theme`, `/settings`, `/compact-mode`, `/minimal`, `/fullscreen`, `/terminal-setup`.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Truecolor, tmux, SSH, OSC 52, and other terminal capability failures.
  </Card>
  <Card title="Keyboard shortcuts" href="/keyboard-shortcuts">
    Focus, cancel, and navigation that interact with the themed TUI chrome.
  </Card>
  <Card title="Quickstart" href="/quickstart">
    First interactive session and reading tool output in scrollback.
  </Card>
</CardGroup>
