Skip to content

User Templates

Noctalia allows you to create custom templates to theme any application that supports configuration files. Your templates automatically update whenever you change your color scheme.


  1. Go to Settings → Color Scheme → Templates
  2. Scroll to Advanced section
  3. Enable User templates
  4. A configuration file will be created at ~/.config/noctalia/user-templates.toml

Create a template file anywhere you like, for example ~/.config/noctalia/templates/myapp.css:

/* Example CSS template */
.my-app {
background: {{colors.surface.default.hex}};
color: {{colors.on_surface.default.hex}};
accent: {{colors.primary.default.hex}};
border: 1px solid {{colors.outline.default.hex}};
}

Edit ~/.config/noctalia/user-templates.toml:

[config]
[templates.myapp]
input_path = "~/.config/noctalia/templates/myapp.css"
output_path = "~/.config/myapp/theme.css"
post_hook = "notify-send 'myapp templated'"

Properties:

  • input_path - Path to your template file
  • output_path - Where to write the generated theme
  • post_hook - Optional command to run after generation (e.g., reload app)

Your template will now be processed automatically whenever the theme changes.


Templates use the syntax {{colors.name.mode.format}}:

  • name: The color name (e.g., primary, surface, on_surface)
  • mode: default, dark, or light. Use default to follow the system theme.
  • format: Output format (see below)
FormatExample Output
hex#FF5733
hex_strippedFF5733
rgbrgb(255, 87, 51)
rgbargba(255, 87, 51, 1.0)
hslhsl(14, 100%, 60%)
hslahsla(14, 100%, 60%, 1.0)
red, green, blueIndividual RGB values (0-255)
hue, saturation, lightnessIndividual HSL values
alphaAlpha value (0.0-1.0)

Noctalia generates a complete 48-color Material Design 3 palette:

Primary Colors (8)
Color NameDescription
primaryMain accent color
on_primaryText/icons on primary
primary_containerContainer using primary
on_primary_containerText/icons on primary container
primary_fixedFixed primary (consistent across modes)
primary_fixed_dimDimmed fixed primary
on_primary_fixedText on fixed primary
on_primary_fixed_variantVariant text on fixed primary
Secondary Colors (8)
Color NameDescription
secondarySecondary accent color
on_secondaryText/icons on secondary
secondary_containerContainer using secondary
on_secondary_containerText/icons on secondary container
secondary_fixedFixed secondary
secondary_fixed_dimDimmed fixed secondary
on_secondary_fixedText on fixed secondary
on_secondary_fixed_variantVariant text on fixed secondary
Tertiary Colors (8)
Color NameDescription
tertiaryTertiary accent color
on_tertiaryText/icons on tertiary
tertiary_containerContainer using tertiary
on_tertiary_containerText/icons on tertiary container
tertiary_fixedFixed tertiary
tertiary_fixed_dimDimmed fixed tertiary
on_tertiary_fixedText on fixed tertiary
on_tertiary_fixed_variantVariant text on fixed tertiary
Error Colors (4)
Color NameDescription
errorError/warning color
on_errorText/icons on error
error_containerContainer for errors
on_error_containerText/icons on error container
Surface Colors (11)
Color NameDescription
surfaceMain background
on_surfacePrimary text color
surface_variantAlternative surface
on_surface_variantText on surface variant
surface_dimDimmed surface
surface_brightBright surface
surface_container_lowestLowest elevation container
surface_container_lowLow elevation container
surface_containerStandard container
surface_container_highHigh elevation container
surface_container_highestHighest elevation container
Outline & Utility Colors (4)
Color NameDescription
outlineBorder/divider color
outline_variantSubtle outline variant
shadowShadow color
scrimOverlay/scrim color
Inverse Colors (3)
Color NameDescription
inverse_surfaceInverted surface
inverse_on_surfaceText on inverse surface
inverse_primaryInverted primary
Background Colors (2)
Color NameDescription
backgroundBackground (alias for surface)
on_backgroundText on background

Modify colors using filters with the | syntax:

accent = {{colors.primary.default.hex | lighten 10}}
overlay = {{colors.surface.default.hex | set_alpha 0.8}}
muted = {{colors.error.default.hex | grayscale}}

Chain multiple filters:

highlight = {{colors.primary.default.hex | lighten 10 | set_alpha 0.9}}
FilterArgumentDescription
grayscalenoneConvert to grayscale
invertnoneInvert the color
lightenpercentage (0-100)Increase lightness
darkenpercentage (0-100)Decrease lightness
saturatepercentage (0-100)Increase saturation
desaturatepercentage (0-100)Decrease saturation
set_alphafloat (0.0-1.0)Set alpha/opacity
set_lightnesspercentage (0-100)Set absolute lightness
set_saturationpercentage (0-100)Set absolute saturation
set_huedegrees (0-360)Set absolute hue
set_redvalue (0-255)Set red channel
set_greenvalue (0-255)Set green channel
set_bluevalue (0-255)Set blue channel
auto_lightnesspercentageLighten if dark, darken if light
blend"#hex", amountBlend hue toward target color
harmonize"#hex"M3 harmonize (max 15° hue shift)
FilterDescription
lower_caseConvert to lowercase
camel_caseConvert to camelCase
pascal_caseConvert to PascalCase
snake_caseConvert to snake_case
kebab_caseConvert to kebab-case
replace: "find", "replace"String replacement

Use control flow blocks with <* ... *> syntax:

<* for name, value in colors *>
--color-{{name}}: {{value.default.hex}};
<* endfor *>

Available iterables:

  • colors - All 48 color name/value pairs
  • palettes.primary, palettes.secondary, etc. - Tonal palette entries
  • 0..10 - Numeric ranges

Loop variables:

  • loop.index - Current index (0-based)
  • loop.first - true if first iteration
  • loop.last - true if last iteration
<* if {{ loop.first }} *>
/* First item */
<* else *>
/* Other items */
<* endif *>

Define custom colors that generate full M3 token sets:

[config.custom_colors]
warning = "#FFA500"
success = { color = "#00FF00", blend = true }

Each generates 6 tokens: {name}, on_{name}, {name}_container, on_{name}_container, {name}_source, {name}_value

TagDescription
{{image}}Path to source wallpaper image

Template (~/.config/noctalia/templates/neovim.lua):

return {
bg = "{{colors.surface.default.hex}}",
fg = "{{colors.on_surface.default.hex}}",
accent = "{{colors.primary.default.hex}}",
error = "{{colors.error.default.hex}}",
warning = "{{colors.tertiary.default.hex}}",
}

Config:

[templates.neovim]
input_path = "~/.config/noctalia/templates/neovim.lua"
output_path = "~/.config/nvim/lua/colors/noctalia.lua"

Template (~/.config/noctalia/templates/vars.css):

:root {
<* for name, value in colors *>
--{{name | kebab_case}}: {{value.default.hex}};
<* endfor *>
}

  • Check file paths - Use absolute paths or ~ for home directory
  • Validate TOML syntax - Use a TOML validator
  • Check logs - Run Noctalia from terminal to see error messages
  • Check mode - Ensure you’re using default, dark, or light correctly
  • Verify color names - Check spelling matches the available colors
  • Trigger regeneration - Change any color scheme setting
  • Check post_hook - Ensure reload commands are correct