---
title: "Architecture"
space: "Commera"
url: "https://docs.bwh.tech/commera/theme-reference/architecture"
updated: "2026-10-08"
---

A Commera theme has three parts: a **Shop Theme** record, a theme folder with templates, and a public folder with assets. All three are in the Frappe app that owns the record's **Module**.

## Folder layout

The folder name is the theme name in lowercase, with spaces and hyphens changed to `_`. For `Acme Catalog`, it is `acme_catalog`. Two themes cannot have the same folder name, so `My-Theme` and `My Theme` cannot both exist.

| Path in your app | What it holds | Served to the browser |
| --- | --- | --- |
| `<app>/themes/<folder>/components/` | The layout, includes, macros and sections that pages use | No |
| `<app>/themes/<folder>/pages/` | One `.html` per page, each with an optional `.py` controller | No |
| `<app>/themes/<folder>/styles/` | CSS sources for a Tailwind build | No |
| `<app>/themes/<folder>/tailwind.input.css` | The Tailwind entry file, for a Tailwind theme only | No |
| `<app>/public/themes/<folder>/` | CSS, scripts, images, fonts and a Tailwind output file | Yes, at `/assets/<app>/themes/<folder>/` |
| `<app>/<module>/shop_theme/<folder>/<folder>.json` | The theme record | No. `bench migrate` imports it. |

The first save of a new record in developer mode makes these empty folders: `components/includes`, `components/macros`, `pages` and `styles` in the theme folder, and `images` and `scripts` in the public folder. Add other folders under `components/` as you need them, for example `sections/` and `overlays/`.

There is no manifest file. The Shop Theme record holds the theme's name, parent and settings.

## The Shop Theme record

| Field | What it does |
| --- | --- |
| Theme Name | The record's name, and the source of the folder name. Letters, digits, spaces, `-` and `_` only. |
| Module | The module whose app holds the theme's files and the record's JSON. Set it before the first save. |
| Is Standard | Ships the record with the app. Commera writes the record's JSON only when this is ticked, and never deletes a standard theme's folders. |
| Parent Theme | The theme that gives every file this theme does not have |
| Theme Settings | A Single DocType with the merchant's settings for this theme. Use **Scaffold Theme Settings** on a saved record to make one. |
| Config | Not used. Leave it empty. |

Commera does not allow a parent chain that loops back to the same theme.

## The theme chain

The chain starts at the active theme and follows **Parent Theme** to the top. For Acme Catalog, the chain is:

1. Acme Catalog, in the `acme_theme` app
2. Summer Theme, in the Commera app
3. Shop Base Theme, in the Commera app

Each theme in the chain can be in a different app.

## Fallback order

| What | Where Commera looks | If no theme has it |
| --- | --- | --- |
| A page, such as `pages/products/details.html` | Each theme in the chain, child first | Commera's core page serves the URL |
| A page controller, such as `pages/products/details.py` | Each theme in the chain, child first. The controller and the page can come from different themes. | The page renders without a controller |
| A `components/...` file that a template extends, includes or imports | Each theme in the chain, child first, then Commera's core templates | Jinja raises a "template not found" error |
| Any other template name, such as `templates/...` | Frappe's normal template loader, which searches every installed app. A theme folder cannot override it. A `templates/...` file in another app replaces Commera's file for every theme on the site, so do not add one. | Jinja raises a "template not found" error |
| An asset, through `shop_theme_asset_url()` | Each theme's public folder in the chain, child first | The helper returns the active theme's URL, so the 404 names your theme |

A child can override a page's `.html` and keep its parent's `.py`, or the reverse. Acme Catalog adds only `pages/products/details.py`. For that URL, Summer Theme's template renders with Acme Catalog's controller. For every other URL, Summer Theme's page and controller run.

:::caution[Import macros from components/, not templates/]

Only names that start with `components/` go through the theme chain. If your template imports `templates/macros/x.html`, it never gets your theme's copy.

:::

## What Shop Base Theme ships

Shop Base Theme has no pages. Activated alone, it changes no URL. It ships:

| File | What it gives |
| --- | --- |
| `components/base.html` | The HTML skeleton: the `lang` and `dir` attributes, SEO meta tags and JSON-LD, analytics tags, the favicon, the merchant's `--ls-*` colours, Alpine.js and its Persist and Collapse plugins, the cart, translation, wishlist and toast stores, the query string helper, and the toast |
| `components/macros/branding.html` | `brand_logo(css_class)` and `brand_name()` |
| `images/placeholder.svg` | An image for products without one |
| `scripts/vendor/alpinejs/` | Alpine.js and its plugins |
| `scripts/synced-query-params.js` | Keeps page state, such as the page number, in the query string |

The `base.html` blocks are listed in [Step 2: Add a layout](/commera/build-a-theme/add-a-layout).

## Next steps

- [Routes and pages](/commera/theme-reference/routes-and-pages)
- [Templates](/commera/theme-reference/templates)
- [Gotchas](/commera/theme-reference/gotchas)
