Skip to content
HomePagesHomePages template kit

theme-invalid

"A template's theme.ts is missing, does not export `theme`, or exports one that is not a valid TokenTheme."

Venue: check — A template’s theme.ts is missing, does not export theme, or exports one that is not a valid TokenTheme.

Every template folder (templates/<key>/) must have a theme.ts at its root, and that file must:

  • exist — a template with no theme.ts at all reports this once, naming the missing file;
  • build — the file is compiled and evaluated, so a syntax error or an unresolvable import reports here;
  • export the reserved binding theme — the name is part of the contract, exactly as schema is in section.ts and fixtures is in fixtures.ts. A differently named export is not found, however well-formed the object behind it is;
  • export a valid TokenTheme — the object is parsed with TokenThemeSchema, and every failing path is reported together.

This holds for every output class. A website template and a rendered-class one declare their tokens the same way and are graded the same way.

theme.ts is a reserved contract file, and everything downstream reads it by that contract: the CLI compiles theme.css from it, the dev server builds the preview stylesheet from it, and the publisher builds the shipped bundle from it. A binding under any other name is simply not there as far as those consumers are concerned.

The name only looks optional. Nothing about a website template forces the file to be read while you are authoring it — its stylesheet is assembled from the sections — so a renamed export sits there working, gets copied into the next template, and fails the first time that copy is a rendered class, as a stylesheet that would not compile. That error names CSS, and the fault is a name. Grading the file the same way for every output class is what keeps the reserved binding one contract instead of two.

Read the message: it names the template and which half of the contract failed. A renamed export is a rename back to theme; if the old name reads better at an import site, alias it there instead.

templates/acme-modern/theme.ts
import type { TokenTheme } from "@homepages/template-kit";
export const acmeModernTheme: TokenTheme = {
colors: { ink: "#111418", page: "#ffffff" },
fonts: { sans: '"Inter", system-ui, sans-serif' },
type: { base: "1rem" },
radii: {},
shadows: {},
layout: {},
document: { font: "sans", color: "ink", background: "page", fontSize: "base" },
};
templates/acme-modern/theme.ts does not export `theme`. It must `export const theme: TokenTheme = { … }`.
templates/acme-modern/theme.ts
import type { TokenTheme } from "@homepages/template-kit";
export const theme: TokenTheme = {
colors: { ink: "#111418", page: "#ffffff" },
fonts: { sans: '"Inter", system-ui, sans-serif' },
type: { base: "1rem" },
radii: {},
shadows: {},
layout: {},
document: { font: "sans", color: "ink", background: "page", fontSize: "base" },
};

An importer that wants the longer name says so at the import, where the ambiguity it is solving actually exists:

import { theme as acmeModernTheme } from "./templates/acme-modern/theme";
  • Tokens in TokenTheme — what the object declares, and the theme.css compiled from it.
  • template-kit theme — the command that compiles it, and the exit codes for the same three failures.
  • template-invalid — the sibling gate on the template folder’s other contract file, template.ts.