This rune is part of @refrakt-md/marketing. Install with npm install @refrakt-md/marketing and add "@refrakt-md/marketing" to the plugins array in your refrakt.config.json.
Feature
Feature showcases. List items become feature definitions — bold text is the feature name, the following paragraph is the description.
Basic usage
A feature grid with named items and descriptions.
{% feature %}
what you get
## Structured data
- **Semantic runes**
Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.
- **Type-safe output**
Every rune produces typed, validated content that your theme components can rely on.
- **Layout inheritance**
Define regions once in a parent layout. Child pages inherit and can override with prepend, append, or replace modes.
{% /feature %}<section data-field="content-section" data-rune="feature" data-rune-fields="{"media-position":"bottom","align":"center"}">
<div data-name="content">
<header>
<p data-name="eyebrow">what you get</p>
<h2 id="structured-data" data-name="headline">Structured data</h2>
</header>
<dl data-columns="3">
<div data-name="feature-item">
<dt>
<span data-name="title">Semantic runes</span>
</dt>
<dd data-name="description">Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.</dd>
</div>
<div data-name="feature-item">
<dt>
<span data-name="title">Type-safe output</span>
</dt>
<dd data-name="description">Every rune produces typed, validated content that your theme components can rely on.</dd>
</div>
<div data-name="feature-item">
<dt>
<span data-name="title">Layout inheritance</span>
</dt>
<dd data-name="description">Define regions once in a parent layout. Child pages inherit and can override with prepend, append, or replace modes.</dd>
</div>
</dl>
</div>
</section>what you get
Structured data
- Semantic runes
- Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.
- Type-safe output
- Every rune produces typed, validated content that your theme components can rely on.
- Layout inheritance
- Define regions once in a parent layout. Child pages inherit and can override with prepend, append, or replace modes.
<section data-field="content-section" class="rf-feature rf-feature--center rf-feature--definitions-grid rf-feature--full" data-media-position="bottom" data-align="center" data-width="full" data-rune="feature" data-density="full">
<div data-name="content" class="rf-feature__content">
<header data-name="preamble" class="rf-feature__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-feature__eyebrow">what you get</p>
<h2 id="structured-data" data-name="headline" class="rf-feature__headline" data-section="title">Structured data</h2>
</header>
<dl data-columns="3" data-name="definitions" class="rf-feature__definitions">
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Semantic runes</span>
</dt>
<dd data-name="description" class="rf-feature__description">Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.</dd>
</div>
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Type-safe output</span>
</dt>
<dd data-name="description" class="rf-feature__description">Every rune produces typed, validated content that your theme components can rely on.</dd>
</div>
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Layout inheritance</span>
</dt>
<dd data-name="description" class="rf-feature__description">Define regions once in a parent layout. Child pages inherit and can override with prepend, append, or replace modes.</dd>
</div>
</dl>
</div>
</section>With heading and description
Add a paragraph after the heading to introduce the feature set.
{% feature %}
what you get
## Built for content teams
Refrakt gives you the building blocks to ship structured content sites without fighting your framework.
- **Semantic runes**
Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.
- **Type-safe output**
Every rune produces typed, validated content that your theme components can rely on.
{% /feature %}<section data-field="content-section" data-rune="feature" data-rune-fields="{"media-position":"bottom","align":"center"}">
<div data-name="content">
<header>
<p data-name="eyebrow">what you get</p>
<h2 id="built-for-content-teams" data-name="headline">Built for content teams</h2>
<p data-name="blurb">Refrakt gives you the building blocks to ship structured content sites without fighting your framework.</p>
</header>
<dl data-columns="2">
<div data-name="feature-item">
<dt>
<span data-name="title">Semantic runes</span>
</dt>
<dd data-name="description">Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.</dd>
</div>
<div data-name="feature-item">
<dt>
<span data-name="title">Type-safe output</span>
</dt>
<dd data-name="description">Every rune produces typed, validated content that your theme components can rely on.</dd>
</div>
</dl>
</div>
</section>what you get
Built for content teams
Refrakt gives you the building blocks to ship structured content sites without fighting your framework.
- Semantic runes
- Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.
- Type-safe output
- Every rune produces typed, validated content that your theme components can rely on.
<section data-field="content-section" class="rf-feature rf-feature--center rf-feature--definitions-grid rf-feature--full" data-media-position="bottom" data-align="center" data-width="full" data-rune="feature" data-density="full">
<div data-name="content" class="rf-feature__content">
<header data-name="preamble" class="rf-feature__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-feature__eyebrow">what you get</p>
<h2 id="built-for-content-teams" data-name="headline" class="rf-feature__headline" data-section="title">Built for content teams</h2>
<p data-name="blurb" class="rf-feature__blurb" data-section="description">Refrakt gives you the building blocks to ship structured content sites without fighting your framework.</p>
</header>
<dl data-columns="2" data-name="definitions" class="rf-feature__definitions">
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Semantic runes</span>
</dt>
<dd data-name="description" class="rf-feature__description">Markdown primitives take on different meaning depending on the wrapping rune. Write Markdown — the rune decides what it means.</dd>
</div>
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Type-safe output</span>
</dt>
<dd data-name="description" class="rf-feature__description">Every rune produces typed, validated content that your theme components can rely on.</dd>
</div>
</dl>
</div>
</section>Media on the right (end)
Use media-position="end" to place definitions alongside a media column — an image, code block, or any other content. A horizontal rule (---) separates the media block (first) from the content block (second).
{% feature media-position="end" %}
{% codegroup %}
```yaml title="refrakt.config.ts"
export default {
content: './content',
theme: '@refrakt-md/lumina'
}
```
```md title="content/index.md"
---
title: Home
---
{% hero %}
# Welcome
{% /hero %}
```
{% /codegroup %}
---
why Refrakt
## Built for versatility
- **Zero config**
Drop Markdown files into your content directory. Routing, layouts, and type generation happen automatically.
- **Framework agnostic**
The identity transform is pure data. Render with Svelte, React, or anything else.
{% /feature %}<section data-field="content-section" data-rune="feature" data-rune-fields="{"media-position":"end","align":"center"}">
<div data-name="content">
<header>
<p data-name="eyebrow">why Refrakt</p>
<h2 id="built-for-versatility" data-name="headline">Built for versatility</h2>
</header>
<dl data-columns="2">
<div data-name="feature-item">
<dt>
<span data-name="title">Zero config</span>
</dt>
<dd data-name="description">Drop Markdown files into your content directory. Routing, layouts, and type generation happen automatically.</dd>
</div>
<div data-name="feature-item">
<dt>
<span data-name="title">Framework agnostic</span>
</dt>
<dd data-name="description">The identity transform is pure data. Render with Svelte, React, or anything else.</dd>
</div>
</dl>
</div>
<div data-name="media">
<section data-rune="code-group">
<div role="tablist" data-name="tabs">
<button data-name="tab" role="tab">
<span>YAML</span>
</button>
<button data-name="tab" role="tab">
<span>Md</span>
</button>
</div>
<div data-name="panels">
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="yaml">
<code data-language="yaml">export default {
content: './content',
theme: '@refrakt-md/lumina'
}
</code>
</pre>
</div>
</div>
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="md">
<code data-language="md">---
title: Home
---
{% hero %}
# Welcome
{% /hero %}
</code>
</pre>
</div>
</div>
</div>
</section>
</div>
</section>why Refrakt
Built for versatility
- Zero config
- Drop Markdown files into your content directory. Routing, layouts, and type generation happen automatically.
- Framework agnostic
- The identity transform is pure data. Render with Svelte, React, or anything else.
export default {
content: './content',
theme: '@refrakt-md/lumina'
}
---
title: Home
---
{% hero %}
# Welcome
{% /hero %}
<section data-field="content-section" class="rf-feature rf-feature--center rf-feature--full" data-media-position="end" data-align="center" data-width="full" data-rune="feature" data-density="full">
<div data-name="content" class="rf-feature__content">
<header data-name="preamble" class="rf-feature__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-feature__eyebrow">why Refrakt</p>
<h2 id="built-for-versatility" data-name="headline" class="rf-feature__headline" data-section="title">Built for versatility</h2>
</header>
<dl data-columns="2" data-name="definitions" class="rf-feature__definitions">
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Zero config</span>
</dt>
<dd data-name="description" class="rf-feature__description">Drop Markdown files into your content directory. Routing, layouts, and type generation happen automatically.</dd>
</div>
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Framework agnostic</span>
</dt>
<dd data-name="description" class="rf-feature__description">The identity transform is pure data. Render with Svelte, React, or anything else.</dd>
</div>
</dl>
</div>
<div data-name="media" class="rf-feature__media" data-section="media" data-media="cover">
<section class="rf-codegroup rf-codegroup--scroll" data-overflow="scroll" data-rune="code-group" data-density="compact" data-code-host="true">
<div role="tablist" data-name="tabs" class="rf-codegroup__tabs">
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>YAML</span>
</button>
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>Md</span>
</button>
</div>
<div data-name="panels" class="rf-codegroup__panels">
<div role="tabpanel" data-name="panel" class="rf-codegroup__panel">
<div class="rf-codeblock">
<pre data-language="yaml"><code data-language="yaml">export default {
content: './content',
theme: '@refrakt-md/lumina'
}
</code></pre>
</div>
</div>
<div role="tabpanel" data-name="panel" class="rf-codegroup__panel">
<div class="rf-codeblock">
<pre data-language="md"><code data-language="md">---
title: Home
---
{% hero %}
# Welcome
{% /hero %}
</code></pre>
</div>
</div>
</div>
</section>
</div>
</section>Media on the left (start)
Use media-position="start" to swap the column order — media on the left, definitions on the right.
{% feature media-position="start" %}
{% codegroup %}
```ts title="transform.ts"
const ast = Markdoc.parse(content);
const tree = Markdoc.transform(ast, {
tags,
nodes
});
```
```html title="output.html"
<section class="rf-hero">
<header class="rf-hero__body">
<h1>Welcome</h1>
</header>
</section>
```
{% /codegroup %}
---
pipeline
## How it works
- **Parse**
Markdoc turns your Markdown into an AST.
- **Transform**
Rune schemas reinterpret the AST nodes based on context.
- **Render**
The identity transform adds BEM classes and structural elements. Your theme takes it from there.
{% /feature %}<section data-field="content-section" data-rune="feature" data-rune-fields="{"media-position":"start","align":"center"}">
<div data-name="content">
<header>
<p data-name="eyebrow">pipeline</p>
<h2 id="how-it-works" data-name="headline">How it works</h2>
</header>
<dl data-columns="3">
<div data-name="feature-item">
<dt>
<span data-name="title">Parse</span>
</dt>
<dd data-name="description">Markdoc turns your Markdown into an AST.</dd>
</div>
<div data-name="feature-item">
<dt>
<span data-name="title">Transform</span>
</dt>
<dd data-name="description">Rune schemas reinterpret the AST nodes based on context.</dd>
</div>
<div data-name="feature-item">
<dt>
<span data-name="title">Render</span>
</dt>
<dd data-name="description">The identity transform adds BEM classes and structural elements. Your theme takes it from there.</dd>
</div>
</dl>
</div>
<div data-name="media">
<section data-rune="code-group">
<div role="tablist" data-name="tabs">
<button data-name="tab" role="tab">
<span>TypeScript</span>
</button>
<button data-name="tab" role="tab">
<span>HTML</span>
</button>
</div>
<div data-name="panels">
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="ts">
<code data-language="ts">const ast = Markdoc.parse(content);
const tree = Markdoc.transform(ast, {
tags,
nodes
});
</code>
</pre>
</div>
</div>
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="html">
<code data-language="html"><section class="rf-hero">
<header class="rf-hero__body">
<h1>Welcome</h1>
</header>
</section>
</code>
</pre>
</div>
</div>
</div>
</section>
</div>
</section>pipeline
How it works
- Parse
- Markdoc turns your Markdown into an AST.
- Transform
- Rune schemas reinterpret the AST nodes based on context.
- Render
- The identity transform adds BEM classes and structural elements. Your theme takes it from there.
const ast = Markdoc.parse(content);
const tree = Markdoc.transform(ast, {
tags,
nodes
});
<section class="rf-hero">
<header class="rf-hero__body">
<h1>Welcome</h1>
</header>
</section>
<section data-field="content-section" class="rf-feature rf-feature--center rf-feature--full" data-media-position="start" data-align="center" data-width="full" data-rune="feature" data-density="full">
<div data-name="content" class="rf-feature__content">
<header data-name="preamble" class="rf-feature__preamble" data-section="preamble">
<p data-name="eyebrow" class="rf-feature__eyebrow">pipeline</p>
<h2 id="how-it-works" data-name="headline" class="rf-feature__headline" data-section="title">How it works</h2>
</header>
<dl data-columns="3" data-name="definitions" class="rf-feature__definitions">
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Parse</span>
</dt>
<dd data-name="description" class="rf-feature__description">Markdoc turns your Markdown into an AST.</dd>
</div>
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Transform</span>
</dt>
<dd data-name="description" class="rf-feature__description">Rune schemas reinterpret the AST nodes based on context.</dd>
</div>
<div data-name="feature-item" class="rf-feature__feature-item">
<dt>
<span data-name="title">Render</span>
</dt>
<dd data-name="description" class="rf-feature__description">The identity transform adds BEM classes and structural elements. Your theme takes it from there.</dd>
</div>
</dl>
</div>
<div data-name="media" class="rf-feature__media" data-section="media" data-media="cover">
<section class="rf-codegroup rf-codegroup--scroll" data-overflow="scroll" data-rune="code-group" data-density="compact" data-code-host="true">
<div role="tablist" data-name="tabs" class="rf-codegroup__tabs">
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>TypeScript</span>
</button>
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>HTML</span>
</button>
</div>
<div data-name="panels" class="rf-codegroup__panels">
<div role="tabpanel" data-name="panel" class="rf-codegroup__panel">
<div class="rf-codeblock">
<pre data-language="ts"><code data-language="ts">const ast = Markdoc.parse(content);
const tree = Markdoc.transform(ast, {
tags,
nodes
});
</code></pre>
</div>
</div>
<div role="tabpanel" data-name="panel" class="rf-codegroup__panel">
<div class="rf-codeblock">
<pre data-language="html"><code data-language="html"><section class="rf-hero">
<header class="rf-hero__body">
<h1>Welcome</h1>
</header>
</section>
</code></pre>
</div>
</div>
</div>
</section>
</div>
</section>Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
align | string | center | Horizontal alignment of headline + body text: left, center, right |
Section header
Feature supports an optional eyebrow, headline, and blurb above the section above feature items. Place a short paragraph or heading before the main content to use them. See Page sections for the full syntax.
Layout attributes
The body splits on --- into media → content zones (media-first in source). media-position controls visual placement independently of source order. In beside layouts (start/end), the definitions stack vertically inside the content column; in top/bottom, they tile as a grid.
| Attribute | Type | Default | Description |
|---|---|---|---|
media-position | string | bottom | Where the media sits: top, bottom (the default — media beneath the text), start (left), end (right) |
media-ratio | string | — | Media's share of the row when beside content (start/end): 1/3, 2/5, 1/2, 3/5, 2/3 |
valign | string | — | Cross-axis alignment when media is beside content: top, center, bottom, stretch |
collapse | string | — | Breakpoint at which beside layouts collapse to a stack: sm, md, lg, never |
Common attributes
All block runes share these attributes for layout and theming.
| Attribute | Type | Default | Description |
|---|---|---|---|
width | string | content | Page grid width: content, wide, or full |
spacing | string | — | Vertical spacing: flush, tight, default, loose, or breathe |
inset | string | — | Horizontal padding: flush, tight, default, loose, or breathe |
tint | string | — | Named colour tint from theme configuration |
tint-mode | string | auto | Colour scheme override: auto, dark, or light |
bg | string | — | Named background preset from theme configuration |