Template configuration
Learn how to customise plym templates
Updated Aug 04, 2026
On this page
A template ships its own design defaults in template.yaml, so switching templates changes the look of the blog without touching config.yaml.
template.yaml lives at templates/<name>/template.yaml and holds three things: heading and body fonts, the four brand colors, and the Prism theme. plym merges it under your config.yaml at startup, so the template decides the design and you override any single value you disagree with.
What a template can set
fonts:
heading: Inter
body: Merriweather
colors:
primary: "#1a1a1a"
secondary: "#6b6b6b"
accent: "#E9793A"
background: "#FAF9F6"
prism:
theme: tomorrow
| Key | Type | Purpose |
|---|---|---|
fonts.heading |
string | Google Fonts family for headings, fetched at weights 600 and 900 |
fonts.body |
string | Google Fonts family for body copy, fetched at weight 400 |
colors.primary |
hex string | Body copy and headings |
colors.secondary |
hex string | Metadata and muted copy |
colors.accent |
hex string | Links and active states |
colors.background |
hex string | Page background |
prism.theme |
string | One of coy, dark, funky, okaidia, solarizedlight, tomorrow, twilight |
The file is optional. A template without one falls back to plym's built-in defaults: Inter and Merriweather, #111111, #444444, #0066ff, #ffffff.
What a template cannot set
Every other key is rejected. template.yaml is validated strictly, so one stray key raises a ValidationError and the api does not start.
That includes prism.enabled and prism.languages. A template picks the theme; whether highlighting ships at all, and for which languages, stays with you in config.yaml. Site identity, inject, http_cache, robots, pagination, reading, backup, and media are also operator-only.
Overriding a template value
Set the same key in config.yaml. The merge is per key, so the template keeps every value you leave alone.
The docs template declares its own palette and typography:
fonts:
heading: Space Grotesk
body: Archivo
colors:
primary: "#1C1712"
secondary: "#6B6055"
accent: "#D2340A"
background: "#FFFCF6"
prism:
theme: tomorrow
Your config.yaml keeps the palette but takes the accent and the body font:
template: docs
fonts:
body: Merriweather
colors:
accent: "#00FF00"
The result:
| Key | Value | Source |
|---|---|---|
fonts.heading |
Space Grotesk | template.yaml |
fonts.body |
Merriweather | config.yaml |
colors.primary |
#1C1712 |
template.yaml |
colors.secondary |
#6B6055 |
template.yaml |
colors.accent |
#00FF00 |
config.yaml |
colors.background |
#FFFCF6 |
template.yaml |
prism.theme |
tomorrow | template.yaml |
Confirm the merge with GET /api/config, which returns the merged result rather than either file.
How the values reach the page
plym turns the merged fonts and colors into CSS variables, prepends them to the template's own stylesheets, minifies the lot, and inlines it as a single <style> block:
:root{--color-primary:#1c1712;--color-secondary:#6b6055;--color-accent:#0f0;--color-background:#fffcf6}
:root{--font-heading:'Space Grotesk';--font-body:'Archivo'}
Fonts are self-hosted, not linked. plym downloads each family from Google Fonts at startup, subsets it to printable ASCII, stores the files under storage/webfonts/, and rewrites the @font-face rules to {blog_prefix}/webfonts/. Characters outside that subset fall back to a system font, and a font name Google doesn't recognize logs font download failed — continuing without webfonts while the rest of the page renders.
A well-written template reads only these variables, which is what makes a color override work at all. A template with hardcoded hex values ignores your colors block; that is a bug in the template.
Replacing the whole template
The design lives in the template, so the larger change is swapping it:
plym template install journal
That command writes template: journal into config.yaml and re-renders every published post, so its template.yaml becomes your new baseline. Your existing overrides in config.yaml still apply, which is worth checking — an accent tuned for the old palette can look wrong against the new one. Remove your override to hand the decision back to the template.
template accepts any directory name under templates/. A name with no matching directory does not stop the api — it fails when a page is rendered, so already-published pages keep serving and the logs fill with failed to re-render. Check the api logs after a manual edit to template.