Each example below is a complete project in the repository's framework/example-configs/ directory, with pages and any OpenAPI files it needs. The test suite builds every one, and checks that the configs on this page match the files, so they stay correct. Start with the bundled minimal or api-docs project, then build it:
shoalrun init my-docs --starter minimal
cd my-docs
shoalrun build docs.config.yaml site-dist
shoalrun serve site-dist
Use --starter api-docs for the Cedar Parcel API example. The other examples remain available to copy from framework/example-configs/.
The notifications project demonstrates an optional cookbook chooser with email, push, and SMS choices. Its YAML maps supported selections to prebuilt recipe pages. It also includes unpublished ingredient and recipe drafts to show the authoring path. Build it from the repository root:
node framework/dist/cli.js build framework/example-configs/notifications/docs.config.yaml /tmp/notifications-site
node framework/dist/cli.js serve /tmp/notifications-site
The configuration reference explains every key.
Minimal site
One publication, page defaults so each page needs only an id, title, summary, and slug, and a two-level sidebar.
# The smallest useful site: one publication, two pages, a sidebar.
schemaVersion: 1
site:
title: Acme handbook
publications:
defaults: { channel: production, version: current, language: en }
channels:
production: { pathPrefix: "" }
versions:
current: { pathPrefix: "" }
enabled:
- { channel: production, version: current }
# Every page gets these values unless it sets its own.
frontmatter:
defaults:
type: guide
channel: production
version: current
access: { policy: public }
navigation:
- id: welcome
doc: guide.welcome
- id: setup
label: Setup
items:
- id: install
doc: guide.install
Parcel API documentation
The fictional Cedar Parcel API demonstrates enrollment shapes, parcel filters, several response formats, binary uploads, deprecation, and a delivery webhook. Its source matches the API reference styling lab. An overview and quickstart introduce the API; enrollment has an authored reference page and the other operations are generated. The complete spec, focused contracts, and API catalog are published. The Markdown twins get a short header, with operation IDs added on reference pages.
# Cedar Parcel API: guides, generated references, and agent-friendly contracts.
schemaVersion: 1
site:
title: Cedar Parcel API
origin: https://docs.cedar-parcel.example
# Hidden visually until focused; present in HTML for agents and screen readers.
agentDiscovery:
mode: focus
message: Use Markdown for complete examples and the linked focused API contracts.
header:
links:
- { label: Quickstart, href: /quickstart, style: primary }
footer:
columns:
- title: Start building
links:
- { label: Overview, href: / }
- { label: Quickstart, href: /quickstart }
- { label: Enrollment reference, href: /reference/create-enrollment }
text: Cedar Parcel is a fictional example API.
theme:
preset: shoal
palette: kelp
mode: system
toggle: true
publications:
defaults: { channel: production, version: current, language: en }
channels:
production: { pathPrefix: "" }
versions:
current: { pathPrefix: "" }
enabled:
- { channel: production, version: current, language: en }
frontmatter:
defaults:
type: guide
channel: production
version: current
language: en
access: { policy: public }
apis:
cedar:
source: { kind: file, path: ./apis/cedar-parcel.yaml, publish: true }
discovery: { endpoint: https://api.cedar-parcel.example/v1 }
publicReview:
digest: a7a7e8eb427a7481f1c1f357d03db40319ad2e79bdeea926501da744cbecfd6f
mappings:
subjectLifecycle:
from: x-docs-availability.phase
values:
limited: beta
building: in-development
retired: deprecated
querySyntax:
- parameter: parameters
pathPrefixes: [/Search/]
style: unwrapped
example: declaredValue(gt)=100
operations:
- id: cedar.createEnrollment
sourceOperationId: createEnrollment
reference: reference.create-enrollment
generateReferences:
pathPrefix: /reference
navigation:
- { id: overview, doc: guide.overview }
- { id: quickstart, doc: guide.quickstart }
- id: reference
label: API reference
api: cedar
items:
- { id: create-enrollment, operation: cedar.createEnrollment }
- { id: generated, generated: tags }
relationships:
- id: overview-to-quickstart
from: guide.overview
to: guide.quickstart
type: next-step
reason: Prepare a local-shop enrollment and inspect its receipt.
- id: quickstart-to-reference
from: guide.quickstart
to: reference.create-enrollment
type: next-step
reason: Compare all four program shapes and their required fields.
outputs:
markdown:
metadata:
format: yaml-frontmatter
fields:
id: frontmatter.id
title: frontmatter.title
summary: frontmatter.summary
canonical: publication.canonicalUrl
byType:
api-reference:
addFields: { operations: frontmatter.operations }
Multilingual help center
English at the root and German under /de. German pages set language: de and reuse the English page IDs. The site also sets a theme that follows the reader's light or dark setting, project-wide snippet values, and a custom callout type.
# One site in English and German, with a theme, a shared snippet,
# and a custom callout type. English is the default language, so its
# pages have no prefix; German pages live under /de.
schemaVersion: 1
site:
title: Tidewater help center
header:
links:
- { label: Status, href: "https://status.example.com" }
- { label: Contact us, href: "mailto:help@example.com", style: button }
theme:
preset: reef
palette: harbor
mode: system # follows the reader's setting and shows a switch
publications:
defaults: { channel: production, version: current, language: en }
channels:
production: { pathPrefix: "" }
versions:
current: { pathPrefix: "" }
enabled:
- { channel: production, version: current } # English
- { channel: production, version: current, language: de } # German
frontmatter:
defaults:
type: guide
channel: production
version: current
access: { policy: public }
# Values every page can use in snippets.
variables:
product: Tidewater
supportHours: 24
snippets:
support.contact:
file: ./snippets/contact.mdx
variables:
product: { type: string, required: true }
supportHours: { type: number, required: true }
callouts:
types:
privacy:
label: Privacy
tone: important
icon: shield-check
navigation:
- id: home
doc: guide.home
- id: account
label: Account
items:
- id: reset-password
doc: guide.reset-password
Shared company preset
Several sites share one base file. Each site's config extends it and changes only what differs: here, the title, the palette, and extra navigation. The shared file also declares a required owner field and a runbook template for every site.
# Settings every docs site in the company shares. Sites layer their
# own docs.config.yaml over this file with `extends`.
schemaVersion: 1
site:
title: Company docs
header:
links:
- { label: All docs, href: "https://docs.example.com" }
- { label: Support, href: "https://support.example.com", style: button }
theme:
preset: ledger
palette: ink
publications:
defaults: { channel: production, version: current, language: en }
channels:
production: { pathPrefix: "" }
versions:
current: { pathPrefix: "" }
enabled:
- { channel: production, version: current }
frontmatter:
defaults:
type: guide
channel: production
version: current
access: { policy: public }
fields:
owner:
type: string
required: true
description: Team that maintains the page.
# Every site starts with the same first page. Paths in an extended file
# resolve from that file, so this template body lives next to it.
navigation:
- id: start
doc: guide.start
templates:
runbook:
type: guide
body: ./runbook.mdx
# One team's site. It inherits the shared theme, publication, page
# defaults, the required `owner` field, and the runbook template.
schemaVersion: 1
extends: [../shared/company-docs.yaml]
site:
title: Billing team docs # replaces the shared title; header links stay
theme:
palette: coral # keeps the shared ledger preset
navigation:
$concat: # adds pages after the shared start page
- id: runbooks
label: Runbooks
items:
- id: failed-invoices
doc: guide.failed-invoices
Run shoalrun config explain theme.preset example-configs/shared-preset/site/docs.config.yaml to see that the preset comes from the shared file.