# theme.json schema

> The JSON Schema for theme files.

The JSON Schema for theme files. Add the schema URL as `$schema` and your editor validates and completes the file as you type:

```json
"$schema": "https://raw.githubusercontent.com/AmineChr54/umbra/main/schemas/theme.schema.json"
```

:::note
This page is generated from [`schemas/theme.schema.json`](https://github.com/AmineChr54/umbra/blob/main/schemas/theme.schema.json).
:::

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://raw.githubusercontent.com/AmineChr54/umbra/main/schemas/theme.schema.json",
  "title": "Umbra theme (theme.json)",
  "description": "A theme is data only: design tokens for shared/umbra.css. The same rules are enforced by scripts/catalog.mjs in CI and by the app when it installs a theme. No code, no url(), no custom CSS.",
  "type": "object",
  "required": ["id", "name", "description", "author", "version", "tokens"],
  "additionalProperties": false,
  "properties": {
    "$schema": { "type": "string" },
    "id": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9-]{1,39}$",
      "not": { "const": "default" },
      "description": "Lowercase letters, digits and dashes (2-40 chars). Must equal the folder name. \"default\" is the built-in theme."
    },
    "name": { "type": "string", "minLength": 1, "maxLength": 300, "description": "Shown on the Appearance page." },
    "description": { "type": "string", "minLength": 1, "maxLength": 300, "description": "One sentence." },
    "author": { "type": "string", "minLength": 1, "maxLength": 300, "description": "GitHub user name." },
    "version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$", "description": "Semantic version. Bump it on every change so users see an Update button." },
    "preview": { "type": "string", "pattern": "^[^/\\\\]+\\.(png|jpg|jpeg|webp)$", "description": "Optional image next to theme.json." },
    "tokens": {
      "type": "object",
      "minProperties": 1,
      "additionalProperties": false,
      "description": "Only the tokens below. Anything you leave out keeps the default look.",
      "properties": {
        "--bg": { "$ref": "#/$defs/color", "description": "Window background. Default #000 (true black for OLED); change it only on purpose." },
        "--surface-1": { "$ref": "#/$defs/color" },
        "--surface-2": { "$ref": "#/$defs/color" },
        "--surface-3": { "$ref": "#/$defs/color" },
        "--line": { "$ref": "#/$defs/color" },
        "--line-strong": { "$ref": "#/$defs/color" },
        "--text": { "$ref": "#/$defs/color" },
        "--text-2": { "$ref": "#/$defs/color" },
        "--text-3": { "$ref": "#/$defs/color" },
        "--accent": { "$ref": "#/$defs/color" },
        "--accent-strong": { "$ref": "#/$defs/color" },
        "--accent-soft": { "$ref": "#/$defs/color" },
        "--up": { "$ref": "#/$defs/color" },
        "--down": { "$ref": "#/$defs/color" },
        "--flat": { "$ref": "#/$defs/color" },
        "--radius-s": { "$ref": "#/$defs/length" },
        "--radius-m": { "$ref": "#/$defs/length" },
        "--radius-l": { "$ref": "#/$defs/length" }
      }
    }
  },
  "$defs": {
    "color": {
      "type": "string",
      "maxLength": 64,
      "pattern": "^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$|^(?:rgb|rgba|hsl|hsla)\\( *[0-9.]+(?:%|deg)? *[, ] *[0-9.]+%? *[, ] *[0-9.]+%? *(?:[,/] *[0-9.]+%? *)?\\)$",
      "description": "#rgb, #rrggbb, #rrggbbaa, rgb()/rgba() or hsl()/hsla()."
    },
    "length": {
      "type": "string",
      "pattern": "^(?:[0-9]|[1-9][0-9])(?:\\.[0-9]{1,3})?(?:px|rem)$",
      "description": "0-99 px or rem, e.g. 12px."
    }
  }
}
```
