Theming templates
Theming Templates
Section titled “Theming Templates”TemplateEngine renders text and files from a theme token map. It supports inline expressions, block syntax for loops and conditionals, color formatting, filter pipelines, and TOML-driven batch processing.
This document describes the supported template syntax and data model.
TemplateEngine is responsible for:
- Rendering template strings and files
- Reading values from theme token maps
- Applying color and string filters
- Executing loops and conditionals
- Processing TOML template configs
- Computing custom colors and
closest_color
It is not responsible for image loading or palette extraction.
Syntax
Section titled “Syntax”Expressions
Section titled “Expressions”Inline expressions use:
{{ ... }}Examples:
{{ colors.primary.default.hex }}{{ colors.surface.dark.rgb }}{{ mode }}{{ image }}Blocks
Section titled “Blocks”Control blocks use:
<* ... *>Supported block tags:
forifelseendifendfor
Examples:
<* for name, value in colors *>{{ name }}={{ value.default.hex }}<* endfor *><* if {{ loop.first }} *>first<* else *>not first<* endif *>Standalone Block Trimming
Section titled “Standalone Block Trimming”If a block tag appears alone on a line, with only whitespace around it, that whole line is removed from the rendered output. This matters for whitespace-sensitive templates.
Data Model
Section titled “Data Model”Palette Colors
Section titled “Palette Colors”Colors are accessed with:
{{ colors.<name>.<mode>.<format> }}Examples:
{{ colors.primary.default.hex }}{{ colors.surface.light.hsl }}{{ colors.terminal_foreground.default.hex_stripped }}Supported modes:
darklightdefault
default resolves to the configured default mode.
Special Values
Section titled “Special Values”The following values are available directly:
modeimageclosest_color
mode is the current default mode.
image is the source image path when rendering from an image-driven theme.
closest_color is populated during config-driven rendering when compare_to and colors_to_compare are used.
Color Aliases
Section titled “Color Aliases”Supported aliases:
hover->surface_container_highon_hover->on_surface
Formats
Section titled “Formats”Supported output formats:
hexhex_strippedrgbrgb_csvrgbahslhslaredgreenbluealphahuesaturationlightness
Format behavior:
hex:#rrggbbhex_stripped:rrggbbrgb:rgb(r, g, b)rgb_csv:r,g,brgba:rgba(r, g, b, a)hsl:hsl(h, s%, l%)hsla:hsla(h, s%, l%, a)red,green,blue: integer channelsalpha: floating-point alphahue: integer huesaturation,lightness: integer percentages
Filters
Section titled “Filters”Filters are chained with pipe syntax:
{{ colors.primary.default.hex | grayscale }}{{ colors.primary.default.hex | set_alpha 0.5 }}{{ colors.primary.default.hex | blend: "#ff0000", 0.5 }}Supported syntaxes:
| filter| filter arg| filter: arg
Color Filters
Section titled “Color Filters”These operate on colors:
grayscaleinvertset_alphaset_lightnessset_hueset_saturationset_redset_greenset_bluelightendarkensaturatedesaturateauto_lightness
Color-Argument Filters
Section titled “Color-Argument Filters”These accept another color:
blendharmonizeto_color
Examples:
{{ colors.primary.default.hex | blend: "#ff0000", 0.5 }}{{ colors.primary.default.hex | harmonize: "#00ff88" }}{{ "#ffaa00" | to_color | darken 0.1 }}String Filters
Section titled “String Filters”These operate on strings:
replacelower_casecamel_casepascal_casesnake_casekebab_case
Examples:
{{ mode | replace: "dark", "night" }}{{ "surface container high" | snake_case }}Control Flow
Section titled “Control Flow”For Loops
Section titled “For Loops”Supported forms:
<* for item in iterable *>...<* endfor *><* for key, value in colors *>...<* endfor *>Conditionals
Section titled “Conditionals”Supported forms:
<* if {{ expr }} *>...<* endif *><* if not {{ expr }} *>...<* else *>...<* endif *>Truthiness rules:
falseis false- numeric
0is false - empty strings are false
- case-insensitive
"false","0", and"none"are false - empty arrays and maps are false
- everything else is true
Iterables
Section titled “Iterables”Supported iterable expressions:
colorspalettes.primarypalettes.secondarypalettes.tertiarypalettes.errorpalettes.neutralpalettes.neutral_variant- numeric ranges like
0..10and-5..5 - arrays and maps from the current scope
colors
Section titled “colors”colors iterates all available token names and their mode maps:
<* for name, value in colors *>{{ name }}={{ value.default.hex }}<* endfor *>palettes.*
Section titled “palettes.*”palettes.* iterates derived tone steps for the selected palette family.
Supported families:
primarysecondarytertiaryerrorneutralneutral_variant
Tone values:
05101520253035405060708090959899100
Loop Metadata
Section titled “Loop Metadata”Inside for loops, the loop object provides:
loop.indexloop.firstloop.last
loop.index is zero-based.
File Rendering
Section titled “File Rendering”When rendering files, TemplateEngine:
- reads the template file
- renders the output
- creates parent directories when needed
- skips rewriting unchanged files
- reports render failures through the file render result
Skipping unchanged output avoids unnecessary timestamp updates and downstream reloads.
Config Processing
Section titled “Config Processing”TemplateEngine supports TOML-driven template processing.
Top-level sections:
[config][templates]
Custom Colors
Section titled “Custom Colors”[config.custom_colors] supports both forms:
[config.custom_colors]brand = "#ff0000"[config.custom_colors.brand]color = "#ff0000"blend = trueGenerated tokens per mode:
{name}_source{name}_value{name}on_{name}{name}_containeron_{name}_container
Template Entries
Section titled “Template Entries”Each template entry supports:
input_pathoutput_pathoutput_path_dynamiccolors_to_comparecompare_topre_hookpost_hookinput_path_modesindex
output_path accepts either a single string or an array of strings:
[templates.qt]input_path = "./qtct.conf"output_path = [ "~/.config/qt5ct/colors/noctalia.conf", "~/.config/qt6ct/colors/noctalia.conf",]output_path_dynamic is optional. It is a shell command string (same templating as hooks: {{ config_dir }}, {{ mode }}, etc.). After rendering, the shell runs it; on exit status 0, each non-empty line of stdout is appended as an output path (after resolveConfigPath). Lines starting with # are ignored. Use this when destinations depend on the machine (for example Emacs config layout) without hard-coding app-specific logic in the engine. Static output_path entries, if any, are kept; dynamic lines are added after them.
input_path_modes selects a different template input for dark and light mode:
[templates.foo]input_path_modes = { dark = "./dark.css", light = "./light.css" }output_path = "~/.config/foo/theme.css"Relative input_path and output_path values are resolved from the config file directory.
index controls processing order and defaults to 0.
Closest Color
Section titled “Closest Color”compare_to and colors_to_compare can be used to compute closest_color.
Behavior:
compare_tois rendered firstcolors_to_compareprovides named comparison candidates- the closest candidate becomes available as
{{ closest_color }}
Supported hooks:
pre_hookpost_hook
Behavior:
- hook commands are rendered through the template engine first
pre_hookruns before any output files are writtenpost_hookruns after rendering, but only when at least one output file changedclosest_coloris available inside hooks
Additional variables available inside hooks and templates:
{{ mode }}{{ image }}{{ closest_color }}{{ config_dir }}{{ config_file }}
Terminal Tokens
Section titled “Terminal Tokens”Every resolved theme exposes flattened terminal tokens. Built-in and community palettes provide curated terminal colors, while wallpaper-derived themes synthesize terminal colors from the generated palette.
terminal_foregroundterminal_backgroundterminal_cursorterminal_cursor_textterminal_selection_fgterminal_selection_bgterminal_normal_blackterminal_normal_redterminal_normal_greenterminal_normal_yellowterminal_normal_blueterminal_normal_magentaterminal_normal_cyanterminal_normal_whiteterminal_bright_blackterminal_bright_redterminal_bright_greenterminal_bright_yellowterminal_bright_blueterminal_bright_magentaterminal_bright_cyanterminal_bright_white
These are accessed like any other color token:
{{ colors.terminal_background.default.hex }}{{ colors.terminal_normal_red.default.rgb }}CLI Usage
Section titled “CLI Usage”Template rendering is available through noctalia theme.
Render one file:
noctalia theme <image> -r input.txt:output.txtProcess a TOML config:
noctalia theme <image> -c templates.tomlList shipped built-in templates:
noctalia theme --list-builtinsProcess the shipped built-in template catalog:
noctalia theme <image> --builtin-configThe shipped built-in catalog lives at assets/templates/builtin.toml.
Render from a precomputed theme JSON:
noctalia theme --theme-json theme.json -r input.txt:output.txtProcess a TOML config:
noctalia theme <image> -c templates.tomlSet the default template mode:
noctalia theme <image> --default-mode light -r input.txt:output.txt