Section schema reference
Every built-in section is defined with defineSection({ type, schema, ... }). The type string is the discriminator stored in each section’s JSON file; the Zod schema is the source of truth for content shape.
Each block on disk has this outer envelope:
{ id: string; // unique within the site type: string; // section type key (see below) content: { ... }; // type-specific content options?: { ... }; // type-specific display options layout?: { colSpan?: number; // only meaningful inside a container };}link_heading
Section titled “link_heading”Source: packages/primitives/src/components/sections/LinkHeading/index.tsx
A top-level section heading. Generates a navigation anchor.
| Field | Type | Required | Notes |
|---|---|---|---|
content.heading | string | Yes | Heading text |
sub_heading
Section titled “sub_heading”Source: packages/primitives/src/components/sections/SubHeading/index.tsx
A second-level heading. Excluded from the top-level nav by default.
| Field | Type | Required | Notes |
|---|---|---|---|
content.heading | string | Yes | Heading text |
content.excludeFromNav | boolean | No | When true, omits this heading from nav generation |
sub_sub_heading
Section titled “sub_sub_heading”Source: packages/primitives/src/components/sections/SubSubHeading/index.tsx
A third-level heading. Never appears in top-level nav.
| Field | Type | Required | Notes |
|---|---|---|---|
content.heading | string | Yes | Heading text |
content.excludeFromNav | boolean | No | When true, omits this heading from nav generation |
Source: packages/primitives/src/components/sections/Prose/index.tsx
A rich-text body block. Content is stored as sanitized HTML.
| Field | Type | Required | Notes |
|---|---|---|---|
content.body | string | Yes | HTML string; listed in richTextFields and sanitized on save |
Source: packages/primitives/src/components/sections/Media/index.tsx
A single image or video block.
content fields
Section titled “content fields”| Field | Type | Required | Notes |
|---|---|---|---|
content.ref | SingleMediaReference | Yes | Image or video reference (see below) |
content.link | LinkValue | No | Optional link wrapping the media |
SingleMediaReference — type: "image"
Section titled “SingleMediaReference — type: "image"”| Field | Type | Required | Notes |
|---|---|---|---|
type | "image" | Yes | Discriminator |
imageId | string | Yes | Media manifest ID; defaults to "" |
caption | string | string[] | No | Caption text |
background | string | No | Background color hint |
invertFrom | string | No | Theme at which to invert the image |
border | boolean | No | Render a border |
objectFit | "cover" | "contain" | No | CSS object-fit |
SingleMediaReference — type: "video"
Section titled “SingleMediaReference — type: "video"”Inherits all "image" fields, plus:
| Field | Type | Required | Notes |
|---|---|---|---|
type | "video" | Yes | Discriminator |
poster | string | No | Poster image URL |
autoplay | boolean | No | Auto-play on load |
loop | boolean | No | Loop playback |
muted | boolean | No | Muted by default |
options fields
Section titled “options fields”| Field | Type | Required | Notes |
|---|---|---|---|
options.square | boolean | No | Force square aspect ratio |
options.showCaption | boolean | No | Render the caption below the media |
options.border | boolean | No | Render a border |
options.objectFit | "cover" | "contain" | No | CSS object-fit; defaults to "contain" |
button
Section titled “button”Source: packages/primitives/src/components/sections/Button/index.tsx
A call-to-action button.
| Field | Type | Required | Notes |
|---|---|---|---|
content.text | string | Yes | Button label |
content.link | LinkValue | No | Destination; see LinkValue below |
content.download | boolean | No | Adds download attribute to the rendered anchor |
container
Section titled “container”Source: packages/primitives/src/components/sections/Container/index.tsx
A layout wrapper that holds child blocks in a CSS grid. Nesting is capped at depth 2 (a container may hold leaves, not other containers).
| Field | Type | Required | Notes |
|---|---|---|---|
content.columns | number (int, 1–6) | Yes | Number of grid columns; defaults to 1 |
content.flow | "row" | "column" | Yes | Grid auto-flow direction; defaults to "row" |
content.children | Section[] | Yes | Child blocks (any section type, each with an id and optional layout.colSpan); defaults to [] |
content.childDefaults | Record<string, unknown> | No | Default options applied to children added via the editor |
Child blocks may carry layout.colSpan (int ≥ 1). If colSpan exceeds columns, it is clamped to columns on the next parse.
spacer
Section titled “spacer”Source: packages/primitives/src/components/sections/Spacer/index.tsx
An empty vertical gap. No content fields.
| Field | Type | Required | Notes |
|---|---|---|---|
content | {} | — | Always an empty object |
colors
Section titled “colors”Source: packages/primitives/src/components/sections/Colors/index.tsx, schema.ts
A brand color palette display.
content fields
Section titled “content fields”| Field | Type | Required | Notes |
|---|---|---|---|
content.colors | ColorItem[] | Yes | One entry per color |
ColorItem
Section titled “ColorItem”| Field | Type | Required | Notes |
|---|---|---|---|
name | string | No | Display name for the color |
spaces | ColorSpace[] (min 1) | Yes | One or more color space representations |
ColorSpace
Section titled “ColorSpace”At least one field must be present.
| Field | Type | Required | Notes |
|---|---|---|---|
hex | string | No | 6-digit hex (#rrggbb) |
rgb | string | No | Free-form RGB string |
cmyk | string | No | Free-form CMYK string |
pantone | string | No | Pantone name or code |
options fields
Section titled “options fields”| Field | Type | Required | Notes |
|---|---|---|---|
options.label | string | No | Section label text |
options.columns | number (int, 2–4) | No | Display columns |
options.collapsing | boolean | No | Enable collapsing layout |
options.showLabel | boolean | No | Render the label; defaults to true in settings |
icon_list
Section titled “icon_list”Source: packages/primitives/src/components/sections/IconList/index.tsx
A list of labelled items, each with optional icon and do/don’t marker.
content fields
Section titled “content fields”| Field | Type | Required | Notes |
|---|---|---|---|
content.items | IconListItem[] | Yes | One entry per list item |
IconListItem
Section titled “IconListItem”| Field | Type | Required | Notes |
|---|---|---|---|
label | string | Yes | Short label |
text | string | Yes | Body text |
icon | string | No | Icon identifier |
dodont | "do" | "dont" | No | Do/Don’t marker |
options fields
Section titled “options fields”| Field | Type | Required | Notes |
|---|---|---|---|
options.icon | string | null | No | Default icon for all items |
options.showLabel | boolean | No | Render item labels |
options.stackText | boolean | No | Stack label + text vertically |
dodont_media
Section titled “dodont_media”Source: packages/primitives/src/components/sections/DoDontMedia/index.tsx
A media block with an explicit Do / Don’t annotation. Designed to be used inside a container alongside other dodont_media blocks.
content fields
Section titled “content fields”| Field | Type | Required | Notes |
|---|---|---|---|
content.ref | SingleMediaReference | Yes | Image or video reference; same shape as media |
content.dodont | "do" | "dont" | Yes | Annotation shown on the block |
content.link | LinkValue | No | Optional link wrapping the media |
options fields
Section titled “options fields”| Field | Type | Required | Notes |
|---|---|---|---|
options.square | boolean | No | Force square aspect ratio |
options.showCaption | boolean | No | Render the caption below the media |
options.border | boolean | No | Render a border |
options.objectFit | "cover" | "contain" | No | CSS object-fit; defaults to "contain" |
Shared types
Section titled “Shared types”LinkValue
Section titled “LinkValue”Defined in packages/primitives/src/schemas/link.ts. A discriminated union on kind.
External link (kind: "external"):
| Field | Type | Notes |
|---|---|---|
kind | "external" | |
href | string | Must be empty, relative, http(s)://, or mailto:; dangerous schemes (javascript:, data:) are rejected |
target | "_self" | "_blank" |
Internal link (kind: "internal"):
| Field | Type | Notes |
|---|---|---|
kind | "internal" | |
pageId | string | ID of the target page |
anchorSectionId | string | null | undefined | Optional in-page anchor |
target | "_self" | "_blank" |