Surfaces
Every block rune exposes one or two decorable surfaces, and a small, universal vocabulary styles them — the same attributes on a card, a figure, a hero, or a bento-cell. A card, for example, has two: its self surface (the card box) and a media surface (its image/embed slot). No per-rune attributes to learn.
| Attribute | Surface | What it does |
|---|---|---|
elevation | self | a box-shadow that floats the box |
frame / frame-* | media | present the media — aspect, crop, silhouette shadow, displacement |
substrate / substrate-* | self (default) | a generated pattern (dots, grid, …) |
tint | colour | recolour the surface (see tint) |
bg | image | an image/video layer behind content (see bg) |
The page walks the model along its four axes:
- Chrome — the shadows and framing that lift a surface off the page.
- Fills — the colour, pattern, and gradient layers that paint it.
- Cover — the poster layout, where content overlays the media.
- Posture — how a clickable surface treats the guests inside it.
Everything here is one or two attributes on an ordinary rune. Nothing is a bespoke component.
Chrome — shadow and frame
A surface decorates on two layers. The self layer is the whole tile — elevation lifts it as a box shadow. The media layer is the framed image inside — frame-* attributes shape it (frame-aspect), anchor the crop (frame-anchor), displace it past the slot (frame-displace), and shadow it (frame-shadow). elevation and frame-shadow are the same physical property (a shadow) on different surfaces, so they carry two names and never collide:
{% card elevation="lg" %}
### `elevation="lg"`
A box shadow on the card's **self** surface — the whole tile floats.
{% /card %}
{% figure frame-aspect="16/9" caption="`frame-aspect` sets the framed media's shape; the image fills it via `object-fit: cover`." %}

{% /figure %}<div data-rune="card" data-rune-fields="{"media-position":"top"}" elevation="lg">
<div data-name="body">
<h3 id="" data-name="title">
<code>elevation="lg"</code>
</h3>
<p>
A box shadow on the card's
<strong marker="**">self</strong>
surface — the whole tile floats.
</p>
</div>
</div>
<figure data-rune="figure" typeof="ImageObject">
<img src="https://picsum.photos/seed/gallerysilhouette/800/450" alt="A framed image" property="contentUrl">
<figcaption data-name="caption" property="caption">`frame-aspect` sets the framed media's shape; the image fills it via `object-fit: cover`.</figcaption>
<meta data-field="frame-aspect" content="16/9">
</figure>elevation="lg"
A box shadow on the card's self surface — the whole tile floats.
<div class="rf-card" data-media-position="top" data-elevation="lg" data-rune="card" data-density="full">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="" data-name="title" class="rf-card__title">
<code>elevation="lg"</code>
</h3>
<p>
A box shadow on the card's
<strong marker="**">self</strong>
surface — the whole tile floats.
</p>
</div>
</div>
</div>
<figure typeof="ImageObject" class="rf-figure rf-figure--default rf-figure--center" data-size="default" data-align="center" data-rune="figure" data-density="compact" style="--frame-aspect: 16/9">
<img src="https://picsum.photos/seed/gallerysilhouette/800/450" alt="A framed image" property="contentUrl" />
<figcaption data-name="caption" property="caption" class="rf-figure__caption" data-section="description">`frame-aspect` sets the framed media's shape; the image fills it via `object-fit: cover`.</figcaption>
</figure>elevation runs a named scale — none | sm | md | lg — on every block rune. elevation="none" flattens a rune's default shadow.
{% card elevation="sm" %}
### `sm`
Barely lifted.
{% /card %}
{% card elevation="md" %}
### `md`
A clear float.
{% /card %}
{% card elevation="lg" %}
### `lg`
Highest z-height.
{% /card %}<div data-rune="card" data-rune-fields="{"media-position":"top"}" elevation="sm">
<div data-name="body">
<h3 id="" data-name="title">
<code>sm</code>
</h3>
<p>Barely lifted.</p>
</div>
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}" elevation="md">
<div data-name="body">
<h3 id="" data-name="title">
<code>md</code>
</h3>
<p>A clear float.</p>
</div>
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}" elevation="lg">
<div data-name="body">
<h3 id="" data-name="title">
<code>lg</code>
</h3>
<p>Highest z-height.</p>
</div>
</div>sm
Barely lifted.
md
A clear float.
lg
Highest z-height.
<div class="rf-card" data-media-position="top" data-elevation="sm" data-rune="card" data-density="full">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="" data-name="title" class="rf-card__title">
<code>sm</code>
</h3>
<p>Barely lifted.</p>
</div>
</div>
</div>
<div class="rf-card" data-media-position="top" data-elevation="md" data-rune="card" data-density="full">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="" data-name="title" class="rf-card__title">
<code>md</code>
</h3>
<p>A clear float.</p>
</div>
</div>
</div>
<div class="rf-card" data-media-position="top" data-elevation="lg" data-rune="card" data-density="full">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="" data-name="title" class="rf-card__title">
<code>lg</code>
</h3>
<p>Highest z-height.</p>
</div>
</div>
</div>frame decorates the media surface. Apply a named preset (frame="screenshot") or set facets inline — they also work without a preset.
| Facet | Values | Effect |
|---|---|---|
frame-aspect | e.g. 16/9, 1/1 | aspect ratio |
frame-shadow | none|sm|md|lg | silhouette drop-shadow |
frame-displace | top|bottom|end|bottom-end|top-end | move the guest toward an edge |
frame-offset | none|sm|md|lg|xl | displacement distance (named scale) |
frame-oversize | scale factor | guest exceeds its slot (clipped) |
frame-place | left top, … | alignment within the slot |
frame-anchor | object-position | crop focal point |
On a figure or showcase the frame lands on the rune itself; on a card or bento-cell it lands on the media zone. Because a card's media zone is a clipping host, a displaced or oversized guest is cropped into a peek rather than spilling out:
{% card elevation="md" frame-aspect="16/9" frame-anchor="top" %}

---
### Framed media
`frame-aspect` sets the shape; `frame-anchor` picks the focal point of the crop.
{% /card %}
{% card frame-aspect="16/9" frame-displace="top-end" frame-offset="md" frame-oversize="1.15" frame-shadow="md" %}

---
### Displaced peek + silhouette shadow
`frame-displace` + `frame-oversize` push the guest past its slot; the host clips it to a peek, and `frame-shadow` traces the cropped silhouette — not the card.
{% /card %}<div data-rune="card" data-rune-fields="{"media-position":"top"}" elevation="md">
<div data-section="media" data-name="media">
<img src="https://picsum.photos/seed/galleryframe/800/600" alt="Framed, top-anchored crop">
</div>
<div data-name="body">
<h3 id="framed-media" data-name="title">Framed media</h3>
<p>
<code>frame-aspect</code>
sets the shape;
<code>frame-anchor</code>
picks the focal point of the crop.
</p>
</div>
<meta data-field="frame-aspect" content="16/9">
<meta data-field="frame-anchor" content="top">
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-section="media" data-name="media">
<img src="https://picsum.photos/seed/gallerypeek/800/600" alt="A displaced peek with silhouette shadow">
</div>
<div data-name="body">
<h3 id="displaced-peek-+-silhouette-shadow" data-name="title">Displaced peek + silhouette shadow</h3>
<p>
<code>frame-displace</code>
+
<code>frame-oversize</code>
push the guest past its slot; the host clips it to a peek, and
<code>frame-shadow</code>
traces the cropped silhouette — not the card.
</p>
</div>
<meta data-field="frame-aspect" content="16/9">
<meta data-field="frame-displace" content="top-end">
<meta data-field="frame-offset" content="md">
<meta data-field="frame-oversize" content="1.15">
<meta data-field="frame-shadow" content="md">
</div>Framed media
frame-aspect sets the shape; frame-anchor picks the focal point of the crop.
Displaced peek + silhouette shadow
frame-displace + frame-oversize push the guest past its slot; the host clips it to a peek, and frame-shadow traces the cropped silhouette — not the card.
<div class="rf-card" data-media-position="top" data-elevation="md" data-rune="card" data-density="full">
<div data-section="media" data-name="media" class="rf-card__media" style="--frame-aspect: 16/9; --frame-anchor: top">
<img src="https://picsum.photos/seed/galleryframe/800/600" alt="Framed, top-anchored crop" />
</div>
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="framed-media" data-name="title" class="rf-card__title">Framed media</h3>
<p>
<code>frame-aspect</code>
sets the shape;
<code>frame-anchor</code>
picks the focal point of the crop.
</p>
</div>
</div>
</div>
<div class="rf-card" data-media-position="top" data-rune="card" data-density="full">
<div data-section="media" data-name="media" class="rf-card__media" data-displace="top-end" data-frame-shadow="md" style="--frame-aspect: 16/9; --frame-offset: var(--rf-spacing-md); --frame-oversize: 1.15">
<img src="https://picsum.photos/seed/gallerypeek/800/600" alt="A displaced peek with silhouette shadow" />
</div>
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="displaced-peek-+-silhouette-shadow" data-name="title" class="rf-card__title">Displaced peek + silhouette shadow</h3>
<p>
<code>frame-displace</code>
+
<code>frame-oversize</code>
push the guest past its slot; the host clips it to a peek, and
<code>frame-shadow</code>
traces the cropped silhouette — not the card.
</p>
</div>
</div>
</div>Whether a displaced or oversized guest spills out or is cropped into a peek is decided by the host, not the guest — see host-owned clip.
Fills — colour, pattern, gradient
Three fill layers paint the surface itself. tint recolours a rune to a named palette — the whole surface, border, and text shift together (and tint-mode can pin the scheme):
{% card tint="catppuccin" %}
### `tint="catppuccin"`
The card adopts the named palette — surface, border, and text in one move.
{% /card %}
{% card tint="solarized" %}
### `tint="solarized"`
Same card, a different registered tint. See [tint](/runes/tint) for the palette registry.
{% /card %}<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-name="body">
<h3 id="" data-name="title">
<code>tint="catppuccin"</code>
</h3>
<p>The card adopts the named palette — surface, border, and text in one move.</p>
</div>
<meta data-field="tint" content="catppuccin">
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-name="body">
<h3 id="" data-name="title">
<code>tint="solarized"</code>
</h3>
<p>
Same card, a different registered tint. See
<a href="/runes/tint">tint</a>
for the palette registry.
</p>
</div>
<meta data-field="tint" content="solarized">
</div>tint="catppuccin"
The card adopts the named palette — surface, border, and text in one move.
tint="solarized"
Same card, a different registered tint. See tint for the palette registry.
<div class="rf-card rf-card--tinted" data-media-position="top" data-tint="catppuccin" data-tint-dark="" data-rune="card" data-density="full" style="--tint-bg: #eff1f5; --tint-surface: #e6e9ef; --tint-text: #4c4f69; --tint-muted: #6c6f85; --tint-primary: #1e66f5; --tint-border: #dce0e8; --tint-dark-bg: #1e1e2e; --tint-dark-surface: #181825; --tint-dark-text: #cdd6f4; --tint-dark-muted: #a6adc8; --tint-dark-primary: #89b4fa; --tint-dark-border: #313244">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="" data-name="title" class="rf-card__title">
<code>tint="catppuccin"</code>
</h3>
<p>The card adopts the named palette — surface, border, and text in one move.</p>
</div>
</div>
</div>
<div class="rf-card rf-card--tinted" data-media-position="top" data-tint="solarized" data-tint-dark="" data-rune="card" data-density="full" style="--tint-bg: #fdf6e3; --tint-surface: #eee8d5; --tint-text: #657b83; --tint-muted: #93a1a1; --tint-primary: #268bd2; --tint-border: #eee8d5; --tint-dark-bg: #002b36; --tint-dark-surface: #073642; --tint-dark-text: #839496; --tint-dark-muted: #586e75; --tint-dark-primary: #268bd2; --tint-dark-border: #073642">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="" data-name="title" class="rf-card__title">
<code>tint="solarized"</code>
</h3>
<p>
Same card, a different registered tint. See
<a href="/runes/tint">tint</a>
for the palette registry.
</p>
</div>
</div>
</div>substrate prints a token-generated pattern — no image asset — from a fixed vocabulary.
| Facet | Values | Effect |
|---|---|---|
substrate | dots|grid|lines|cross|checker|none | the pattern |
substrate-size | sm|md|lg | cell size |
substrate-opacity | sm|md|lg | ink strength |
substrate-fill | inherit (default) / inset | sit on the surface, or the recessed inset fill |
A substrate fills the rune's self surface by default — a banner pattern covers the whole banner. Opt into the media well with substrate-target="media". substrate-fill="inset" lays it over a slightly recessed fill that still tracks the surface colour:
{% card substrate="dots" %}
### dots
A generated dot grid — default size and opacity.
{% /card %}
{% card substrate="grid" %}
### grid
Two tiled gradients.
{% /card %}
{% card substrate="lines" substrate-opacity="lg" %}
### lines · opacity="lg"
Diagonal hatching at heavier ink strength.
{% /card %}
{% card substrate="checker" substrate-size="lg" %}
### checker · size="lg"
Alternating filled cells, scaled up via `substrate-size`.
{% /card %}
{% card substrate="cross" substrate-fill="inset" %}
### cross · fill="inset"
Pattern over a recessed, tint-tracking fill.
{% /card %}<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-name="body">
<h3 id="dots" data-name="title">dots</h3>
<p>A generated dot grid — default size and opacity.</p>
</div>
<meta data-field="substrate" content="dots">
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-name="body">
<h3 id="grid" data-name="title">grid</h3>
<p>Two tiled gradients.</p>
</div>
<meta data-field="substrate" content="grid">
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-name="body">
<h3 id="lines-·-opacity="lg"" data-name="title">lines · opacity="lg"</h3>
<p>Diagonal hatching at heavier ink strength.</p>
</div>
<meta data-field="substrate" content="lines">
<meta data-field="substrate-opacity" content="lg">
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-name="body">
<h3 id="checker-·-size="lg"" data-name="title">checker · size="lg"</h3>
<p>
Alternating filled cells, scaled up via
<code>substrate-size</code>
.
</p>
</div>
<meta data-field="substrate" content="checker">
<meta data-field="substrate-size" content="lg">
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top"}">
<div data-name="body">
<h3 id="cross-·-fill="inset"" data-name="title">cross · fill="inset"</h3>
<p>Pattern over a recessed, tint-tracking fill.</p>
</div>
<meta data-field="substrate" content="cross">
<meta data-field="substrate-fill" content="inset">
</div>dots
A generated dot grid — default size and opacity.
grid
Two tiled gradients.
lines · opacity="lg"
Diagonal hatching at heavier ink strength.
checker · size="lg"
Alternating filled cells, scaled up via substrate-size.
cross · fill="inset"
Pattern over a recessed, tint-tracking fill.
<div class="rf-card" data-media-position="top" data-substrate="dots" data-rune="card" data-density="full">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="dots" data-name="title" class="rf-card__title">dots</h3>
<p>A generated dot grid — default size and opacity.</p>
</div>
</div>
</div>
<div class="rf-card" data-media-position="top" data-substrate="grid" data-rune="card" data-density="full">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="grid" data-name="title" class="rf-card__title">grid</h3>
<p>Two tiled gradients.</p>
</div>
</div>
</div>
<div class="rf-card" data-media-position="top" data-substrate="lines" data-rune="card" data-density="full" style="--substrate-opacity: 0.85">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="lines-·-opacity="lg"" data-name="title" class="rf-card__title">lines · opacity="lg"</h3>
<p>Diagonal hatching at heavier ink strength.</p>
</div>
</div>
</div>
<div class="rf-card" data-media-position="top" data-substrate="checker" data-rune="card" data-density="full" style="--substrate-cell: 24px">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="checker-·-size="lg"" data-name="title" class="rf-card__title">checker · size="lg"</h3>
<p>
Alternating filled cells, scaled up via
<code>substrate-size</code>
.
</p>
</div>
</div>
</div>
<div class="rf-card" data-media-position="top" data-substrate="cross" data-substrate-fill="inset" data-rune="card" data-density="full">
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="cross-·-fill="inset"" data-name="title" class="rf-card__title">cross · fill="inset"</h3>
<p>Pattern over a recessed, tint-tracking fill.</p>
</div>
</div>
</div>Gradient fills are token-driven — from/to take semantic colour names that resolve to var(--rf-color-*), so the gradient tracks the theme. A gradient-only card needs an intrinsic height to show:
{% card height="md" bg-gradient="to-br" bg-from="primary" bg-to="info" %}
### Gradient fill
A two-stop linear gradient from token colours — no image needed.
{% /card %}
{% card height="md" bg-gradient="to-t" bg-from="surface" bg-via="primary" bg-to="info" bg-gradient-type="radial" %}
### Radial, three stops
`bg-gradient-type="radial"` with a `via` middle stop.
{% /card %}<div data-rune="card" data-rune-fields="{"media-position":"top","height":"md"}">
<div data-name="body">
<h3 id="gradient-fill" data-name="title">Gradient fill</h3>
<p>A two-stop linear gradient from token colours — no image needed.</p>
</div>
<meta data-field="bg-gradient" content="to-br">
<meta data-field="bg-from" content="primary">
<meta data-field="bg-to" content="info">
</div>
<div data-rune="card" data-rune-fields="{"media-position":"top","height":"md"}">
<div data-name="body">
<h3 id="radial,-three-stops" data-name="title">Radial, three stops</h3>
<p>
<code>bg-gradient-type="radial"</code>
with a
<code>via</code>
middle stop.
</p>
</div>
<meta data-field="bg-gradient" content="to-t">
<meta data-field="bg-from" content="surface">
<meta data-field="bg-to" content="info">
<meta data-field="bg-via" content="primary">
<meta data-field="bg-gradient-type" content="radial">
</div>Gradient fill
A two-stop linear gradient from token colours — no image needed.
Radial, three stops
bg-gradient-type="radial" with a via middle stop.
<div class="rf-card rf-card--has-bg" data-media-position="top" data-height="md" data-bg="" data-rune="card" data-density="full">
<div data-name="bg" style="--bg-image: linear-gradient(to bottom right, var(--rf-color-primary), var(--rf-color-info))" class="rf-card__bg"></div>
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="gradient-fill" data-name="title" class="rf-card__title">Gradient fill</h3>
<p>A two-stop linear gradient from token colours — no image needed.</p>
</div>
</div>
</div>
<div class="rf-card rf-card--has-bg" data-media-position="top" data-height="md" data-bg="" data-rune="card" data-density="full">
<div data-name="bg" style="--bg-image: radial-gradient(var(--rf-color-surface), var(--rf-color-primary), var(--rf-color-info))" class="rf-card__bg"></div>
<div data-name="content" class="rf-card__content">
<div data-name="body" class="rf-card__body">
<h3 id="radial,-three-stops" data-name="title" class="rf-card__title">Radial, three stops</h3>
<p>
<code>bg-gradient-type="radial"</code>
with a
<code>via</code>
middle stop.
</p>
</div>
</div>
</div>The full fill vocabulary lives in tint and bg.
Cover — the poster layout
media-position="cover" is a one-attribute switch from a normal card into a poster: the media fills the interior and the content overlays it. Cover mode turns on a legibility scrim automatically, so text stays readable over any photo — a directional gradient by default, or a frosted-glass band with scrim-type="frost". content-place anchors the overlay; height (or aspect) gives the poster its shape:
{% card href="/runes/learning/recipe" media-position="cover" height="lg" %}

---
Brunch classic
### Tequila Sunrise
A bright, layered cocktail — five minutes, no shaker.
{% /card %}
{% card media-position="cover" content-place="center center" height="lg" %}

---
### Centred hero
`content-place="center center"` pins the overlay; the gradient scrim follows as a centred radial spot.
{% /card %}<div data-rune="card" data-rune-fields="{"media-position":"cover","height":"lg"}">
<div data-section="media" data-name="media">
<img src="https://assets.refrakt.md/tequila-sunrise.png" alt="A tequila sunrise cocktail">
</div>
<p data-name="eyebrow">Brunch classic</p>
<div data-name="body">
<h3 id="tequila-sunrise" data-name="title">Tequila Sunrise</h3>
<p>A bright, layered cocktail — five minutes, no shaker.</p>
</div>
<a data-name="link" href="/runes/learning/recipe" aria-hidden="true" tabindex="-1"></a>
</div>
<div data-rune="card" data-rune-fields="{"media-position":"cover","content-place":"center center","height":"lg"}">
<div data-section="media" data-name="media">
<img src="https://picsum.photos/seed/gallerycover/800/1000" alt="A coastal scene">
</div>
<div data-name="body">
<h3 id="centred-hero" data-name="title">Centred hero</h3>
<p>
<code>content-place="center center"</code>
pins the overlay; the gradient scrim follows as a centred radial spot.
</p>
</div>
</div>
Brunch classic
Tequila Sunrise
A bright, layered cocktail — five minutes, no shaker.
Centred hero
content-place="center center" pins the overlay; the gradient scrim follows as a centred radial spot.
<div class="rf-card rf-card--cover" data-media-position="cover" data-height="lg" data-rune="card" data-density="full" data-cover-scope="full">
<div data-section="media" data-name="media" class="rf-card__media" data-guest-posture="presentational">
<img src="https://assets.refrakt.md/tequila-sunrise.png" alt="A tequila sunrise cocktail" />
</div>
<div data-name="content" class="rf-card__content" data-color-scheme="dark">
<p data-name="eyebrow" class="rf-card__eyebrow">Brunch classic</p>
<div data-name="body" class="rf-card__body">
<h3 id="tequila-sunrise" data-name="title" class="rf-card__title">Tequila Sunrise</h3>
<p>A bright, layered cocktail — five minutes, no shaker.</p>
</div>
</div>
<a data-name="link" href="/runes/learning/recipe" aria-hidden="true" tabindex="-1" class="rf-card__link"></a>
</div>
<div class="rf-card rf-card--cover" data-media-position="cover" data-content-place="center center" data-height="lg" data-rune="card" data-density="full" data-cover-scope="full" style="--cover-place-block: center; --cover-place-inline: center; --cover-scrim-image: radial-gradient(ellipse farthest-side at center, rgb(0 0 0 / 0.55) 40%, transparent 100%); --cover-scrim-mask: radial-gradient(ellipse farthest-side at center, #000 50%, transparent 100%)">
<div data-section="media" data-name="media" class="rf-card__media" data-guest-posture="presentational">
<img src="https://picsum.photos/seed/gallerycover/800/1000" alt="A coastal scene" />
</div>
<div data-name="content" class="rf-card__content" data-color-scheme="dark">
<div data-name="body" class="rf-card__body">
<h3 id="centred-hero" data-name="title" class="rf-card__title">Centred hero</h3>
<p>
<code>content-place="center center"</code>
pins the overlay; the gradient scrim follows as a centred radial spot.
</p>
</div>
</div>
</div>A recipe uses header scope: the same media-position="cover" switch, but only the title block overlays the image while the ingredients and steps flow below on the page palette:
{% recipe prepTime="PT5M" servings=1 difficulty="easy" media-position="cover" scrim-type="frost" scrim-blur="md" %}

---
A cocktail classic
## Tequila Sunrise
A layered showstopper that transitions from deep orange to golden yellow.
- 60ml tequila
- 120ml fresh orange juice
- 15ml grenadine
- Orange slice and cherry to garnish
1. Fill a tall glass with ice; pour in tequila and orange juice. Stir gently.
2. Slowly pour grenadine over the back of a spoon so it sinks.
3. Let the layers settle, then garnish.
{% /recipe %}<article data-field="content-section" data-rune="recipe" typeof="Recipe" data-rune-fields="{"prepTime":"PT5M","cookTime":"","servings":"1","difficulty":"easy","media-position":"cover"}">
<meta content="PT5M" property="prepTime">
<meta content="1" property="recipeYield">
<div data-name="media">
<img src="https://assets.refrakt.md/tequila-sunrise.png" alt="A tequila sunrise cocktail" property="image">
</div>
<p data-name="eyebrow">A cocktail classic</p>
<h2 id="tequila-sunrise" data-name="headline" property="name">Tequila Sunrise</h2>
<p data-name="blurb" property="description">A layered showstopper that transitions from deep orange to golden yellow.</p>
<ul data-name="ingredients">
<li data-name="ingredient" property="recipeIngredient">60ml tequila</li>
<li data-name="ingredient" property="recipeIngredient">120ml fresh orange juice</li>
<li data-name="ingredient" property="recipeIngredient">15ml grenadine</li>
<li data-name="ingredient" property="recipeIngredient">Orange slice and cherry to garnish</li>
</ul>
<ol data-name="steps">
<li data-name="step" typeof="HowToStep" property="recipeInstructions">
<p property="text">Fill a tall glass with ice; pour in tequila and orange juice. Stir gently.</p>
</li>
<li data-name="step" typeof="HowToStep" property="recipeInstructions">
<p property="text">Slowly pour grenadine over the back of a spoon so it sinks.</p>
</li>
<li data-name="step" typeof="HowToStep" property="recipeInstructions">
<p property="text">Let the layers settle, then garnish.</p>
</li>
</ol>
<meta data-field="scrim-type" content="frost">
<meta data-field="scrim-blur" content="md">
</article>
A cocktail classic
Tequila Sunrise
A layered showstopper that transitions from deep orange to golden yellow.
- 60ml tequila
- 120ml fresh orange juice
- 15ml grenadine
- Orange slice and cherry to garnish
Fill a tall glass with ice; pour in tequila and orange juice. Stir gently.
Slowly pour grenadine over the back of a spoon so it sinks.
Let the layers settle, then garnish.
<article data-field="content-section" typeof="Recipe" class="rf-recipe rf-recipe--easy rf-recipe--cover" data-media-position="cover" data-prep-time="PT5M" data-cook-time="" data-servings="1" data-difficulty="easy" data-scrim-type="frost" data-scrim-blur="md" data-rune="recipe" data-density="full" data-cover-scope="header">
<div data-name="cover-band" data-color-scheme="dark" class="rf-recipe__cover-band">
<div data-name="media" class="rf-recipe__media" data-section="media" data-media="cover" data-guest-posture="presentational">
<img src="https://assets.refrakt.md/tequila-sunrise.png" alt="A tequila sunrise cocktail" property="image" />
</div>
<header data-name="preamble" class="rf-recipe__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-recipe__eyebrow">A cocktail classic</p>
<h2 id="tequila-sunrise" data-name="headline" property="name" class="rf-recipe__headline" data-section="title">Tequila Sunrise</h2>
<p data-name="blurb" property="description" class="rf-recipe__blurb" data-section="description">A layered showstopper that transitions from deep orange to golden yellow.</p>
</header>
</div>
<div data-name="content" class="rf-recipe__content">
<dl data-name="metadata" data-zone="metadata" data-zone-layout="definition-list" class="rf-recipe__metadata">
<div data-name="row" data-field="prepTime" class="rf-recipe__row">
<dt data-meta-label="">Prep</dt>
<dd data-meta-type="temporal">5m</dd>
</div>
<div data-name="row" data-field="servings" class="rf-recipe__row">
<dt data-meta-label="">Serves</dt>
<dd data-meta-type="quantity">1</dd>
</div>
<div data-name="row" data-field="difficulty" class="rf-recipe__row">
<dt data-meta-label="">Difficulty</dt>
<dd>
<span class="rf-badge" data-meta-type="category" data-meta-sentiment="positive">easy</span>
</dd>
</div>
</dl>
<ul data-name="ingredients" class="rf-recipe__ingredients">
<li data-name="ingredient" property="recipeIngredient" class="rf-recipe__ingredient">60ml tequila</li>
<li data-name="ingredient" property="recipeIngredient" class="rf-recipe__ingredient">120ml fresh orange juice</li>
<li data-name="ingredient" property="recipeIngredient" class="rf-recipe__ingredient">15ml grenadine</li>
<li data-name="ingredient" property="recipeIngredient" class="rf-recipe__ingredient">Orange slice and cherry to garnish</li>
</ul>
<ol data-name="steps" class="rf-recipe__steps" data-sequence="numbered">
<li data-name="step" typeof="HowToStep" property="recipeInstructions" class="rf-recipe__step">
<p property="text">Fill a tall glass with ice; pour in tequila and orange juice. Stir gently.</p>
</li>
<li data-name="step" typeof="HowToStep" property="recipeInstructions" class="rf-recipe__step">
<p property="text">Slowly pour grenadine over the back of a spoon so it sinks.</p>
</li>
<li data-name="step" typeof="HowToStep" property="recipeInstructions" class="rf-recipe__step">
<p property="text">Let the layers settle, then garnish.</p>
</li>
</ol>
</div>
<meta content="PT5M" property="prepTime" />
<meta content="1" property="recipeYield" />
</article>The cover scrim, content-place, and scrim-type are documented on card → cover mode.
Composition
Every dial here composes with a media guest, not just an image — a codegroup, chart, or map in a card's media zone takes the same frame, substrate, and cover treatments. Those patterns (including a displaced codegroup over a substrate, and a linked cover poster whose guest is demoted to a backdrop) live in Media guests; the demotion rule is the interaction-posture contract.
See also
- tint · bg — the colour and image/gradient fill layers.
- card → cover mode and recipe → cover mode — the poster layouts.
- media-guest interaction posture — the demotion contract.
- Surface model — the theme-side configuration:
frame/bgpreset registries,frameTarget/substrateTargetrouting, the inset token, host-owned clip, and the substrate ownership split.