Parents, theme_asset(), and the storefront asset boundary.
Inheritance
The parents array lists ancestor theme slugs (furthest parent first). Twig loader order:
- Core
templates/ - Active theme
views/ - Each parent from nearest to furthest
First matching template wins—child overrides parent.
theme_asset()
<link rel="stylesheet" href="{{ theme_asset('css/app.css') }}" />
<script src="{{ theme_asset('js/main.js') }}" defer></script>
URLs resolve under /theme-assets/… from the active theme's assets/ directory (with parent fallback rules).
Storefront asset boundary
Applies to all customer-facing paths: pages, content types, taxonomies, theme home.
Rules
- Core marketing shell (
public/root.twig) may use/css/styles.cssand core JS. - Theme storefront must use
theme_asset()only—never link/css/styles.cssfromthemes/*/views/**. - Do not rely on marketing-only CSS class names without defining them in the theme.
Correct vs incorrect
| Correct | Incorrect |
|---|---|
{{ theme_asset('css/app.css') }} in theme layout | <link href="/css/styles.css"> in content template |
Why it matters
- Portable themes swappable without marketing CSS coupling
- White-label and commercial theme packages stay self-contained
- Content archives stable when core marketing styles change
Layout lint
composer lint:twig-layouts
composer lint:twig-layouts --strict
Runs during composer test.
Homepage
GET / renders page/home.twig in the active theme unless Admin assigns a CMS page as public homepage.