<!-- demo:embed example="basic/3-custom-theme" -->

You've seen one theme already — the shipped `default` theme rendering the Minimal sample inside `/builder.php`. To put your brand on top of BuilderJS you'll customise this layer, not the engine. This page tells you what a theme *is*, when to author one, and when authoring just a sample is enough.

## What ships with a theme

A theme is a single folder under `demo/themes/<name>/`. It contains four kinds of files, and that's it:

- **Templates** (`*.template.html`) — one EJS template per element type. `Image.template.html` renders an `ImageElement`, `Button.template.html` renders a `ButtonElement`, and so on. The shipped `default` theme has 30+ templates covering every element the engine surfaces. They are the *visual contract* — change them and the rendered HTML changes.
- **Samples** (`master/sample/email/*.json` + `master/sample/page/*.json`) — pre-authored page trees ready to drop into `builder.load(themeJson, …)`. The default theme ships 70+ samples between email + page; every card on `/gallery.php` is one of them.
- **Assets** (`master/assets/image/`) — content images. The shipped library is 100% AI-generated photo + illustration imagery, royalty-free for any buyer use (RULE-H1 — no copyrighted images, ever). Replace with your brand photography by dropping files in this folder; the JSON `src` keys reference them by relative path.
- **Manifest** (`index.json`) — metadata: theme `name`, `title`, the `pages` array (which page templates apply), the `templates` array (which element templates ship), and an optional `_brand` block of brand tokens.

The whole theme directory is portable — copy it to another BuilderJS install and it works. No SQL migrations, no custom build step. That portability is why themes are the buyer customisation surface, not the engine.

## When to author a new sample vs a new theme

Short version:

- *I want one new email layout for my campaign* → **author a sample**. A new `.json` file under `themes/default/master/sample/email/<your-name>.json` plus a thumbnail, and the gallery picks it up.
- *I want my brand colours, my fonts, my logo, my domain-specific element layout* → **fork the default theme**. Copy `themes/default/` → `themes/<your-brand>/`, edit `index.json` + replace logos / fonts / templates / samples, register the new theme dir.

Most buyer projects start with the first and graduate to the second. The line crosses when a single sample isn't enough to express your brand — for example, when every email needs the same custom header that no shipped template renders, or when you need a primary colour that isn't in the shipped palette.

## Why themes are pluggable, not configurable

You might expect a theme to be "the same `default` templates plus a CSS override". BuilderJS goes further: a theme owns its templates **wholesale**. Two reasons:

1. **Templates control rendered HTML, not just styling.** A theme that wants `<h2 class="brand-headline">` instead of `<h1>` for a heading element changes one template, not a CSS file. That kind of structural override would be impossible with CSS-only theming.
2. **Email-client compatibility lives in templates.** The shipped templates are tuned for the Litmus 22-client matrix (Outlook, Apple Mail, Gmail web, …). When you author a custom theme, you inherit those compatibility decisions; when you fork a template you can re-tune for your audience.

The trade-off: you maintain the templates yourself. The shipped `default` theme is your reference; the `example-mini/` starter is what an author-from-scratch theme looks like (just `Page.template.html` + `Block.template.html` + a tiny manifest — under 100 LOC total before you customise it).

## The shipped `default` theme tour

Open `demo/themes/default/` and look at the layout:

```
themes/default/
├── index.json                       — manifest (name, templates list, page list)
├── *.template.html                  — 30+ EJS templates (the visual contract)
├── master/
│   ├── sample/
│   │   ├── email/                   — 50+ email layouts (Welcome, Newsletter, Receipt…)
│   │   └── page/                    — 20+ landing pages + checkout pages
│   └── assets/
│       └── image/
│           ├── email/               — content imagery used by email samples
│           └── page/                — content imagery used by page samples
└── others/                          — internal-only variant samples (not surfaced in the gallery)
```

`master/` is the **buyer surface** — every shipped sample under `master/sample/` appears in the gallery + builder. `others/` is for variants that aren't ready or shouldn't be marketed (work-in-progress samples, internal QA fixtures).

When `demo/builder.php` boots, the buyer-modifiable line is `$builderConfig['defaultTheme'] = 'default'` — change it to your fork name and every page in the demo loads your theme instead.

## Common buyer questions answered up front

> **Q: Can I have multiple themes side-by-side?** A: Yes. Drop a second theme dir under `themes/`, register it in `ThemeRegistry::SEED` (one row per theme), and `/gallery.php` shows both. The builder accepts a `?theme=<name>` query param so each rendered page can pin its theme.

> **Q: Can I share assets between themes?** A: Two patterns. (1) Use the shipped `default` theme's images by leaving JSON `src` paths pointing at `master/assets/image/...` — the engine resolves relative to the *active* theme but you can hard-link to a known-stable shared path. (2) Symlink the assets dir at the filesystem level — `themes/<your-fork>/master/assets → themes/default/master/assets`. Both work; the symlink approach is cleaner if your themes share imagery wholesale.

> **Q: Do I need to write EJS, or can templates be plain HTML?** A: They're EJS — `<%= var %>` and `<% if %>` blocks. But 90% of every shipped template is plain HTML; the EJS bits inject the JSON-driven values. Read three of the shipped templates and the pattern is obvious.

## Did you make it?

You should now be able to:

- ✅ State the smallest unit you can ship to add your brand to BuilderJS — answer: one new `.json` sample (or one new theme dir for a deeper change).
- ✅ Distinguish theme-level customisation from sample-level customisation.
- ✅ Open `themes/default/` and recognise each top-level dir's purpose.

What's next?

- → [Theme anatomy](/docs/05-theme-anatomy/) — open every file in `themes/default/` and learn what each one renders.
- 🔗 [`/gallery.php`](/gallery.php) — the 43-theme + 50+ sample browser.
- 🔗 [`examples/basic/3-custom-theme/`](/examples/basic/3-custom-theme/) — a runnable end-to-end demo that loads a custom theme.
