Skip to content

Theme Authoring

alsi-lawr edited this page Jul 16, 2026 · 2 revisions

Theme authoring

This page covers built-in theme families contributed to Neotheme. User configuration should use the complete semantic palette described in Palette Customization.

Choose a palette model

A built-in module must return a complete NeothemePalette. It can:

  1. Define every semantic role directly.
  2. Define a compact NeothemeSimplifiedPalette and call the family-neutral transform.

All current built-in families use the simplified model because related semantic roles intentionally reuse the same configured colors.

Simplified module shape

local transform = require("neotheme.themes.simplified").transform

---@type NeothemeSimplifiedPalette
local simplified = {
	-- Supply every required field from the schema below.
}

return transform(simplified)

Keep raw color literals in this theme definition. Highlight and integration modules consume semantic roles and must not special-case a family or variant.

Simplified input schema

Surfaces

All surface inputs are required and map directly to the same complete-palette role:

  • surface_deepest
  • surface_dark
  • surface_base
  • surface_raised
  • surface_selected
  • surface_border
  • surface_muted
  • surface_addition
  • surface_error

Text

Input Complete role Fallback
text_primary text.primary Required
text_bright text.bright Required
text_strong text.strong Required
text_muted text.muted syntax_type
text_on_accent text.on_accent surface_deepest
text_on_error text.on_error Required

text_muted and text_on_accent are optional. Every other simplified field is required.

Syntax and diagnostics

Required syntax inputs:

  • syntax_comment
  • syntax_string
  • syntax_keyword
  • syntax_function_name
  • syntax_type
  • syntax_property
  • syntax_literal
  • diagnostic_error
  • version_control_conflict

Expansion mapping

The transformer maps direct syntax inputs and derives related roles as follows:

Complete role or roles Simplified source
syntax.comment syntax_comment
syntax.string syntax_string
syntax.keyword, syntax.operator syntax_keyword
syntax.function_name, syntax.tag syntax_function_name
syntax.type, syntax.attribute syntax_type
syntax.property syntax_property
syntax.literal syntax_literal
syntax.punctuation text_bright
syntax.regexp, syntax.special diagnostic_error
diagnostic.error diagnostic_error
diagnostic.warning syntax_keyword
diagnostic.information syntax_function_name
diagnostic.hint syntax_literal
diagnostic.success syntax_string
markup.heading_1, markup.link_label, markup.list syntax_keyword
markup.heading_2, markup.link syntax_function_name
markup.heading_3, markup.raw, markup.checked syntax_string
markup.heading_4, markup.math syntax_literal
markup.heading_5, markup.unchecked syntax_type
markup.heading_6, markup.quote syntax_comment
version_control.added syntax_string
version_control.changed syntax_keyword
version_control.removed diagnostic_error
version_control.ignored surface_muted
version_control.conflict version_control_conflict
ui.accent, ui.cursor, ui.search syntax_keyword
ui.directory syntax_function_name
ui.current_search text_on_error
ui.match syntax_literal
ui.focus text_strong

The transformer contains no family-specific values or names, so new families can use the same model when their reuse pattern matches.

Register a built-in theme

Add the module to the private registry with its public name and background metadata:

local modules = {
	["family-variant"] = {
		module = "neotheme.themes.family.variant",
		background = "dark",
		family = "family",
	},
}

Then update the NeothemeOptions.theme annotation so editor tooling exposes the new public name.

Neotheme does not create a separate colors/<theme>.lua file. All built-in variants use colors/neotheme.lua.

Tests for a built-in theme

The built-in contract should verify:

  • The public name appears in themes().
  • The family appears in families() and themes(family) returns the member.
  • Every semantic palette path resolves to a six-digit color.
  • The theme can be selected and loaded.
  • Its background metadata is applied.
  • Returned palettes and name lists are mutation-safe copies.

Do not add exact hex-value assertions for curated built-in colors. Tests should protect registry, completeness, selection, copying, and semantic behavior.

Documentation and previews

For a new built-in variant:

  1. Add it to the relevant docs/themes/<family>/README.md inventory.
  2. Add a new family to the root README and Themes family tables.
  3. Regenerate the family previews and matrices with assets/scripts/generate-theme-assets.sh.
  4. Confirm :NeothemeList <family> and the visual browser expose it.

The portable asset tools and dependencies are documented in the repository's assets/scripts/README.md.

Clone this wiki locally