Skip to content

theme.json schema

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

"$schema": "https://raw.githubusercontent.com/AmineChr54/umbra/main/schemas/theme.schema.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."
}
}
}