Sheet 11 Revision 1.7.1

Content

Content authoring patterns and frontmatter conventions.

On this page

Frontmatter Schema

Every content file starts with YAML frontmatter:

---
layout: layouts/page.njk    # Required
title: Page Title           # Required
description: Meta desc      # Recommended
date: 2026-01-08           # Recommended
draft: false               # Optional
tags: [tag1, tag2]         # Optional
image: /path/to/og.jpg     # Optional
---

Layout Options

Layout Use Case
layouts/base.njk Raw HTML shell, rarely used directly
layouts/page.njk Standard content pages
layouts/docs.njk Documentation with sidebar TOC
layouts/post.njk Blog posts (if using blog)

Facts Not Yet Known

When a site is built before every fact is in hand (a role, a year, a contact email), the gap is marked in the data, never guessed. Keep the facts in one JavaScript data file and mark each gap with blank():

// src/_data/person.js
const blank = (hint) => ({ kind: "blank", hint });

export default {
  name: "Danny Caruso",
  contact: blank("public email"),
  credits: [{ show: "Bury the Dead", role: blank("role"), year: 2021 }],
};

Render every fact through one macro, so a gap shows as a marked gap:

{# src/_includes/macros/fact.njk #}
{% macro fact(value) -%}
{%- if value.kind == "blank" -%}<span class="blank">{{ value.hint }}</span>{%- else -%}{{ value }}{%- endif -%}
{%- endmacro %}

{% from "macros/fact.njk" import fact %}
<p>{{ fact(person.contact) }}</p>

Style .blank as a dashed tag with ::after { content: " to come"; }. The macro escapes facts like any other output, and filling a gap is a one-line change to the data file.

Markdown Content

Markdown files are processed with Nunjucks, so you can use template features:

# {{ title }}

Published on {{ date | readableDate }}

## Section Heading

Regular markdown content here.

{% include "partials/cta.njk" %}

Collections

Define collections in eleventy.config.js:

eleventyConfig.addCollection("posts", function (collectionApi) {
  return collectionApi
    .getFilteredByGlob("src/content/posts/**/*.md")
    .filter((item) => !item.data.draft)
    .sort((a, b) => b.date - a.date);
});

Data Cascade

Eleventy merges data from multiple sources (highest priority first):

  1. Frontmatter in the template
  2. Template-specific data file (page.11tydata.js)
  3. Directory data file (posts.json)
  4. Global data files (_data/*.json)