Back to Help

Layout recipes

May 17, 2026

Copy-paste starting points for custom layouts — an article page, a list home, an asset page, shared header/footer partials, and one universal layout that covers all three surfaces.


Working starting points. Paste one into a new layout, hit Preview, then make it yours. Every recipe uses only the documented tokens — see the token reference for what each one does, and the overview for how to create and bind a layout.

All of these put {{{theme.cssBlock}}} in the <head> so the channel palette flows through as --brand-* CSS variables. Change the look by editing the palette in Default-appearance mode, not by hard-coding colours here.

1. Article / page detail

Bind this as the default article/page layout, or pick it on a single article.

<!doctype html>
<html lang="{{site.cultureCode}}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{article.title}} — {{channel.name}}</title>
  {{{theme.cssBlock}}}
  <style>
    body { margin: 0; background: var(--brand-surface); color: var(--brand-text);
           font-family: var(--brand-font-body); }
    main { max-width: 720px; margin: 3rem auto; padding: 0 1rem; line-height: 1.7; }
    h1 { font-family: var(--brand-font-heading); font-size: 2.25rem; }
    .byline { color: var(--brand-muted); margin-bottom: 2rem; }
    img { max-width: 100%; height: auto; }
    a { color: var(--brand-primary); }
  </style>
</head>
<body>
  {{> site-header}}
  <main>
    <h1>{{article.title}}</h1>
    <p class="byline">
      {{#if article.author.name}}By {{article.author.name}} · {{/if}}{{article.publishedAt}}
    </p>
    {{#if article.coverImage}}<img src="{{article.coverImage}}" alt="">{{/if}}
    {{{article.body}}}
  </main>
  {{> site-footer}}
</body>
</html>

2. Channel home (article list)

Bind this as the Channel home layout.

<!doctype html>
<html lang="{{site.cultureCode}}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{channel.name}}</title>
  {{{theme.cssBlock}}}
  <style>
    body { margin: 0; background: var(--brand-surface); color: var(--brand-text);
           font-family: var(--brand-font-body); }
    main { max-width: 960px; margin: 2rem auto; padding: 0 1rem; }
    .grid { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fill,minmax(280px,1fr)); }
    .card { border: 1px solid var(--brand-muted); border-radius: 8px; overflow: hidden; }
    .card img { width: 100%; display: block; }
    .card .pad { padding: 1rem; }
    .card h2 { font-family: var(--brand-font-heading); font-size: 1.15rem; margin: 0 0 .5rem; }
    .card a { color: var(--brand-text); text-decoration: none; }
  </style>
</head>
<body>
  {{> site-header}}
  <main>
    <div class="grid">
      {{#each articles}}
        <article class="card">
          {{#if coverImage}}<a href="{{url}}"><img src="{{coverImage}}" alt=""></a>{{/if}}
          <div class="pad">
            <h2><a href="{{url}}">{{title}}</a></h2>
            {{#if excerpt}}<p>{{excerpt}}</p>{{/if}}
          </div>
        </article>
      {{/each}}
    </div>
  </main>
  {{> site-footer}}
</body>
</html>

3. Asset page

Bind this as the Asset page layout.

<!doctype html>
<html lang="{{site.cultureCode}}">
<head>
  <meta charset="utf-8">
  <title>{{asset.title}} — {{channel.name}}</title>
  {{{theme.cssBlock}}}
  <style>
    body { margin: 0; background: var(--brand-surface); color: var(--brand-text);
           font-family: var(--brand-font-body); text-align: center; }
    main { max-width: 900px; margin: 2rem auto; padding: 0 1rem; }
    img, video { max-width: 100%; height: auto; border-radius: 8px; }
  </style>
</head>
<body>
  {{> site-header}}
  <main>
    <h1>{{asset.title}}</h1>
    {{#if asset.isImage}}<img src="{{asset.url}}" alt="{{asset.title}}">{{/if}}
    {{#if asset.isVideo}}<video src="{{asset.url}}" controls></video>{{/if}}
    {{#if asset.description}}<p>{{asset.description}}</p>{{/if}}
  </main>
  {{> site-footer}}
</body>
</html>

4. Shared header & footer partials

All three recipes above reference {{> site-header}} and {{> site-footer}}. Create two partials so the chrome is written once. (The partial Id is derived from the name — "Site header" becomes site-header.)

Partial: Site header

<header style="padding:1rem;border-bottom:1px solid var(--brand-muted)">
  <a href="/{{site.cultureCode}}/c/{{channel.slug}}"
     style="color:var(--brand-text);text-decoration:none;font-weight:600">
    {{#if channel.logo}}<img src="{{channel.logo}}" alt="" style="height:28px;vertical-align:middle"> {{/if}}
    {{channel.name}}
  </a>
</header>

Partial: Site footer

<footer style="padding:2rem 1rem;text-align:center;color:var(--brand-muted);font-size:.9rem">
  {{#if theme.footer.copyright}}{{theme.footer.copyright}}{{else}}© {{site.currentYear}} {{channel.name}}{{/if}}
</footer>

5. One universal layout for every surface

Because missing tokens render empty, a single layout can cover all three surfaces by branching on what data is present. This is essentially the skeleton a new layout starts from — bind the same layout to Channel home, Asset page, and the default article layout, and it does the right thing on each.

<!doctype html>
<html lang="{{site.cultureCode}}">
<head>
  <meta charset="utf-8">
  <title>{{channel.name}}</title>
  {{{theme.cssBlock}}}
</head>
<body>
  {{> site-header}}
  <main style="max-width:820px;margin:2rem auto;padding:0 1rem">

    {{#if article}}
      <article>
        <h1>{{article.title}}</h1>
        {{{article.body}}}
      </article>
    {{/if}}

    {{#if articles}}
      {{#each articles}}
        <article style="padding:1rem 0;border-bottom:1px solid var(--brand-muted)">
          <h2><a href="{{url}}">{{title}}</a></h2>
          {{#if excerpt}}<p>{{excerpt}}</p>{{/if}}
        </article>
      {{/each}}
    {{/if}}

    {{#if asset}}
      <h1>{{asset.title}}</h1>
      {{#if asset.isImage}}<img src="{{asset.url}}" alt="{{asset.title}}" style="max-width:100%">{{/if}}
      {{#if asset.isVideo}}<video src="{{asset.url}}" controls style="max-width:100%"></video>{{/if}}
    {{/if}}

  </main>
  {{> site-footer}}
</body>
</html>
Try it.
  1. Create the two partials (Site header, Site footer) first — recipes 1–3 reference them.
  2. Create a layout, paste recipe 5, Preview. The sandboxed preview ships sample article + list + asset data, so all three branches render at once.
  3. Bind it as Channel home and as the default article layout. Visit the home and an article — each shows only its relevant branch with real data.
  4. Swap in recipes 1–3 as separate layouts when you want each surface tuned individually instead of one shared template.

See also

channels custom-layouts recipes handlebars examples