diff --git a/services/www/src/docs/content/cli/theme.mdx b/services/www/src/docs/content/cli/theme.mdx index f8096c589f1..d56a3a3d8a2 100644 --- a/services/www/src/docs/content/cli/theme.mdx +++ b/services/www/src/docs/content/cli/theme.mdx @@ -2,11 +2,11 @@ title: "Theme" --- -OpenCode includes built-in light and dark themes. Choose a color scheme in settings, or use colors derived from your terminal. +OpenCode comes with many built-in themes for light and dark modes. Choose one in settings, or mix your own colors. ## Choose a theme -Press `Ctrl+P`, then select **Open settings** to change the theme and color mode. +Press `Ctrl+P`, select **Open settings**, then open **Theme**. The default theme is `opencode`. You can also set them in your global [CLI settings](/cli/config): @@ -15,14 +15,22 @@ You can also set them in your global [CLI settings](/cli/config): "$schema": "https://opencode.ai/v2/cli.json", "theme": { "name": "tokyonight", - "mode": "system" + "mode": "dark" } } ``` -## Color mode +Run `/themes` to change themes without opening settings. -Choose whether OpenCode follows your terminal's appearance or always uses light or dark colors: +## System colors + +When OpenCode can read your terminal palette, the theme picker includes the `system` theme. It derives colors from your terminal's foreground, background, and ANSI palette. + +The theme name `system` selects terminal-derived colors. The mode `system` follows the terminal's light or dark appearance. + +## Mode + +Use `system` to follow your terminal's appearance, or lock the theme to light or dark mode: | Mode | Behavior | | --- | --- | @@ -30,28 +38,209 @@ Choose whether OpenCode follows your terminal's appearance or always uses light | `dark` | Always use the theme's dark colors. | | `light` | Always use the theme's light colors. | -For example, always use dark colors: +This maps to the `mode` field inside `theme` in your CLI config. If a custom theme provides only one mode, OpenCode uses that mode when the other is requested. -```json title="cli.json" +## Adding a Theme + +To add a theme you downloaded or received from someone else, copy its JSON file into the global themes directory: + +```bash +cp theme.json ~/.config/opencode/themes/ +``` + +You can also put the theme in a project's `.opencode/themes` directory to make it available only in that project. + +The filename becomes the theme name. For example, `my-theme.json` appears as `my-theme`. Restart OpenCode after adding the file, then run `/themes` to select it. + +OpenCode reads `.json` files only. A theme closer to the current directory replaces a global or parent theme with the same name. + +## Using Themes in Plugins + +[CLI plugins](/build/plugins/cli) read resolved colors through `context.theme` and the active mode through `context.themeMode`. Use semantic tokens so your plugin follows the selected theme: + +```tsx title="Inside a CLI plugin component" +import { usePlugin } from "@opencode/plugin/tui" + +function Status() { + const context = usePlugin() + return Ready +} +``` + +The public CLI plugin API does not register themes. Add custom theme definitions with JSON files in the directories above. + +## Creating a Theme + +Use our [Theme Tool](https://opencode.ai/theme/) to create or edit themes. This tool makes it easy to understand the structure, change it, and define custom colors. The explanation below is for reference. + +A theme separates shared semantic tokens from mode-specific palettes: + +| Section | Purpose | +| --- | --- | +| `base` | The complete token tree shared by every mode. | +| `light`, `dark` | A complete hue palette and optional token overrides for that mode. | + +Every theme provides a complete `base` and defines at least one of `light` or `dark`. Modes inherit from the file's `base`; they do not inherit from the built-in OpenCode theme or from each other. + +The examples below show only the sections relevant to each concept. Use the theme editor to generate a complete file before customizing it. + +### Structure + +Put colors used by both modes in `base`, then override them inside `light` or `dark` when needed: + +```json title="theme.json (excerpt)" { - "theme": { - "name": "tokyonight", - "mode": "dark" + "$schema": "https://opencode.ai/theme.json", + "base": { + "text": { + "base": "$hue.neutral.200", + "muted": "$hue.neutral.400" + } + }, + "light": { + "hue": {} + }, + "dark": { + "hue": {}, + "text": { + "muted": "$hue.neutral.300" + } } } ``` -## Terminal colors +The complete `base` supplies every semantic token. Each mode supplies every required hue and may override any part of the base token tree. -When OpenCode can read your terminal palette, the theme picker also includes `system`. This theme derives its colors from your terminal's foreground, background, and ANSI palette. +### Colors and references -```json title="cli.json" +A color can be a hex value, `"transparent"`, or a reference to another theme color: + +```json title="Inside base or a mode" { - "theme": { - "name": "system", - "mode": "system" + "text": { + "base": "#eeeeee", + "muted": "$hue.neutral.400" + }, + "border": { + "base": "$text.muted" } } ``` -The theme name `system` selects terminal-derived colors. The color mode `system` follows the terminal's light or dark appearance. +### Hues + +Hues are nine-step color scales. Every mode defines the eight base hues and three aliases: + +| 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` | + +Define all nine steps for a base hue, or alias one hue to another: + +```json title="Inside light or dark" +{ + "hue": { + "purple": { + "100": "#b38ff4", + "200": "#9d7cd8", + "300": "#8869bd", + "400": "#7357a2", + "500": "#5f4688", + "600": "#4b356f", + "700": "#392557", + "800": "#271640", + "900": "#17072b" + }, + "accent": "$hue.purple" + } +} +``` + +Hue steps follow the mode's contrast direction. Light themes run from dark at `100` to light at `900`; dark themes run from light at `100` to dark at `900`. + +The `categorical` array in `base` sets the ordered hues used to distinguish agents and other repeated items: + +```json title="Inside base" +{ + "categorical": ["accent", "purple", "green", "blue"] +} +``` + +### Semantic tokens + +Semantic tokens assign colors to UI roles. Components use these roles instead of selecting colors directly. + +| Group | Purpose | +| --- | --- | +| `text` | Base, muted, action, form-field, status, and feedback text. | +| `background` | Base, raised surfaces, actions, form fields, and feedback fills. | +| `border`, `scrollbar` | Border and scrollbar colors. | +| `diff` | Added, removed, context, highlight, and line-number colors. | +| `syntax` | Source-code highlighting. | +| `markdown` | Markdown elements. | + +For example, `text.muted` controls secondary text while `background.raised` defines progressively elevated surfaces: + +```json title="Inside base" +{ + "text": { + "base": "$hue.neutral.200", + "muted": "$hue.neutral.400" + }, + "background": { + "base": "$hue.neutral.800", + "raised": { + "base": "$hue.neutral.700", + "high": "$hue.neutral.600", + "max": "$hue.neutral.500" + } + } +} +``` + +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`. + +### Actions and states + +Actions have `primary`, `secondary`, and `destructive` variants. Actions and form fields use `base` plus optional interaction states: + +```json title="Inside base or a mode" +{ + "text": { + "action": { + "primary": { + "base": "$hue.interactive.400", + "$hovered": "$hue.interactive.300", + "$disabled": "$hue.neutral.600" + } + } + } +} +``` + +The state keys are `$hovered`, `$focused`, `$pressed`, `$selected`, and `$disabled`. An unspecified state uses that action or form field's `base` color. + +### Dialog surface + +Panels, dialogs, menus, and toasts select their depth from `background.raised`. Dialogs may additionally override semantic tokens with `@dialog`: + +```json title="Inside base or a mode" +{ + "@dialog": { + "background": { + "base": "$background.raised.base", + "action": { + "primary": { + "$hovered": "$background.raised.high" + } + } + } + } +} +``` + +The TUI resolves references again after applying `@dialog`, so tokens that reference `$background.base` follow the dialog background. A contextual action `base` becomes the fallback for its unspecified states; explicit state overrides still win.