Inheritance allows for extending a template by simply including it into another one and overriding only the parts that have to be different. In order to change details in a derived template, it is important to understand how snippets work and how they can be used to override parts of a template on an atomic level.
Extending Templates
The basic idea is quite simple. A base template contains a snippet definition for every element that is supposed to be overridden by a derived template. Let's assume the following file structure of a theme:
packages/
yournamespace/
yourtheme/
base.php
derived.php
The base template could look as follows:
<# base.php #>
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<nav>...</nav>
<@ snippet article @>
@{ +main }
<@ end @>
<@ article @>
</body>
</html>
As you can see, the main content is wrapped in a snippet called article, which is called right after its definition in order to generate the actual output. This allows for easily creating a derived version of that template with a different article body by overriding the snippet after including the base template:
<# derived.php #>
<# Include the base template first. #>
<@ base.php @>
<# Now override the article snippet. #>
<@ snippet article @>
@{ +hero }
<hr>
@{ +main }
<@ end @>
A page using the derived template now has two block fields — +hero and +main.
Example
The Standard Lite theme that ships with Automad makes heavy use of inheritance. Its templates are tiny files that only include a shared page component. The layout of that page component calls a hero snippet, which is defined as an empty placeholder by default:
<# components/hero.php #>
<@~ snippet hero @><@ end ~@>
<@ hero @>
Templates with a hero section include the pageWithHero.php component instead, which extends the regular page and simply overrides the placeholder:
<# components/pageWithHero.php #>
<@~ page.php ~@>
<@~ snippet hero ~@>
<section class="std-layout__hero std-content">@{ +hero }</section>
<@~ end ~@>
Process Flow
In order to work efficiently with template inheritance, it is essential to understand the process flow of the template engine and its limitations. Under the hood, a template is processed twice. In a first run, all snippet definitions are registered before the template is actually rendered. Therefore it is possible to call a snippet before it is defined. However, the snippet itself is only evaluated during rendering in a second step. Due to that behavior, it is important to define overrides of nested snippets on the top level of a template. Let's have a look at an example:
<# Define and evaluate "main". #>
<@ snippet main ~@>
Original content ...
<@~ end @>
<@ main @>
<# Now override "main". #>
<@ snippet main ~@>
New content ...
<@~ end @>
<# The rendered output is "New content ..." #>
That was easy. Now let's try to nest snippets:
<@ snippet main ~@>
Original content ...
<@~ end @>
<@ main @>
<# Override "main" and add the nested snippet "nested". #>
<@ snippet main ~@>
<@ snippet nested @>
Nested ...
<@ end @>
<@~ nested ~@>
<@~ end @>
<# The rendered output is now "Nested ..." #>
But what if we also want to override nested?
<@ snippet main ~@>
Original content ...
<@~ end @>
<@ main @>
<@ snippet main ~@>
<@ snippet nested @>
Nested ...
<@ end @>
<@~ nested ~@>
<# Trying to override "nested" inside of "main". #>
<@~ snippet nested ~@>
Nested override ...
<@~ end @>
<@~ end @>
The example above fails, since the override of nested is only evaluated during rendering. In order to make it work, the override of nested has to be moved to the top level:
<@ snippet main ~@>
Original content ...
<@~ end @>
<@ main @>
<@ snippet main ~@>
<@ snippet nested @>
Nested ...
<@ end @>
<@~ nested ~@>
<@~ end @>
<# Override "nested" on the top level. #>
<@~ snippet nested ~@>
Nested override ...
<@~ end @>
<# The rendered output is now "Nested override ..." #>