Basker Docs

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

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.liquid

Theme 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>

Tenant info, navigation, and global content:

<footer class="site-footer">
  <div class="site-footer__inner">
    <p class="site-footer__copyright">
      &copy; {{ '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.liquid that 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 emptynavigation() returns pages marked as visible in navigation. Confirm pages have Show in navigation enabled in the admin.

On this page