Themes
Choose a theme from the terminal UI by pressing Ctrl+P, selecting Open settings, and opening Theme. You can also
set it directly in your global CLI config:
{
"theme": {
"name": "tokyonight",
"mode": "system"
}
}Themes belong to the terminal client. Put theme selection in cli.json, not opencode.json. See
CLI settings for the config location and other terminal settings.
Modes
Use system to follow your terminal’s appearance, or lock the theme to one mode:
{
"theme": {
"name": "opencode",
"mode": "dark"
}
}| Value | Behavior |
|---|---|
system | Follows the light or dark mode of the terminal. |
dark | Always uses the dark theme. |
light | Always uses the light theme. |
If a custom theme provides only one mode, OpenCode uses that mode when the other is requested.
Builtins
Select a built-in theme by its name:
{
"theme": {
"name": "catppuccin"
}
}Available themes include:
aura,ayu,carbonfox,cobalt2,cursor,dracula,everforest,flexokicatppuccin,catppuccin-frappe,catppuccin-macchiatogithub,gruvbox,kanagawa,material,matrix,mercury,monokai,nightowl,nordone-dark,opencode,orng,lucent-orng,osaka-jade,palenight,rosepinesolarized,synthwave84,tokyonight,vercel,vesper,zenburn
The system theme is available when OpenCode can read your terminal palette.
Files
To create a global theme named ocean, use the theme tool to generate a complete file in the global theme directory.
Its relevant structure looks like this:
{
"base": {
"text": {
"base": "#d8f3ff"
}
},
"dark": {
"hue": {
"accent": "$hue.cyan"
}
}
}Then select ocean in settings or set theme.name to ocean in cli.json. The filename, without .json, is the
theme name.
| Scope | Directory |
|---|---|
| Global | ~/.config/opencode/themes/ |
| Project | .opencode/themes/ in the project tree |
OpenCode reads .json files only. A theme closer to the current directory replaces a global or parent theme with the
same name.
Format
Every V2 theme declares one complete base token tree and at least one light or dark hue palette.
Modes inherit only from that file’s base; they do not inherit from the built-in OpenCode theme or from each other. Use
the theme tool to generate a complete base before customizing it.
The examples below show only the sections relevant to each concept; they are not complete theme files.
{
"base": {
"text": {
"base": "#16324f"
}
},
"light": {
"hue": {}
},
"dark": {
"hue": {},
"text": { "base": "#d8f3ff" }
}
}Colors
Use a hex color, transparent, or a reference to another theme color:
{
"base": {
"text": {
"base": "#d8f3ff",
"muted": "$hue.neutral.400"
},
"background": {
"base": "transparent"
}
}
}Hex values may use 3, 4, 6, or 8 digits. References begin with $; hue references use the form
$hue.<name>.<step>.
Hues
Override a hue alias to change a family of related colors at once:
{
"base": {
"categorical": ["accent", "purple", "green"]
},
"dark": {
"hue": {
"accent": "$hue.cyan",
"interactive": "$hue.blue"
}
}
}| Kind | Values |
|---|---|
| Base | gray, red, orange, yellow, green, cyan, blue, purple |
| Alias | accent, interactive, neutral |
| Step | 100, 200, 300, 400, 500, 600, 700, 800, 900 |
Hue steps follow the mode’s contrast direction: light themes run from dark at 100 to light at 900, while dark
themes run from light at 100 to dark at 900.
categorical sets the ordered hues used to distinguish agents and other repeated items. It must contain at least one
base hue or alias.
States
Use state keys to change interactive colors. Unspecified states use the nearest base value:
{
"base": {
"text": {
"action": {
"primary": {
"base": "$hue.interactive.400",
"$hovered": "$hue.interactive.300",
"$disabled": "$hue.neutral.600"
}
}
}
}
}| Group | Values |
|---|---|
| Actions | primary, secondary, destructive |
| States | $hovered, $focused, $pressed, $selected, $disabled |
Form fields use the same state keys directly under text.formfield or background.formfield.
Tokens
The complete base contains every token group. A mode may override any part of it after providing its complete hue
palette:
{
"base": {
"border": {
"base": "$hue.neutral.700"
},
"syntax": {
"comment": "$hue.neutral.500",
"keyword": "$hue.accent.400"
},
"diff": {
"text": {
"added": "$hue.green.400",
"removed": "$hue.red.400"
}
}
}
}| Group | Purpose |
|---|---|
text | Base, muted, action, form-field, and feedback text |
background | Base, surfaces, actions, form fields, and feedback fills |
border | Borders |
scrollbar | Scrollbars |
diff | Added, removed, context, highlight, and line-number colors |
syntax | Source-code highlighting |
markdown | Markdown elements |
Syntax keys are comment, keyword, function, variable, string, number, type, operator, and
punctuation. Markdown keys are text, heading, link, linkText, code, blockQuote, emphasis, strong,
horizontalRule, listItem, listEnumeration, image, imageText, and codeBlock.
Raised surfaces
Panels, dialogs, menus, and toasts select their depth from background.raised. Dialogs additionally support a
contextual @dialog surface. The TUI re-resolves the palette after applying @dialog, so tokens that reference
$background.base follow the dialog background:
{
"base": {
"background": {
"base": "#071521",
"raised": {
"base": "#0c1f2e",
"high": "#102a3c",
"max": "#16364b"
}
},
"@dialog": {
"background": {
"base": "$background.raised.base"
}
}
}
}@dialog accepts the same token groups as the base mode. An action’s contextual base also becomes the fallback
for its unspecified states; an explicit state override still wins.