Start typing to search the documentation.

Docs navigation

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:

~/.config/opencode/cli.json
{
  "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:

~/.config/opencode/cli.json
{
  "theme": {
    "name": "opencode",
    "mode": "dark"
  }
}
ValueBehavior
systemFollows the light or dark mode of the terminal.
darkAlways uses the dark theme.
lightAlways 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:

~/.config/opencode/cli.json
{
  "theme": {
    "name": "catppuccin"
  }
}

Available themes include:

  • aura, ayu, carbonfox, cobalt2, cursor, dracula, everforest, flexoki
  • catppuccin, catppuccin-frappe, catppuccin-macchiato
  • github, gruvbox, kanagawa, material, matrix, mercury, monokai, nightowl, nord
  • one-dark, opencode, orng, lucent-orng, osaka-jade, palenight, rosepine
  • solarized, 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:

~/.config/opencode/themes/ocean.json (excerpt)
{
  "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.

ScopeDirectory
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.

themes/ocean.json
{
  "base": {
    "text": {
      "base": "#16324f"
    }
  },
  "light": {
    "hue": {}
  },
  "dark": {
    "hue": {},
    "text": { "base": "#d8f3ff" }
  }
}

Colors

Use a hex color, transparent, or a reference to another theme color:

themes/ocean.json
{
  "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:

themes/ocean.json
{
  "base": {
    "categorical": ["accent", "purple", "green"]
  },
  "dark": {
    "hue": {
      "accent": "$hue.cyan",
      "interactive": "$hue.blue"
    }
  }
}
KindValues
Basegray, red, orange, yellow, green, cyan, blue, purple
Aliasaccent, interactive, neutral
Step100, 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:

themes/ocean.json
{
  "base": {
    "text": {
      "action": {
        "primary": {
          "base": "$hue.interactive.400",
          "$hovered": "$hue.interactive.300",
          "$disabled": "$hue.neutral.600"
        }
      }
    }
  }
}
GroupValues
Actionsprimary, 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:

themes/ocean.json
{
  "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"
      }
    }
  }
}
GroupPurpose
textBase, muted, action, form-field, and feedback text
backgroundBase, surfaces, actions, form fields, and feedback fills
borderBorders
scrollbarScrollbars
diffAdded, removed, context, highlight, and line-number colors
syntaxSource-code highlighting
markdownMarkdown 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:

themes/ocean.json
{
  "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.