These guides take you from zero to a working theme, following the
conventions of themes/default, the reference theme this documentation
models every example on. Read Themes and
Jinja language first if you haven’t.
Prerequisites
- Rust toolchain (for
ringroad-rendererCLI), or just the repo, since the API loads themes at startup. - The concepts: nodes (the unit of editing), templates (whole-page
layouts), snippets (macros), theme.json (manifest), and the
four jinja extensions (
@editor:markers,{% schema %},{% stylesheet %},{% javascript %}).
The guides
| Guide | Covers |
|---|---|
| Theme from scratch | Directory skeleton, theme.json, layout, first node, the whole boot sequence |
| Writing a node | Node anatomy: markup, schema, asset blocks; section vs simple; slots and children strategies |
| Templates and layout | The page shell, page templates, snippets/macros, and the asset pipeline |
| Storefront data | What data is available on each page type, the extra.* contract, cart/checkout/account |
Golden rules
- A node file is self-contained. Markup, schema, CSS, and JS for one node type live in one file. Name the file after the type.
- Never fight the wrapper. The renderer wraps your node in
rr-node rr-node--{type}with an id; style through it. - Settings always have values. The engine merges schema defaults at
render time, no
| default()guards needed for declared settings. - Tokens, not hardcoded colors. The default theme styles exclusively
through
var(--color-*)/var(--radius-*)/var(--spacing-*)custom properties the layout emits from global settings. data-*attributes for behavior. The default theme’smain.jsdrives every interaction fromdata-*hooks; node JS blocks only add per-node behavior scoped via$$node_id$$.- Snippets are pure. Macros render from their arguments; keep request state out of them.
- Stubs beat 500s. Templates and nodes degrade gracefully: optional
data renders empty (Lenient undefined),
render_node/render_textnever raise,{% include … ignore missing %}for optional snippets.