Theming#
Kiln ships a fresh, responsive default theme as a package resource: light/dark colour schemes, a sidebar nav, an on-page table of contents, and search. You can tweak it with options, or replace any part with your own templates.
Tweaking the default theme#
theme: .default(
palette: .autoLightDark(primary: .black, accent: .blue),
logo: "assets/logo.svg",
favicon: "assets/logo.svg",
fonts: .init(text: "Inter", code: "JetBrains Mono"),
features: [.backToTop, .searchHighlight]
)
| Option | Purpose |
|---|---|
palette |
Palette with primary/accent Colors and a default mode (.auto/.light/.dark). |
logo |
Header logo (content-relative path). |
favicon |
Site favicon. |
fonts |
Fonts(text:code:) for body and code text. |
features |
Opt-in extras: .searchSuggest, .searchHighlight, .navigationTabs, .backToTop. |
Color has presets (.black, .blue, .indigo, …) or accepts any CSS string
via Color("#2f6feb").
Bringing your own templates#
To customise the markup, point Kiln at a directory of your own Leaf templates and assets:
theme: .custom(directory: "Theme")
Templates resolve from your directory first and fall back to the bundled theme, so you only override what you need. The theme is split into small partials:
Theme/
├── templates/
│ ├── base.leaf # overall page shell (<head>, header, layout, scripts)
│ ├── page.leaf # a standard documentation page
│ ├── home.leaf # the home page
│ ├── 404.leaf # the error page
│ └── partials/
│ ├── header.leaf
│ ├── footer.leaf
│ ├── nav-tree.leaf
│ ├── toc.leaf
│ ├── search.leaf
│ ├── language-switcher.leaf
│ └── social-icons.leaf
├── css/
└── js/
Sharing templates across sites#
If several sites share the same look — a common header, footer, and cards — you don’t want to copy those templates into every project. Ship them once from a shared Swift package as a bundled resource, then pull them in as a shared theme layer.
In the shared package, bundle a theme directory (with a templates/ folder) as a
resource and expose its URL:
// Package.swift
.target(name: "DesignTheme", resources: [.copy("Theme")])
// DesignTheme.swift
public enum DesignTheme {
public static var directory: URL {
Bundle.module.url(forResource: "Theme", withExtension: nil)!
}
}
Then each site lists it in sharedLayers:
theme: .custom(directory: "Theme", sharedLayers: [DesignTheme.directory])
Templates now resolve in order: your site’s Theme/ → the shared layer(s) →
Kiln’s bundled default. So a site overrides anything locally, falls back to the
shared design for common partials, and falls back to Kiln’s default for the rest.
sharedLayers also works on .default(sharedLayers:) if you don’t have a local
theme directory. Assets (css/js) follow the same layering, with later layers
overriding earlier ones.
Template context#
Templates receive a context with site, page, nav, language, languages,
strings, customStrings, and searchIndexURL. The rendered page body is
injected with #unsafeHTML(page.content).
strings holds Kiln’s localised UI strings for the current language — the search
box, navigation/footer labels, error-page text, and so on — e.g.
#(strings.previousPage) or #(strings.home). customStrings holds your own
theme-defined strings; rather than reaching into it directly, look strings up
with the #localise("key") tag, which resolves against the current language and
falls back to the default language:
<p class="tagline">#localise("tagline")</p>
See Content & Localisation for how to define both sets of strings per language.
Per-page templates
A page can opt into a different template via the template front-matter key
(see Markdown) — handy for a landing page or a differently
laid-out reference.
Extra CSS / JS#
For small additions you don’t need a full custom theme — just add stylesheets or scripts (content-relative paths):
extraCSS: ["assets/custom.css"],
extraJavaScript: ["assets/custom.js"]