Docs: Themes
Themes
How a tilspire theme is structured, what it owes the modules it displays, and how to fork the starter theme safely.
A tilspire theme is a plain folder of templates, components and assets — nothing compiled, nothing built.
Anatomy of a theme #
- theme.json — the manifest:
name,version,author,description,licenseandtags, plus an optionaloptionslist whose entries (key,label,type,defaultand a few more) become settings you can change in the admin panel. - components/ — shared partials, such as the header and footer.
- views/ — the templates for each kind of page tilspire renders.
- assets/ — the theme's own CSS, JavaScript, fonts and images.
A theme that draws its own sign-up form (views/auth-register.php) calls <?php $this->signupChecks(); ?> inside the form, before the submit button, since 1.17.3. It adds the checks that keep bots out. Without it, sign-ups are held to the per-address limit alone, and Health says so.
What a theme provides, and what tilspire fills in #
Since 1.4.0, what a theme owes the modules it displays is written down. A theme claims each job it wants to do itself; for every job it doesn't claim, tilspire fills in a working default, so a module always renders properly on any theme.
| The job | If the theme doesn't claim it |
|---|---|
| A content container: the width and padding around page content | tilspire adds a small stylesheet that constrains it |
| The icon set modules use | tilspire loads the icons when a page needs them |
| The stylesheets and scripts a module or page asks for | tilspire adds them to the page itself |
| The SEO head block: title, description, canonical link, robots, Open Graph, Twitter and JSON-LD | tilspire writes the parts the theme left out, and leaves the ones it wrote alone |
A way in: a link to /account for members | Nothing can be filled in here — put the link in your navigation |
The --cms-* colours, radii and fonts modules style themselves with | Modules fall back to neutral values of their own |
A theme that claims a job gets its page back untouched, and keeps control over where things go.
Content fields in your templates #
Pages built on a content-model template carry typed fields. Themes read them through the Template API — field(), rows() and related() — which escapes each value according to its declared type. The starter theme shows how, and the Template API lists every call: pages by Layout or parent, dates, pictures, links, the logo and more.
Design #
Since 1.15.0, Site ▸ Design changes the active theme's colours, fonts, sizes and layouts without editing a file. A theme declares what may change in its theme.json options: a colour, number or font option can name the CSS custom property it sets, and a layout option offers the layouts the theme itself provides. tilspire writes only those values, from allowlists, and fonts come from a set it hosts itself, so nothing is fetched from elsewhere.
A change is proposed, never applied at once: it waits in the Review queue, where you can preview it on your site as it looks now and after. A colour pair that fails the WCAG contrast check is refused, with its ratio. The Assistant and the Operator API can propose designs the same way.
Languages and menus #
On a site with more than one language, the Template API returns the visitor's language, and menus reach a theme through menu() already translated. A theme that shows no language switcher gets a plain one from tilspire.
Choosing the active theme #
Set the active theme under Site ▸ Themes. It applies across the whole site.
Start from the starter theme #
tilspire ships with a starter theme at site/themes/default/. It's built to the theme contract and written to be read end to end and forked, not treated as a finished showcase. Copy it into a new folder under site/themes/, rename it in its theme.json, and activate your copy.
A theme forked before 1.14.0 #
If your theme asks can('access_admin') to decide who is staff — to let staff past the maintenance page, or to show them a link — change it to isStaff(). Since 1.14.0 staff can sign in on the site, and access_admin is only true while they are signed in to the admin panel, so a staff member browsing the site would otherwise be treated as a visitor. The starter theme made the same change. Asking for a specific permission is unaffected.
Never edit the starter theme in place #
site/themes/default/ is one of the paths tilspire's updater manages. Every update overwrites it completely, so any change you make there directly is lost the next time you update. Always work in a copy.