Section
A generic page section: a short header — eyebrow, headline, blurb — above an arbitrary body. Use it to introduce any content block with a title and intro, especially primitives that have no header of their own like bento, a grid of cards, or a collection.
The header is anchored to the headline, written as a heading at the start of the section. A paragraph directly before it is the eyebrow, and a paragraph directly after it is the blurb. Everything else is the body. A section that does not start with a heading, or with one paragraph and then a heading, has no header: all of its content is body, so prose placed in a section is never re-read as an eyebrow.
Basic usage
{% section %}
Composability
## Runes that work together
Every block is a rune — combine them freely to build a page.
{% bento %}
## Marketing
Hero, CTA, pricing, testimonials.
## Docs
API references, changelogs, symbols.
## Storytelling
Characters, realms, factions, lore.
{% /bento %}
{% /section %}<section data-rune="section" data-rune-fields="{"align":"start"}">
<header>
<p data-name="eyebrow">Composability</p>
<h2 id="runes-that-work-together" data-name="headline">Runes that work together</h2>
<p data-name="blurb">Every block is a rune — combine them freely to build a page.</p>
</header>
<div data-name="body">
<section data-rune="bento" data-rune-fields="{"gap":"1rem","columns":"6","row-height":"","content-height":"","media-ratio":"","collapse":""}">
<div data-name="grid">
<div data-rune="bento-cell" data-rune-fields="{"size":"large","cols":"4","rows":"2","content-height":"","media-ratio":""}" data-media-position="start" data-field="cell">
<h3 id="marketing" data-name="title">Marketing</h3>
<div data-name="body">
<p>Hero, CTA, pricing, testimonials.</p>
</div>
</div>
<div data-rune="bento-cell" data-rune-fields="{"size":"large","cols":"4","rows":"2","content-height":"","media-ratio":""}" data-media-position="start" data-field="cell">
<h3 id="docs" data-name="title">Docs</h3>
<div data-name="body">
<p>API references, changelogs, symbols.</p>
</div>
</div>
<div data-rune="bento-cell" data-rune-fields="{"size":"large","cols":"4","rows":"2","content-height":"","media-ratio":""}" data-media-position="start" data-field="cell">
<h3 id="storytelling" data-name="title">Storytelling</h3>
<div data-name="body">
<p>Characters, realms, factions, lore.</p>
</div>
</div>
</div>
</section>
</div>
</section>Composability
Runes that work together
Every block is a rune — combine them freely to build a page.
Marketing
Hero, CTA, pricing, testimonials.
Docs
API references, changelogs, symbols.
Storytelling
Characters, realms, factions, lore.
<section class="rf-section rf-section--start" data-align="start" data-rune="section" data-density="full">
<header data-name="preamble" class="rf-section__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-section__eyebrow" data-section="eyebrow">Composability</p>
<h2 id="runes-that-work-together" data-name="headline" class="rf-section__headline" data-section="title">Runes that work together</h2>
<p data-name="blurb" class="rf-section__blurb" data-section="description">Every block is a rune — combine them freely to build a page.</p>
</header>
<div data-name="body" class="rf-section__body" data-section="body">
<section class="rf-bento" data-columns="6" data-gap="1rem" data-row-height="" data-content-height="" data-media-ratio="" data-collapse="" data-elevation="flush" data-rune="bento" data-density="full" style="--bento-columns: 6; --bento-gap: 1rem">
<div data-name="grid" class="rf-bento__grid">
<div class="rf-bento-cell" data-media-position="start" data-field="cell" data-size="large" data-cols="4" data-rows="2" data-content-height="" data-media-ratio="" data-rune="bento-cell" data-density="compact" style="--cell-cols: 4; --cell-rows: 2">
<div data-name="content" class="rf-bento-cell__content">
<h3 id="marketing" data-name="title" class="rf-bento-cell__title" data-section="title">Marketing</h3>
<div data-name="body" class="rf-bento-cell__body" data-section="body">
<p>Hero, CTA, pricing, testimonials.</p>
</div>
</div>
</div>
<div class="rf-bento-cell" data-media-position="start" data-field="cell" data-size="large" data-cols="4" data-rows="2" data-content-height="" data-media-ratio="" data-rune="bento-cell" data-density="compact" style="--cell-cols: 4; --cell-rows: 2">
<div data-name="content" class="rf-bento-cell__content">
<h3 id="docs" data-name="title" class="rf-bento-cell__title" data-section="title">Docs</h3>
<div data-name="body" class="rf-bento-cell__body" data-section="body">
<p>API references, changelogs, symbols.</p>
</div>
</div>
</div>
<div class="rf-bento-cell" data-media-position="start" data-field="cell" data-size="large" data-cols="4" data-rows="2" data-content-height="" data-media-ratio="" data-rune="bento-cell" data-density="compact" style="--cell-cols: 4; --cell-rows: 2">
<div data-name="content" class="rf-bento-cell__content">
<h3 id="storytelling" data-name="title" class="rf-bento-cell__title" data-section="title">Storytelling</h3>
<div data-name="body" class="rf-bento-cell__body" data-section="body">
<p>Characters, realms, factions, lore.</p>
</div>
</div>
</div>
</div>
</section>
</div>
</section>Header-only or body-only
Every header slot is optional. With no leading heading the section is just a styled wrapper around its body; with only a header it's a standalone intro.
Choose which header roles to read
header says how much of the start of the section becomes its header:
| Value | Reads |
|---|---|
auto (default) | eyebrow, headline and blurb, around a leading heading |
headline | only a leading heading; the paragraph after it stays body |
none | no header; all content is body |
{% section header="headline" %}
## Release notes
This paragraph is body text, not a blurb.
{% /section %}<section data-rune="section" data-rune-fields="{"align":"start"}">
<header>
<h2 id="release-notes" data-name="headline">Release notes</h2>
</header>
<div data-name="body">
<p>This paragraph is body text, not a blurb.</p>
</div>
</section>Release notes
This paragraph is body text, not a blurb.
<section class="rf-section rf-section--start" data-align="start" data-rune="section" data-density="full">
<header data-name="preamble" class="rf-section__preamble" data-section="preamble">
<h2 id="release-notes" data-name="headline" class="rf-section__headline" data-section="title">Release notes</h2>
</header>
<div data-name="body" class="rf-section__body" data-section="body">
<p>This paragraph is body text, not a blurb.</p>
</div>
</section>A composed rune that places a section gets the same grammar for its slots. When a slot's content lands in a header role the slot is not named for, the build reports slot-header-role and names both remedies: name the field for the role, or set header on the section.
Media beside or above the content
Give a section media-position and it splits on --- the way card does: a leading media zone, the content, and an optional trailing footer. The header stays in the content column, beside or below the media. Without media-position the section reads no zones, and a --- is an ordinary rule in the body.
{% section media-position="start" media-ratio="1/3" %}

---
Field notes
## The harbour at dawn
Salt, rope and the first boats going out.
The tide turns an hour after sunrise, and the market opens with it.
{% /section %}<section data-rune="section" data-rune-fields="{"align":"start","media-position":"start","media-ratio":"1/3"}">
<div data-name="media">
<svg class="rf-placeholder" data-shape="cover" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice" width="100%" fill="none" role="img" aria-label="A harbour at dawn">
<rect x="0" y="0" width="1600" height="900" fill="var(--rf-color-surface)"></rect>
<circle cx="1216" cy="270" r="90" fill="var(--rf-color-border)"></circle>
<path d="M0 648 Q 448 495 800 630 T 1600 576 L 1600 900 L 0 900 Z" fill="var(--rf-color-muted)"></path>
<rect x="4" y="4" width="1593" height="893" fill="none" stroke="var(--rf-color-border)" stroke-width="7"></rect>
</svg>
</div>
<div data-name="content">
<header>
<p data-name="eyebrow">Field notes</p>
<h2 id="the-harbour-at-dawn" data-name="headline">The harbour at dawn</h2>
<p data-name="blurb">Salt, rope and the first boats going out.</p>
</header>
<div data-name="body">
<p>The tide turns an hour after sunrise, and the market opens with it.</p>
</div>
</div>
</section>Field notes
The harbour at dawn
Salt, rope and the first boats going out.
The tide turns an hour after sunrise, and the market opens with it.
<section class="rf-section rf-section--start" data-align="start" data-media-position="start" data-media-ratio="1/3" data-rune="section" data-density="full">
<div data-name="media" class="rf-section__media" data-section="media" data-guest-fit="clip">
<svg class="rf-placeholder" data-shape="cover" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice" width="100%" fill="none" role="img" aria-label="A harbour at dawn">
<rect x="0" y="0" width="1600" height="900" fill="var(--rf-color-surface)"></rect>
<circle cx="1216" cy="270" r="90" fill="var(--rf-color-border)"></circle>
<path d="M0 648 Q 448 495 800 630 T 1600 576 L 1600 900 L 0 900 Z" fill="var(--rf-color-muted)"></path>
<rect x="4" y="4" width="1593" height="893" fill="none" stroke="var(--rf-color-border)" stroke-width="7"></rect>
</svg>
</div>
<div data-name="content" class="rf-section__content">
<header data-name="preamble" class="rf-section__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-section__eyebrow" data-section="eyebrow">Field notes</p>
<h2 id="the-harbour-at-dawn" data-name="headline" class="rf-section__headline" data-section="title">The harbour at dawn</h2>
<p data-name="blurb" class="rf-section__blurb" data-section="description">Salt, rope and the first boats going out.</p>
</header>
<div data-name="body" class="rf-section__body" data-section="body">
<p>The tide turns an hour after sunrise, and the market opens with it.</p>
</div>
</div>
</section>| Attribute | Values | What it does |
|---|---|---|
media-position | top, bottom, start, end, cover | Where the media zone sits. Turns zoning on. |
media-ratio | 1/3 … 2/3 | The media's share of the row, beside the content |
valign | top, center, bottom, stretch | Cross-axis alignment, beside the content |
collapse | sm, md, lg, never | When a side-by-side split stacks |
media | portrait, cover, thumbnail, hero, icon | What the media zone holds, so the media dimension styles it. portrait is cropped round. |
The zones count as card's do: one zone is the content, two are media and content, and three or more add the last as the footer. An empty leading zone means no media, and the media element is left out.
Cover
media-position="cover" lays the content over the media, as on card. content-place anchors the content ("end start", or auto), and height (sm to xl) or aspect gives a section with little content its shape.
Align the header
align positions the header (the body always spans the section). Use center for marquee sections, end to right-align.
{% section align="center" %}
Pick a plan
## Simple, transparent pricing
No hidden fees. Cancel anytime.
{% bento %}
## Free
For trying things out.
## Pro
For growing teams.
{% /bento %}
{% /section %}<section data-rune="section" data-rune-fields="{"align":"center"}">
<header>
<p data-name="eyebrow">Pick a plan</p>
<h2 id="simple-transparent-pricing" data-name="headline">Simple, transparent pricing</h2>
<p data-name="blurb">No hidden fees. Cancel anytime.</p>
</header>
<div data-name="body">
<section data-rune="bento" data-rune-fields="{"gap":"1rem","columns":"6","row-height":"","content-height":"","media-ratio":"","collapse":""}">
<div data-name="grid">
<div data-rune="bento-cell" data-rune-fields="{"size":"large","cols":"4","rows":"2","content-height":"","media-ratio":""}" data-media-position="start" data-field="cell">
<h3 id="free" data-name="title">Free</h3>
<div data-name="body">
<p>For trying things out.</p>
</div>
</div>
<div data-rune="bento-cell" data-rune-fields="{"size":"large","cols":"4","rows":"2","content-height":"","media-ratio":""}" data-media-position="start" data-field="cell">
<h3 id="pro" data-name="title">Pro</h3>
<div data-name="body">
<p>For growing teams.</p>
</div>
</div>
</div>
</section>
</div>
</section>Pick a plan
Simple, transparent pricing
No hidden fees. Cancel anytime.
Free
For trying things out.
Pro
For growing teams.
<section class="rf-section rf-section--center" data-align="center" data-rune="section" data-density="full">
<header data-name="preamble" class="rf-section__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-section__eyebrow" data-section="eyebrow">Pick a plan</p>
<h2 id="simple-transparent-pricing" data-name="headline" class="rf-section__headline" data-section="title">Simple, transparent pricing</h2>
<p data-name="blurb" class="rf-section__blurb" data-section="description">No hidden fees. Cancel anytime.</p>
</header>
<div data-name="body" class="rf-section__body" data-section="body">
<section class="rf-bento" data-columns="6" data-gap="1rem" data-row-height="" data-content-height="" data-media-ratio="" data-collapse="" data-elevation="flush" data-rune="bento" data-density="full" style="--bento-columns: 6; --bento-gap: 1rem">
<div data-name="grid" class="rf-bento__grid">
<div class="rf-bento-cell" data-media-position="start" data-field="cell" data-size="large" data-cols="4" data-rows="2" data-content-height="" data-media-ratio="" data-rune="bento-cell" data-density="compact" style="--cell-cols: 4; --cell-rows: 2">
<div data-name="content" class="rf-bento-cell__content">
<h3 id="free" data-name="title" class="rf-bento-cell__title" data-section="title">Free</h3>
<div data-name="body" class="rf-bento-cell__body" data-section="body">
<p>For trying things out.</p>
</div>
</div>
</div>
<div class="rf-bento-cell" data-media-position="start" data-field="cell" data-size="large" data-cols="4" data-rows="2" data-content-height="" data-media-ratio="" data-rune="bento-cell" data-density="compact" style="--cell-cols: 4; --cell-rows: 2">
<div data-name="content" class="rf-bento-cell__content">
<h3 id="pro" data-name="title" class="rf-bento-cell__title" data-section="title">Pro</h3>
<div data-name="body" class="rf-bento-cell__body" data-section="body">
<p>For growing teams.</p>
</div>
</div>
</div>
</div>
</section>
</div>
</section>Attributes
| Attribute | Type | Required | Description |
|---|---|---|---|
align | "start" | "center" | "end" | — | Header alignment: start (default), center, or end |
header | "auto" | "headline" | "none" | — | Which header roles to read: auto (eyebrow, headline and blurb around a leading heading; the default), headline (only the heading), or none (all content is body) |
media | "portrait" | "cover" | "thumbnail" | "hero" | "icon" | — | What the media zone holds, stamped as `data-media` so the media dimension styles it (portrait is cropped round) |
content-place | string | — | Cover overlay anchor: "<block> <inline>" (e.g. "end start") or "auto" |
height | "sm" | "md" | "lg" | "xl" | — | Intrinsic section height (named scale) for cover sections |
aspect | string | — | Intrinsic section aspect ratio (e.g. "16/9") for cover sections |
Inherited from the split layout preset
| Attribute | Type | Required | Description |
|---|---|---|---|
media-position | "top" | "bottom" | "start" | "end" | "cover" | — | Where the media zone sits relative to the content: above (top), below (bottom), or beside (start/end) |
media-ratio | "1/3" | "2/5" | "1/2" | "3/5" | "2/3" | — | Media zone’s share of the row width when media is beside content (start/end) |
valign | "top" | "center" | "bottom" | "stretch" | — | Cross-axis alignment when media is beside content (start/end); applies to the shorter zone |
collapse | "sm" | "md" | "lg" | "never" | — | Breakpoint at which side-by-side layouts collapse to a single stacked column |
Universal attributes
bg
The background layer (SPEC-088): image, video, gradient, flat overlay wash and legibility scrim, in a single injected layer behind the rune's content.
| Attribute | Type | Required | Description |
|---|---|---|---|
bg | string | — | Background preset applied to this block |
bg-from | string | — | Gradient start colour — a semantic token name (→ var(--rf-color-*)) |
bg-gradient | "to-t" | "to-b" | "to-l" | "to-r" | "to-tr" | "to-br" | "to-bl" | "to-tl" | — | Gradient direction (bounded named set) |
bg-gradient-type | "linear" | "radial" | "conic" | — | Gradient type |
bg-to | string | — | Gradient end colour — a semantic token name |
bg-via | string | — | Optional middle gradient stop — a semantic token name |
scrim | "top" | "bottom" | "left" | "right" | "none" | — | Scrim direction (heaviest edge); presence turns the scrim on, "none" opts out of the default cover scrim |
scrim-blur | "none" | "sm" | "md" | "lg" | — | Frost scrim blur amount |
scrim-strength | "sm" | "md" | "lg" | — | Gradient scrim strength |
scrim-tone | "dark" | "light" | — | Whether the scrim darkens (for light text) or lightens (for dark text) |
scrim-type | "gradient" | "frost" | — | Scrim treatment: gradient (default) or frost (backdrop blur) |
dropcap
Per-instance drop-cap opt-in (SPEC-108).
| Attribute | Type | Required | Description |
|---|---|---|---|
dropcap | boolean | — | Style the opening letter of a prose body as a drop cap (SPEC-108). Honoured only when the body reads as prose; ignored otherwise. |
elevation
The chrome/depth ladder (SPEC-107). The skin maps each rung to a chrome bundle by attribute, so there is no BEM class.
| Attribute | Type | Required | Description |
|---|---|---|---|
elevation | "sunken" | "flush" | "flat" | "raised" | "floating" | "overlay" | "none" | "sm" | "md" | "lg" | — | Surface depth on the SPEC-107 ladder (sunken→overlay); none/sm/md/lg are deprecated aliases |
frame
Surface chrome (SPEC-086): a named preset from the theme registry with inline `frame-*` overrides layered on top.
| Attribute | Type | Required | Description |
|---|---|---|---|
frame | string | — | Named frame preset presenting this block's media surface |
frame-anchor | string | — | Crop focal point when the guest is cut (object-position) |
frame-aspect | string | — | Aspect ratio of the framed media, e.g. "16/9" |
frame-displace | "top" | "bottom" | "end" | "bottom-end" | "top-end" | "both" | — | Edge/corner the framed guest moves toward |
frame-displace-mode | "peek" | "bleed" | — | How displacement renders: `peek` (default) translates the guest visually inside its frame target; `bleed` uses negative margin on the media zone so following layout pulls up — extends past a section like a hero without leaving a gap above |
frame-offset | "none" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | — | Displacement distance (non-linear named scale: sm…xl ride the block-spacing tokens, 2xl/3xl/4xl step up to section-spacing for bleed-mode displacements that clear a section edge) |
frame-overflow | "clip" | "bleed" | — | How the frame handles a guest whose content is wider than it: `clip` (default) keeps it inside the rounded frame; `bleed` runs an overflowing guest out to the layout edge on a narrow viewport so it reads as cropped by the screen (a bleed host only — hero/feature) |
frame-oversize | string | — | How far the guest exceeds its slot (scale factor) |
frame-place | string | — | Guest-box alignment in the slot (e.g. "left top") |
frame-shadow | "none" | "sm" | "md" | "lg" | — | Silhouette drop-shadow strength for the framed media |
inset
Internal padding override.
| Attribute | Type | Required | Description |
|---|---|---|---|
inset | "flush" | "tight" | "default" | "loose" | "breathe" | — | Inner padding of this block |
motion
Scroll-reveal entrance (SPEC-105). The author declares the character, the theme owns the choreography, a behaviour owns the timing.
| Attribute | Type | Required | Description |
|---|---|---|---|
reveal | "none" | "fade" | "slide" | "scale" | "blur" | — | Scroll-reveal entrance character (none|fade|slide|scale|blur); the theme owns the choreography |
stagger | boolean | — | Cascade this block's items in as it reveals (no-op on single-child runes) |
prominence
Header emphasis (SPEC-107). Scales a rune's page-section header; the skin maps it to a type register by attribute, so there is no BEM class.
| Attribute | Type | Required | Description |
|---|---|---|---|
prominence | "quiet" | "normal" | "prominent" | "display" | — | Section-header emphasis (only on page-section-header family runes) |
reading
Editorial register for body text (SPEC-108). The author picks the register; the theme owns the magnitude.
| Attribute | Type | Required | Description |
|---|---|---|---|
reading | "fine" | "ui" | "prose" | — | Reading register for this block’s body (SPEC-108): fine | ui | prose. The theme owns the editorial treatment. |
spacing
Block-level rhythm override.
| Attribute | Type | Required | Description |
|---|---|---|---|
spacing | "flush" | "tight" | "default" | "loose" | "breathe" | — | Vertical spacing above and below this block |
substrate
Generated pattern fills (SPEC-087). Markers only — the engine sets the attributes and cell/opacity custom properties, CSS draws the pattern.
| Attribute | Type | Required | Description |
|---|---|---|---|
substrate | "dots" | "grid" | "lines" | "cross" | "checker" | "none" | — | Generated surface pattern |
substrate-fill | "inherit" | "inset" | — | Surface fill the pattern sits on (full colour stays with tint) |
substrate-opacity | "sm" | "md" | "lg" | — | Pattern ink strength |
substrate-size | "sm" | "md" | "lg" | — | Pattern cell size |
substrate-target | "self" | "media" | — | Which surface the pattern fills (overrides the rune/theme default) |
tint
Per-rune colour override (SPEC-053): a named tint from the theme registry, with inline per-token overrides layered on top.
| Attribute | Type | Required | Description |
|---|---|---|---|
tint | string | — | Color tint preset applied to this block |
tint-mode | "auto" | "dark" | "light" | — | Whether the tint adapts to auto, dark, or light mode |
width
The track a block rune occupies.
| Attribute | Type | Required | Description |
|---|---|---|---|
width | "compact" | "narrow" | "content" | "wide" | "full" | — | Maximum width constraint for this block |
Anatomy
| Slot | From | Class |
|---|---|---|
| Eyebrow | a paragraph directly before the headline | .rf-section__eyebrow |
| Headline | a heading at the start, or after the eyebrow | .rf-section__headline |
| Blurb | a paragraph directly after the headline | .rf-section__blurb |
| Body | everything else | .rf-section__body |
| Media | the leading zone, with media-position | .rf-section__media |
| Content | the header, body and footer, with media-position | .rf-section__content |
| Footer | a trailing zone after a second ---, with media-position | .rf-section__footer |
The header slots are wrapped in .rf-section__preamble; the whole block is .rf-section.