Juicer
English

Theme inheritance

How a theme pulls in other themes via theme.toml, and how the full lookup chain is resolved.

A theme can build on other themes. It declares them in a theme.toml file at its own root:

themes/juicerdocs/
├── theme.toml          ← inherits = ["juicercommon"]
├── layouts/
├── partials/
└── static/
# themes/juicerdocs/theme.toml# themes/juicerdocs/theme.toml
inherits = ["juicercommon"]

When a site activates juicerdocs, juicer doesn’t just look in juicerdocs/ — it walks the inherits list too, splicing each named theme into the lookup chain right after the theme that named it. Partials, shortcodes, layouts, and static files all resolve against this expanded chain.

This is how the bundled themes share code: each one inherits juicercommon, the hidden base theme that holds the seo block, the dark-mode init/toggle scripts, and the common shortcodes (note, tip, warning, tabs, github, …). No theme duplicates them.

The lookup chain

Start from the site’s theme setting — a single name or an array — and walk each theme’s inherits depth-first. The result is one ordered chain. Resolution is first-wins: the earliest place a file is found is the one that’s used.

# site.toml# site.toml
theme = "juicerdocs"
# themes/juicerdocs/theme.toml# themes/juicerdocs/theme.toml
inherits = ["juicercommon"]

resolves to the chain:

site  >  juicerdocs  >  juicercommon

So a partials/seo.html in your site beats juicerdocs‘s, which beats juicercommon‘s. Drop your own partials/note.html into juicerdocs/ and it overrides the shared shortcode without touching juicercommon.

Inheritance composes with the site-level theme array. Both are resolved into the same chain — the array first, then each entry’s inherits:

# site.toml# site.toml
theme = ["my-overrides", "juicerdocs"]
site  >  my-overrides  >  juicerdocs  >  juicercommon

inherits is transitive: if juicercommon itself declared inherits = ["base"], base would land at the end of the chain.

Note

Precedence never changes: site files always win, then themes in chain order. Inheritance only makes the chain longer — it never lets a theme override the site.

Deduplication

A theme reached more than once — a diamond, where two themes both inherit a common base — appears in the chain once, at its earliest position:

# site.toml# site.toml
theme = ["themeA", "themeB"]   # both inherit "shared"# both inherit "shared"
site  >  themeA  >  shared  >  themeB

shared resolves once, kept at its first occurrence. A file defined in themeA, themeB, and shared is won by themeA (earliest in the chain).

Errors

Warning

A theme that lists a non-existent theme in inherits fails the build with a clear message:

theme 'juicerdocs' inherits unknown theme 'typo' — no directory at …

A cycleA inherits B inherits A — also fails, with the path traced so you can see where it closes:

theme cycle: themeA → themeB → themeA

A missing theme named at the site level (in site.toml‘s theme) is treated leniently — it’s simply skipped — matching juicer’s long-standing behavior. Only inherits links are validated strictly, because a theme author controls its own dependencies.

No theme.toml?

A theme with no theme.toml inherits nothing — it’s exactly equivalent to inherits = []. Themes written before this feature existed keep working byte-for-byte; inheritance is purely additive.

Tip

theme.toml currently understands a single key, inherits. It’s the one place themes declare their dependencies, so a theme is self-describing — you can read its theme.toml and know the whole chain it expects.

Search

Esc
to navigate to open Esc to close