Documentation
ZeroPress Theme Runtime Spec v0.3
Status: Active (current manifest contract for validation and uploads)
ZeroPress Theme Runtime Spec v0.3
Status: Active (current manifest contract for validation and uploads)
0. Core Philosophy
- Themes define structure, not application behavior.
- A theme bundle is a file-only artifact.
- Tooling (
dev,validate,pack) is external to the runtime contract. - Themes must render meaningful HTML even without JavaScript.
1. Scope
A ZeroPress theme is responsible only for:
- HTML templates
- CSS and static assets
- Theme metadata (
theme.json)
Out of scope:
- Backend APIs, authentication, and admin features
- Client-side routing-dependent app architecture
- Runtime logic-centric application behavior
- Site-level menu assignment state
- Site-level widget assignment state
2. Runtime Contract
Baseline structure compatible with current build/upload pipelines:
my-theme/
theme.json
layout.html
index.html
post.html
page.html
archive.html
category.html
tag.html
404.html (optional)
partials/
*.html
assets/
style.css
theme.js (optional)
Key points:
- Template files are expected as root-level
.htmlfiles. partials/is optional.assets/style.cssis required.assets/theme.jsis optional and is not auto-executed by the runtime contract.archive.html,category.html, andtag.htmlare optional capabilities. If a theme omits them, related route outputs are omitted even when preview-data still contains those route arrays.404.htmlis optional. When present, build tooling may emit a404.htmlartifact; when absent, that artifact is omitted.- Upload ZIPs may be root-flat or wrapped in one top-level folder.
3. theme.json v0.3
Minimal example:
{
"name": "My Theme",
"namespace": "your-namespace",
"slug": "my-theme",
"version": "0.3.0",
"license": "MIT",
"description": "A ZeroPress theme.",
"runtime": "0.3",
"menuSlots": {
"primary": {
"title": "Primary Menu",
"description": "Main header navigation"
},
"sidebar": {
"title": "Sidebar Menu"
}
},
"widgetAreas": {
"sidebar": {
"title": "Sidebar Widgets",
"description": "Widgets shown next to article and page content"
},
"header": {
"title": "Header Widgets"
}
}
}
Required fields:
name(string, 1-80 chars)namespace(string, 3-24 chars)slug(string, 3-32 chars)version(semver)license(enum)runtime(must be"0.3")
Optional fields:
author(string, 1-80 chars if present)description(string, up to 280 chars)menuSlots(object map of recommendedmenu_idvalues)widgetAreas(object map of recommendedwidget_area_idvalues)
License enum:
MITApache-2.0BSD-3-ClauseGPL-3.0-onlyGPL-3.0-or-later
Identity rules:
namespaceandslugmust follow the ZeroPress package naming rulesnamespacemust match the registered publisher namespace in directory-backed submission flowsslugis the canonical theme identifier within a namespace
3.1 menuSlots
menuSlots is an optional object that declares which menu_id values a theme commonly expects.
- Keys are stable identifiers such as
primary,footer, ordocs-sidebar - Values contain admin-facing helper metadata for menu creation guidance
menuSlotsdoes not assign a menu to a slotmenuSlotsdoes not filter or control build behavior- Themes may still read any
menus.<menu_id>present in preview-data, whether or not that id is declared here
Recommended slot ids:
primaryfooterdocs-sidebar
Validation rules:
menuSlotsmust be an object if present- it must contain at least one slot when present
- each slot id must use lowercase letters, digits, and internal hyphens only
- each slot id must be 1-32 characters
- each slot definition must contain
title - slot definitions may optionally include
description - unknown properties inside a slot definition are invalid
3.2 Menu Slots vs Template Slots
Menu slots are distinct from template slots.
- Template slots are placeholders inside HTML templates, such as
{{slot:content}} - Menu slots are helper metadata declarations inside
theme.json - Template slot names and
menu_idvalues are separate namespaces, so values likefootermay appear in both without conflict
Defining menuSlots does not change template rendering rules or allowed template slot names.
3.2 widgetAreas
widgetAreas is an optional object that declares which widget_area_id values a theme commonly expects.
- Keys are stable identifiers such as
sidebar,header, ordocs-sidebar - Values contain admin-facing helper metadata for widget area creation guidance
widgetAreasdoes not assign widgets to an areawidgetAreasdoes not filter or control build behavior- Themes may still read any
widgets.<widget_area_id>present in preview-data, whether or not that id is declared here
Recommended area ids:
sidebarheaderdocs-sidebar
Validation rules:
widgetAreasmust be an object if present- it must contain at least one area when present
- each area id must use lowercase letters, digits, and internal hyphens only
- each area id must be 1-32 characters
- each area definition must contain
title - area definitions may optionally include
description - unknown properties inside an area definition are invalid
3.3 Theme Helper Metadata vs Template Slots
Theme helper metadata is distinct from template slots.
- Template slots are placeholders inside HTML templates, such as
{{slot:content}} menuSlotsandwidgetAreasare helper metadata declarations insidetheme.json- Template slot names,
menu_idvalues, andwidget_area_idvalues are separate namespaces, so values likeheaderorfootermay appear in multiple places without conflict
Defining menuSlots or widgetAreas does not change template rendering rules or allowed template slot names.
4. Template Rules
layout.htmlmust include exactly one{{slot:content}}.- Allowed slot names:
content,header,footer,meta. - Nested slot expressions are not allowed.
<script>tags are not allowed inlayout.html.- Mustache block syntax is not allowed (
{{#...}},{{/...}}).
5. Variables and Rendering
- Only simple variable substitution is supported.
- Rendering is designed for build-generated data injection.
- JavaScript is for progressive enhancement, not core rendering.
- Preview-data stays canonical and data-only; build tooling computes theme-facing render data immediately before template rendering.
- Runtime data may include richer HTML-ready fields on post objects.
{{post.comments_html}}is supported as an optional rendered HTML fragment for post comment sections.{{menu:<menu_id>}}is supported as an optional rendered HTML fragment for menu trees, for example{{menu:primary}}.
6. Security and Packaging Rules
- Tooling does not execute theme code.
- Path escape is forbidden.
- Symlink-based root escape is forbidden.
- Distribution unit is a ZIP archive.
Commonly excluded files:
.gitnode_modulesdist*.log__MACOSX,.DS_Storepackage-lock.json,pnpm-lock.yaml,yarn.lock,bun.lockb
7. Validation Profile
Errors:
- Missing
theme.jsonor invalid JSON - Missing required templates (
layout.html,index.html,post.html,page.html) - Missing
assets/style.css - Missing required manifest fields
- Invalid
versionsemver - Invalid
license runtimenot equal to"0.3"- Invalid
namespaceorslug - Invalid
menuSlotstype or empty object - Invalid
widgetAreastype or empty object - Invalid menu slot identifiers
- Invalid menu slot definition shape
- Invalid widget area identifiers
- Invalid widget area definition shape
- Unknown properties inside a menu slot definition
- Unknown properties inside a widget area definition
layout.htmlslot rule violations- Disallowed template syntax patterns
Warnings:
- Missing optional templates:
archive.html,category.html,tag.html post.htmlmissing{{post.comments_html}}{{post.comments_html}}used outsidepost.html
Runtime behavior:
- Required preview-data route arrays do not guarantee emitted artifacts for optional route kinds.
archive,category, andtagpages are emitted only when both renderable route data exists and the matching template exists.404.htmlis emitted only when the theme provides a404.htmltemplate.
8. CLI Alignment
npx zeropress-theme validate, pack, and dev use this runtime contract as the baseline.
9. Compatibility Notes
v0.3is the only accepted manifest contract for new validation and upload flows.v0.2manifests are historical and are no longer accepted by validator-backed upload/submission paths.
10. Toolkit Baseline
- Node.js:
>= 18.18.0 - ESM only
- CommonJS is not supported
11. Normative vs Informative Summary
| Item | Classification | Notes |
|---|---|---|
theme.json, layout.html, index.html, post.html, page.html, assets/style.css |
Normative (Required) | Missing files produce validation errors |
theme.json.name, theme.json.namespace, theme.json.slug, theme.json.version, theme.json.license, theme.json.runtime |
Normative (Required) | Must be present and valid |
theme.json.author |
Informative (Optional) | Optional package display metadata |
theme.json.description |
Informative (Optional) | Recommended metadata for theme clarity |
theme.json.menuSlots |
Informative (Optional) | If present, it must satisfy the runtime schema |
theme.json.widgetAreas |
Informative (Optional) | If present, it must satisfy the runtime schema |
layout.html must contain exactly one {{slot:content}} |
Normative (Required) | Validation error if violated |
Allowed template slots: content, header, footer, meta |
Normative (Required) | Unknown slots are invalid |
No <script> in layout.html |
Normative (Required) | Validation error |
Mustache block syntax forbidden ({{#...}}, {{/...}}) |
Normative (Required) | Validation error |
| Path/symlink root escape forbidden | Normative (Required) | Validation error |
archive.html, category.html, tag.html |
Informative (Recommended) | Missing files produce warnings |
partials/ directory |
Informative (Optional) | Supported but not required |
assets/theme.js |
Informative (Optional) | Optional file; not auto-executed |