Themes (shared header, footer, layout, CSS)
By default each page renders in a minimal built-in theme. Give your pages a shared look three ways — zero-code to a publishable package.
1. Header & footer partials (no code)
Drop pages/_header.html and pages/_footer.html — they wrap every page.
2. A local theme
pages/_theme.js owns the document:
export const css = `body { max-width: 760px; margin: 0 auto } header { padding: 1rem }`;
export function layout({ title, head, content, meta }) {
return `<!doctype html><html lang="en"><head>${head}<title>${title}</title>
<link rel="stylesheet" href="/_theme.css"></head>
<body><nav>…</nav><main>${content}</main><footer>…</footer></body></html>`;
}
Put head in <head> (it carries the page's SEO/OG/JSON-LD tags).
3. A third-party theme
npx create-volt create-theme my-theme # scaffolds a publishable volt-theme-my-theme
cd volt-theme-my-theme && npm publish
# in your app:
npm install volt-theme-my-theme # then set THEME=my-theme in .env
Resolution: THEME env (volt-theme-<name>) → local pages/_theme.js → built-in default.
One stylesheet for page + editor
The active theme's CSS is served at /_theme.css — from a theme's export const css, or a pages/_theme.css file, or the default. Pages link it, and the WYSIWYG editor loads the same /_theme.css into RTEPro, so the editor preview matches the published page. CSS is authored once, in the theme — never duplicated.
OG images
Per-page in front-matter (image: /media/og.webp), or a site-wide default with OG_IMAGE in .env.