Theme anatomy
Inside themes/default/ — name every file purpose.
Now you'll open every file in themes/default/ and name what it does. By the end of this page you should be able to walk a stranger through the theme directory like it's your own.
The directory shape, again — but with reasons
- Templates (the rendering contract) live next to
index.json. - Samples (the buyer pages) live under
master/sample/. - Assets (images) live under
master/assets/.
That's the entire theme surface.
index.json — the manifest
Open themes/default/index.json:
name— the dir name (must match the folder).title— display label for the gallery + theme picker. Buyers see this string.pages— which top-level page-template families this theme provides. Most themes shipPage(the layout for emails / landing pages) and optionallyForm(the layout for checkout / signup forms).templates— an array listing every<Name>.template.htmlfile the theme ships. Used by the engine to validate what samples can use.formats+configData— baseline values applied to every page that uses this theme (default text colour, default button colour, default container width, etc.). Per-page overrides win.
You can ignore the rest at first. The five above are enough to register a theme.
Templates — the visual contract
Every element type the engine surfaces has a corresponding <Name>.template.html. The shape:
- The element's own JSON node (
src,alt,url, etc. become local vars) - The element's
formatsobject (under the localformatsvar) - A small set of theme-wide globals (
MEDIA_URL,THEME_CONFIG_DATA, etc.)
Two template categories you'll see:
- Element templates —
P,Heading,Image,Button,Divider,List, … one per leaf element class. These are the bulk of the templates dir. - Container templates —
Page,Block,Grid,Cell,Form. These define how their children are stacked. The shipped Page template is wrapped for email-client compatibility (table-based layout, MSO conditionals).
Every template is < 200 LOC; most are 30-80 LOC. You can read all 30 in an afternoon.
Samples — the buyer pages
master/sample/email/ holds the email samples. master/sample/page/ holds the landing-page + checkout samples. Each sample is one .json file plus a thumbnail (.png for full-bleed previews, .svg for placeholder layouts) and an optional .html rendering for offline preview.
Pick any sample and open its JSON — you'll recognise the shape from JSON structure. The Minimal.json sample is the smallest meaningful example (≈ 250 lines); WelcomeCustomers.json and SitewideSale.json are richer (~1000 lines). Pick by your appetite.
Assets — content images
master/assets/image/email/ and master/assets/image/page/ hold every content image used by samples — hero photos, product shots, social-icon glyphs, brand glyphs. The shipped images are AI-generated for a clean royalty-free shipping story (RULE-H1).
When a sample's ImageElement references "src": "master/assets/image/email/logo.png", the engine resolves it as MEDIA_URL + 'master/assets/image/email/logo.png'. MEDIA_URL is the fourth global in the bundle — for the shipped demo it's /themes/default/, so the final URL is /themes/default/master/assets/image/email/logo.png.
Replace any image by dropping a file with the same path + name. The JSON pointing at it doesn't change.
The 8 hard rules every theme must follow
Distilled from THEME_BEST_PRACTICE.md (which is 2800 LOC of rules + rationale; you don't need to read it cover-to-cover, but you will hit it eventually):
- No copyrighted images, ever (RULE-H1). Use the shipped AI library, generate your own via the included
scripts/core/gen-images.mjs, or buy royalty-free. - Hierarchy is sacred (RULE-H2). Page → Block → Element. Page → Block → Grid → Cell → Block → Element. Don't skip layers.
- No hardcoded image sizes except logos + icons (RULE-H3). Content imagery is responsive; only fixed assets like logos / glyphs declare pixel widths.
- Aspect-lock on by default (RULE-H4). Image elements preserve aspect ratio unless the buyer explicitly overrides.
- 4-colour palette cap (RULE-H8). One sample uses at most 4 distinct colours (background, foreground, accent, muted). More than 4 reads as Geocities.
- 5-typography-tier cap (RULE-H10). H1 / H2 / H3 / Body / Caption. Anything beyond is rejected at the audit.
- No margin on
BlockElement(RULE-H11). Vertical rhythm comes from Block'spadding_top+padding_bottom. Margin collapses unpredictably in email clients. - Explicit units in dimensions (RULE-H12).
"width": 600is pixels; never"width": "100%"in aformatsfield. Percentages live elsewhere (cell widths, container queries) — but a leaf element's width is always typed.
The rules feel pedantic until you've shipped a sample that breaks one and watched it render differently in Gmail vs Outlook. Then they read as protection.
The 5 templates you'll touch most
If you fork the default theme, these are the files most buyers customise:
Page.template.html— the outer page wrapper. Where you put your brand's outermost background, container width, fixed header.Heading.template.html— your brand's headline typography (font, weight, line-height).P.template.html— your brand's body typography.Button.template.html— primary CTA shape (border radius, padding, hover state).Image.template.html— usually fine as-is; touch only if you need brand-specific image treatments (rounded corners, drop shadows, polaroid frames).
Almost every other template inherits visual decisions from these five via formats.<key> defaults in the buyer's brand-customised index.json block.
Did you make it?
You should now be able to:
- ✅ Open
themes/default/, name every top-level dir +index.json's purpose without re-reading. - ✅ Read an EJS template and predict the rendered HTML for a given JSON node.
- ✅ State the 8 hard rules and explain why each one exists.
- ✅ Identify the 5 templates a brand fork would touch first.
What's next?
- → Create a sample ⭐ — the headline lock-step copy-along; you'll author a new email and see it in the gallery in 30 minutes.
- 🔗 file-tree of
demo/themes/default/. - 🔗
THEME_BEST_PRACTICE.mdfor the long-form authoring rules + audit matrix.
Stuck?
- 🐛 grep
themes/default/<TemplateName>.template.htmlfor the exact EJS contract. - 🐛 the
example-minitheme dir is the smallest possible theme — open it for a 5-file reference of what's required vs nice-to-have.