Examples
The patterns this site is built from. Each one is a few lines of ordinary HTML or Markdown. Complete example sites, with their source, are on unify.fwdslsh.dev.
A page overriding one region
The layout declares a footer slot with its default content:
<footer><slot name="footer"><p>© My Site</p></slot></footer>
A page replaces it with one attribute on a top-level element. Every other page keeps the default:
<!doctype html>
<html>
<head><title>Contact</title></head>
<body>
<h1>Contact</h1>
<p slot="footer">© My Site · <a href="mailto:hi@example.com">email us</a></p>
</body>
</html>
A section with its own chrome
Drop src/blog/posts/_layout.html in place and every post gets it. The shared head, nav and footer come from the same fragments the root layout uses, so nothing is written twice:
<!doctype html>
<html lang="en">
<head>
<include src="/_includes/base/head.html"></include>
<title>· fwdslsh</title>
<meta property="og:type" content="article">
<meta name="schema" content="BlogPosting">
</head>
<body>
<include src="/_includes/base/nav.html"></include>
<main id="main"><article class="post"><slot></slot></article></main>
<include src="/_includes/base/footer.html"></include>
</body>
</html>
The schema meta in the layout declares every post a BlogPosting at once. That is what puts them in the feed and gives each one a JSON-LD block.
A Markdown post
Pure Markdown with frontmatter. The date carries a time, because a feed entry needs an instant and unify won't invent one:
---
title: How this site is built
description: Every page is plain HTML or Markdown, composed by unify at build time.
author: fwdslsh
date: 2026-10-02T09:00:00Z
---
# How this site is built
This site is built with unify, one of our own tools...
author has no special meaning to unify. It becomes <meta name="author">, and the post list below reads it back for the byline.
A card with slots
A fragment declares named slots and a bare one:
<!-- _includes/card.fragment.html -->
<article class="card">
<span class="card-icon"><slot name="icon"></slot></span>
<h3><slot name="title"></slot></h3>
<slot></slot>
</article>
A non-empty include fills them. This works in Markdown too, as long as the include starts a line and has no blank lines inside:
<include src="/_includes/card.fragment.html">
<img slot="icon" src="/assets/icons/signpost.svg" alt="" width="20" height="20">
<a slot="title" href="/rabit/index.html">rabit</a>
<p>A small JSON manifest that tells agents what a site holds and where.</p>
</include>
A generated post list
unify has no collections, so the blog index comes from a script. src/unify.yaml holds the flags every command shares:
pretty-urls: true
base-url: https://fwdslsh.dev/
canonical: auto
generate: _scripts/gen.mjs
source-inventory: true
Before each build unify runs the script with an overlay directory and a context file. The context names a JSON inventory of every source page, so the script filters, sorts and writes a fragment without parsing a single file:
const [, , , overlay, contextPath] = process.argv;
const context = JSON.parse(readFileSync(contextPath, "utf8"));
const inventory = JSON.parse(readFileSync(context.inputs.sourcePages, "utf8"));
const posts = inventory.pages
.filter((p) => p.source.startsWith("blog/posts/"))
.sort((a, b) => Date.parse(b.date) - Date.parse(a.date));
const items = posts.map((p) => `<li><a href="${p.href}">${p.title}</a></li>`);
mkdirSync(join(overlay, "_includes"), { recursive: true });
writeFileSync(join(overlay, "_includes", "post-list.html"), `<ol>${items.join("")}</ol>`);
blog/index.md then includes /_includes/post-list.html like any other fragment. The overlay and src/ share one path space, and the generated fragment never touches src/.
Saving the flags
Run the build once with the flags you want and --save-config, and unify writes them into src/unify.yaml for every later command:
unify build --pretty-urls --base-url https://example.com/ --canonical auto --save-config
unify dev # same flags, from the file
The full source of this site is on GitHub, and the blog post walks through it.