LumisDocs
Themes

Theme files

The JSON format of a Lumis theme, its JSON Schema, and how to write one.

A Lumis theme is a JSON file that follows the schema at https://lumis.sh/schemas/themes/v1.0.0.json. Built-in themes, themes generated from a Neovim colorscheme, and themes you write by hand all use it.

Name the schema in $schema to get completion and validation in editors that support JSON Schema, such as VS Code and Zed:

{
  "$schema": "https://lumis.sh/schemas/themes/v1.0.0.json",
  "name": "my_theme",
  "appearance": "dark",
  "revision": "v1",
  "license": "MIT",
  "highlights": {
    "normal": { "fg": "#f8f8f2", "bg": "#282a36" },
    "comment": { "fg": "#6272a4", "italic": true },
    "keyword": { "fg": "#ff79c6", "bold": true },
    "string": { "fg": "#f1fa8c" },
    "function": { "fg": "#50fa7b" },
    "error": { "fg": "#ff5555", "underline": "wavy" }
  }
}

Fields

FieldRequiredValue
$schemanoURL of the schema version the file follows. Lumis ignores it when loading.
nameyesTheme name. Not empty.
appearanceyes"light" or "dark": the background the theme is designed for.
revisionyesCommit or version of the source the theme came from. For a hand-written theme, any label such as "v1". Not empty.
licensenoSPDX license expression, such as "MIT". "NONE" means the source has no license file and "NOASSERTION" that its license is unknown, which is also the value when the field is omitted.
highlightsyesStyles keyed by scope name. See Scopes.

The schema allows no other fields. Lumis ignores unknown fields when it loads a theme, so a misspelled field such as "highlight" raises no error at runtime; validating against the schema is what catches it.

Styles

Each entry in highlights is a style. Every field is optional:

FieldValue
fgForeground color as # followed by six hex digits, such as "#ff79c6".
bgBackground color, in the same format.
boldtrue or false.
italictrue or false.
underlinetrue (same as "solid"), or one of "solid", "wavy", "double", "dotted", "dashed", "none". "undercurl" is accepted as "wavy".
strikethroughtrue or false.

Lumis loads other color strings such as "#fff" or "red", but terminal output skips any color that is not six hex digits, so the schema rejects them.

Older theme files may use the boolean fields undercurl, underdouble, underdotted and underdashed. They still load and are marked deprecated in the schema. Use underline with the matching style instead.

Scopes

Keys in highlights are the highlight scope names that Tree-sitter queries assign, such as keyword, function.method or comment.documentation. Some scopes end with a language name, such as string.special.symbol.elixir, to style one language differently.

A scope without its own style uses its closest parent. A token captured as function.method.call looks up function.method.call, then function.method, then function. A theme that styles a few top-level scopes therefore covers every language, and more specific entries refine it.

These scopes style output rather than syntax:

ScopeStyles
normalDefault text and background of the whole block.
highlightedLines selected for line highlighting.
line_numberLine numbers.
line_number.highlightedLine numbers of highlighted lines.
diff.plus.word, diff.minus.wordChanged words in diffs.

highlights.toml lists every scope the bundled queries use.

Create a theme

  • Generate one from any Neovim colorscheme with the Neovim colorscheme recipe. Generated files already name the schema.
  • Start from a built-in theme. Each one is published as JSON at https://unpkg.com/@lumis-sh/themes/dist/json/<theme>.json; see the theme list for names.
  • Write one by hand, starting from the example above.

Then load it in your runtime.

Schema versions

A published schema version never changes. A change to the theme format ships as a new version at a new URL, so a file that names an older version keeps validating against the schema it was written for. https://lumis.sh/schemas/theme.json, the first URL the schema was published at, redirects to version 1.0.0.

On this page