Layouts
Wrap every template with shared HTML — head, navigation, footer — using Basker's layout system
A layout is the HTML shell that wraps every template — <html>, <head>, <body>, header, footer. It's where the document structure, global navigation, asset loading, and head injections live.
Prerequisites
- A Basker theme directory with the required structure.
- Familiarity with HTML and Liquid template syntax.
Steps
1. Create the default layout file
Every theme must include layouts/default.liquid. This layout wraps all templates unless a template explicitly specifies a different one.
layouts/default.liquidTheme validation fails with an error if this file is missing.
2. Add the HTML document skeleton
The layout contains the full HTML document. Use {{ content_for_layout }} to mark where the rendered template output is injected.
<!DOCTYPE html>
<html lang="{{ locale() }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ page.title | default: tenant().name }}</title>
{{ content_for_header }}
{{ 'main.css' | asset_url | stylesheet_tag }}
</head>
<body class="{{ body_classes() }}">
{% render 'site-header' %}
<main id="main-content" role="main">
{{ content_for_layout }}
</main>
{% render 'site-footer' %}
{{ 'main.js' | asset_url | script_tag }}
</body>
</html>{{ content_for_layout }} is required — without it, the rendered template has nowhere to appear.
3. Inject head content with content_for_header
{{ content_for_header }} outputs system-injected content for the <head>:
- Preconnect hints for CDN domains.
- Generator meta tag.
- Preview-mode scripts and styles when active.
- reCAPTCHA scripts when forms use reCAPTCHA.
Place it after your own meta tags and before your stylesheets.
4. Load assets with asset_url and tag filters
Reference files from assets/ using asset_url. Pair with stylesheet_tag or script_tag to produce complete HTML tags.
{{ 'styles/reset.css' | asset_url | stylesheet_tag }}
{{ 'styles/main.css' | asset_url | stylesheet_tag }}
{{ 'scripts/navigation.js' | asset_url | script_tag }}asset_url generates a versioned URL pointing to the theme's asset storage. stylesheet_tag wraps it in <link>, script_tag wraps it in <script>.
5. Add CSS body classes with body_classes()
{{ body_classes() }} outputs CSS classes derived from the current template path — useful for template-specific styling.
<body class="{{ body_classes() }}">For templates/event.featured.liquid, this outputs:
<body class="page page-featured">6. Build navigation
Use navigation() to access the page tree. A snippet keeps it reusable:
{% assign nav = navigation() %}
<nav class="site-nav" aria-label="Main navigation">
<ul class="site-nav__list">
{% for item in nav %}
<li class="site-nav__item{% if item.has_children %} site-nav__item--parent{% endif %}">
<a href="{{ item.relativePath | default: item.url }}" class="site-nav__link">
{{ item.title }}
</a>
{% if item.has_children %}
<ul class="site-nav__submenu">
{% for child in item.children %}
<li class="site-nav__subitem">
<a href="{{ child.relativePath | default: child.url }}" class="site-nav__sublink">
{{ child.title }}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>7. Add the footer
Tenant info, navigation, and global content:
<footer class="site-footer">
<div class="site-footer__inner">
<p class="site-footer__copyright">
© {{ 'now' | date: '%Y' }} {{ tenant().name }}
</p>
</div>
</footer>Complete example
A full layouts/default.liquid for an arts venue site:
<!DOCTYPE html>
<html lang="{{ locale() }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{{ page.meta.description | default: '' }}">
<title>{{ page.title | default: tenant().name }} | {{ tenant().name }}</title>
{{ content_for_header }}
{{ 'styles/reset.css' | asset_url | stylesheet_tag }}
{{ 'styles/main.css' | asset_url | stylesheet_tag }}
</head>
<body class="{{ body_classes() }}">
<a href="#main-content" class="skip-link">Skip to content</a>
{% render 'announcement-bar' %}
{% render 'site-header' %}
<main id="main-content" role="main">
{{ content_for_layout }}
</main>
{% render 'site-footer' %}
{{ 'scripts/navigation.js' | asset_url | script_tag }}
</body>
</html>Result
You should have:
- A
layouts/default.liquidthat wraps all templates. - System content injected via
content_for_header. - Versioned asset loading via
asset_url,stylesheet_tag,script_tag. - Navigation rendered from the page tree.
- Template-specific body classes.
Verify by uploading and loading any page. View source to confirm content_for_header output is in the <head>.
Troubleshooting
"Missing layouts/default.liquid" — Theme validation requires this file. Create it in layouts/ with at least {{ content_for_layout }} in the body.
Template content doesn't appear — Confirm the layout includes {{ content_for_layout }} exactly once. That's the injection point.
Assets return 404 — Verify the file exists in assets/. Filenames are case-sensitive.
Navigation is empty — navigation() returns pages marked as visible in navigation. Confirm pages have Show in navigation enabled in the admin.
Related
- Templates — create the templates that render inside layouts.
- Template context — global functions and variables available in layouts.
- Rendering blocks — render content blocks inside templates.