Layouts
moat ships with a built-in layout that uses oat for styling — sidebar nav, dark mode toggle, responsive topnav. You don't need to create any layout files to get started.
To customize, create _layout.html in your docs directory. It completely replaces the built-in layout.
Built-in layout
The built-in layout provides:
- oat CSS from CDN
- Sidebar navigation with collapsible sections
- Dark/light theme toggle
- Responsive topnav with sidebar toggle on mobile
- Syntax highlighting CSS (
_syntax.css) [[links]]from config rendered above the page nav- Footer from
[extra].footerin config (supports HTML) - Landing page variant for
layout: landingpages
Run moat init docs to get a copy of the built-in layout you can edit.
Custom layout
Create _layout.html with Go template syntax. Use {{ block "name" . }} to define sections that variants can override:
<!DOCTYPE html>
<html>
<head>
{{ block "title" . }}<title>{{ .Title }}</title>{{ end }}
<link rel="stylesheet" href="https://unpkg.com/@knadh/oat/oat.min.css">
<link rel="stylesheet" href="{{ .BasePath }}/_syntax.css">
{{ block "head" . }}{{ end }}
</head>
<body>
<nav>{{ .Nav }}</nav>
<main>
{{ block "content" . }}
<article>{{ .Content }}</article>
{{ end }}
</main>
</body>
</html>
The {{ block "name" . }}...{{ end }} sections have default content that variants can replace.
Named variants
Create _layout.{name}.html files that override blocks from the base. Only redefine what you need — everything else comes from the base layout.
_layout.landing.html:
{{ define "title" }}{{ .SiteName }} — Welcome{{ end }}
{{ define "head" }}
<style>
.hero { margin-bottom: 3rem; }
</style>
{{ end }}
{{ define "content" }}
<section class="hero">{{ .Content }}</section>
{{ end }}
Use it in a page's frontmatter:
---
title: Home
layout: landing
---
Variant files only contain {{ define }} blocks. The base layout provides everything else — nav, head, scripts, etc. Change the base once, all variants inherit.
If you don't provide a custom _layout.html, the built-in layout is used as the base, and you can still add variants that override its blocks.
Template variables
| Variable | Type | Description |
|---|---|---|
{{ .Title }} |
string | Page title |
{{ .Description }} |
string | Page description |
{{ .Content }} |
HTML | Rendered markdown content |
{{ .Nav }} |
HTML | Generated navigation sidebar |
{{ .CurrentPath }} |
string | Current page URL path |
{{ .SiteName }} |
string | Site name from config or CLI |
{{ .BasePath }} |
string | URL prefix (e.g. /moat) |
{{ .Extra }} |
map | Extra frontmatter from the page |
{{ .Site }} |
map | Site-level [extra] from config |
Template functions
| Function | Description |
|---|---|
safeHTML |
Renders a string as raw HTML (use for trusted config values like footer) |
Navigation HTML
{{ .Nav }} outputs a <nav> with nested <ul> lists:
[[links]]from config are rendered first as<li>items- Top-level pages are direct
<li>items - Directories become collapsible
<details>sections - The current page gets
aria-current="page"
Static assets
Put CSS, images, and other static files in _static/:
<link rel="stylesheet" href="/_static/style.css">
<img src="/_static/logo.png">
The _static/ directory is copied to the output as-is during build.
How inheritance works
Go templates use block and define:
- The base layout uses
{{ block "content" . }}default markup{{ end }} - A variant uses
{{ define "content" }}replacement markup{{ end }} - moat clones the base, parses the variant into the clone —
defineoverridesblock - If a variant doesn't define a block, the base's default is used
This is Go's native template mechanism — no custom template engine.