# 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:

```json title="~/.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](/cli/config) for the config location and other terminal settings.

## Modes

Use `system` to follow your terminal's appearance, or lock the theme to one mode:

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

## Built-ins

Select a built-in theme by its name:

```json title="~/.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:

```json title="~/.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.

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

```json title="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:

```json title="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:

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

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

```json title="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"
      }
    }
  }
}
```

| Group        | Purpose                                                       |
| ------------ | ------------------------------------------------------------- |
| `text`       | Base, muted, faint, 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:

```json title="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.