# Runtime theming
Magi-code uses one Opaline semantic theme for Mission Control. Machine output, protocol output, sessions, provider requests, and tool payloads remain unstyled.
## Theme IDs and precedence
`matrix-green` is the embedded Matrix theme. Opaline built-ins use their registered IDs. Custom files are loaded from `$MC_HOME/themes/*.toml`; their ID is the exact, case-sensitive filename including extension (for example `Solarized.toml` has ID `custom:Solarized.toml`).
Resolution order is: explicit temporary `--theme ID`, saved `interface.appearance.theme`, then Matrix fallback. An invalid explicit theme is a launch error with exit code 2. An invalid saved theme emits a bounded warning and is not rewritten; runtime falls back to Matrix. A successful `/theme` save clears a temporary CLI override; cancel and save failure do not.
## Settings
```json
{
"interface": {
"appearance": { "theme": "matrix-green", "reduced_motion": false },
"no_color": false
}
}
```
Saved `interface.no_color` and `NO_COLOR` control terminal color, followed by terminal detection. Exact detection is truecolor when `COLORTERM` is `truecolor` or `24bit`, ANSI-256 when `TERM` contains `256color`, ANSI-16 for a color terminal, and None when color is disabled. Unicode and animation are separate from color; there is no CLI color override.
During a live run, the prompt border shows a fixed-width ASCII scanner (`[>.....]`) moving back and forth; it yields space to status and cancel labels in narrow panes. Running tool/subagent cards use dot-orbit indicators. Successful live card indicators and the run-completion status receive a brief bold, underlined accent (650 ms); replay, errors, and cancellation do not trigger it. These cues hide behind modals and are disabled by `interface.appearance.reduced_motion`. They never change copied transcript text or session history.
## Complete custom theme example
Save this self-contained parser-valid file as `$MC_HOME/themes/Solarized.toml` (the ID is `custom:Solarized.toml`):
<!-- runtime-theme-example-start -->
```toml
[meta]
name = "Solarized"
author = "example"
variant = "dark"
version = "1.0"
description = "Complete custom theme"
[palette]
background = "#002b36"
panel = "#073642"
code = "#001f27"
highlight = "#0b4f5c"
selection = "#145b68"
primary = "#fdf6e3"
secondary = "#93a1a1"
muted = "#657b83"
dim = "#586e75"
accent = "#2aa198"
accent_secondary = "#268bd2"
accent_tertiary = "#b58900"
accent_deep = "#1b7770"
success = "#859900"
error = "#dc322f"
warning = "#b58900"
info = "#268bd2"
[tokens]
"text.primary" = "primary"
"text.secondary" = "secondary"
"text.muted" = "muted"
"text.dim" = "dim"
"bg.base" = "background"
"bg.panel" = "panel"
"bg.code" = "code"
"bg.highlight" = "highlight"
"bg.selection" = "selection"
"accent.primary" = "accent"
"accent.secondary" = "accent_secondary"
"accent.tertiary" = "accent_tertiary"
"accent.deep" = "accent_deep"
success = "success"
error = "error"
warning = "warning"
info = "info"
"border.focused" = "accent"
"border.unfocused" = "muted"
"code.keyword" = "accent"
"code.function" = "accent_secondary"
"code.string" = "accent_tertiary"
"code.number" = "warning"
"code.comment" = "muted"
"code.type" = "info"
"code.line_number" = "dim"
"magi.surface.background" = "background"
"magi.surface.panel" = "panel"
"magi.surface.panel_alt" = "highlight"
"magi.surface.overlay" = "highlight"
"magi.surface.selection" = "selection"
"magi.surface.code" = "code"
"magi.surface.success" = "highlight"
"magi.surface.pending" = "highlight"
"magi.surface.error" = "highlight"
"magi.text.primary" = "primary"
"magi.text.muted" = "muted"
"magi.text.dim" = "dim"
"magi.text.accent" = "accent"
"magi.text.title" = "accent"
"magi.text.prompt" = "primary"
"magi.text.status" = "secondary"
"magi.text.help" = "secondary"
"magi.text.metadata" = "muted"
"magi.text.command" = "accent_secondary"
"magi.text.command_active" = "accent"
"magi.border.default" = "muted"
"magi.border.active" = "accent"
"magi.border.alternate" = "accent_secondary"
"magi.border.command" = "muted"
"magi.border.command_active" = "accent"
"magi.border.error" = "error"
"magi.status.queued" = "muted"
"magi.status.running" = "warning"
"magi.status.writing" = "warning"
"magi.status.success" = "success"
"magi.status.failed" = "error"
"magi.status.canceled" = "muted"
"magi.status.tool" = "accent_secondary"
"magi.status.subagent" = "accent_tertiary"
"magi.status.assistant" = "accent"
"magi.status.diagnostic" = "info"
"magi.transcript.user" = "accent_secondary"
"magi.transcript.assistant" = "primary"
"magi.transcript.thinking" = "accent_tertiary"
"magi.transcript.tool" = "accent_secondary"
"magi.transcript.session" = "muted"
"magi.transcript.diagnostic" = "info"
"magi.transcript.heading" = "accent"
"magi.transcript.section" = "primary"
"magi.transcript.error_label" = "error"
"magi.transcript.error_body" = "primary"
"magi.diff.inserted" = "success"
"magi.diff.removed" = "error"
"magi.diff.changed" = "warning"
"magi.diff.context" = "primary"
"magi.diff.hunk_header" = "accent_secondary"
"magi.diff.file_header" = "accent"
"magi.diff.metadata" = "muted"
"magi.syntax.plain" = "primary"
"magi.syntax.keyword" = "accent"
"magi.syntax.string" = "accent_tertiary"
"magi.syntax.comment" = "muted"
"magi.syntax.number" = "warning"
"magi.syntax.function" = "accent_secondary"
"magi.syntax.type" = "info"
"magi.syntax.operator" = "accent_secondary"
"magi.syntax.punctuation" = "secondary"
"magi.syntax.code_fence" = "accent"
"magi.syntax.language_label" = "accent_secondary"
"magi.syntax.gutter" = "dim"
"magi.syntax.fallback" = "secondary"
[styles]
keyword = { fg = "accent", bold = true }
line_number = { fg = "dim" }
selected = { fg = "accent", bg = "selection" }
active_selected = { fg = "accent_tertiary", bg = "highlight", bold = true }
focused_border = { fg = "accent" }
unfocused_border = { fg = "muted" }
success_style = { fg = "success" }
error_style = { fg = "error" }
warning_style = { fg = "warning" }
info_style = { fg = "info" }
dimmed = { fg = "dim" }
muted = { fg = "muted" }
inline_code = { fg = "accent_tertiary", bg = "code" }
[gradients]
primary = ["accent", "accent_secondary"]
warm = ["warning", "accent_tertiary"]
success_gradient = ["success", "accent_secondary"]
error_gradient = ["error", "warning"]
aurora = ["accent", "accent_secondary", "accent_tertiary", "info"]
"magi.gradient.fast" = ["accent", "accent_secondary", "accent_tertiary"]
```
<!-- runtime-theme-example-end -->
## Trust and bounds
Only direct regular files in the themes directory are considered. Symlinks, non-regular entries, non-UTF-8 filenames, and files over 512 KiB are rejected. The scan accepts at most 128 custom files and scans at most 256 directory entries. A source is bounded to 512 KiB, with at most 4096 colors, 1024 styles, 256 gradients, 4096 gradient stops, and token-reference depth 64. Diagnostics are limited to 24 entries, sanitized of control characters, and truncated to 240 characters. Invalid siblings do not prevent valid themes from loading.
## Picker behavior
In Mission Control, `/theme` opens the picker. Type to search, use the arrows to select, and preview changes live. Enter starts saving the selected ID. Esc restores the exact opening appearance snapshot, installed with a fresh monotonic presentation revision. A failed save leaves the preview and picker available for retry; closing or canceling restores the opening state. Each open shows the cached catalog immediately and starts a background refresh; valid refreshes update the cache, and deleted or repaired custom files appear on the next refresh.
## Motion and output
`interface.no_color` disables color codes and color values. In human rich output, Opaline modifier-only SGR (such as bold) may remain where a style modifier applies. Plain and machine formats remain unstyled and ANSI-free.
Rich output applies semantic roles to headers, assistant and thinking bodies, tool bodies, metadata, diagnostic levels, Markdown, syntax, and diffs. ANSI is lowered to truecolor, 256-color, or 16-color according to the detected depth. Plain and machine formats are unstyled and ANSI-free. Theme data is display-only and never enters sessions, provider requests, tool arguments/results, or protocol output.
The PRD remains Active and pending final acceptance verification; implementation behavior is documented here: `docs/prd/0079-full-runtime-theming-custom-themes-and-reduced-motion.md`.