note

This rune is part of @refrakt-md/marketing. Install with npm install @refrakt-md/marketing and add "@refrakt-md/marketing" to the packages 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">
  <meta content="stacked" data-field="layout">
  <meta content="center" data-field="align">
  <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-layout="grid" 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--stacked rf-feature--center rf-feature--full" data-layout="stacked" data-align="center" data-ratio="1 1" data-valign="top" data-gap="default" data-width="full" data-rune="feature" data-density="full" style="--split-ratio: 1fr 1fr; --split-valign: start; --split-gap: var(--rf-spacing-md)">
  <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-layout="grid" 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">
  <meta content="stacked" data-field="layout">
  <meta content="center" data-field="align">
  <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-layout="grid" 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--stacked rf-feature--center rf-feature--full" data-layout="stacked" data-align="center" data-ratio="1 1" data-valign="top" data-gap="default" data-width="full" data-rune="feature" data-density="full" style="--split-ratio: 1fr 1fr; --split-valign: start; --split-gap: var(--rf-spacing-md)">
  <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-layout="grid" 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>

Split layout

Use layout="split" to place definitions alongside a media column — an image, code block, or any other content. A horizontal rule (---) separates the two sections.

{% feature layout="split" %}
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.

---

{% 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 %}
{% /feature %}
<section data-field="content-section" data-rune="feature">
  <meta content="split" data-field="layout">
  <meta content="center" data-field="align">
  <meta content="1 1" data-field="ratio">
  <meta content="top" data-field="valign">
  <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>
      <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--split rf-feature--center rf-feature--full" data-layout="split" data-align="center" data-ratio="1 1" data-valign="top" data-gap="default" data-width="full" data-rune="feature" data-density="full" style="--split-ratio: 1fr 1fr; --split-valign: start; --split-gap: var(--rf-spacing-md)">
  <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-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">
      <div data-name="topbar" class="rf-codegroup__topbar" data-section="header">
        <span data-name="dot" class="rf-codegroup__dot"></span>
        <span data-name="dot" class="rf-codegroup__dot"></span>
        <span data-name="dot" class="rf-codegroup__dot"></span>
      </div>
      <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>

Split reversed

Use layout="split-reverse" to swap the column order — media on the left, definitions on the right.

{% feature layout="split-reverse" %}
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.

---

{% 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 %}
{% /feature %}
<section data-field="content-section" data-rune="feature">
  <meta content="split-reverse" data-field="layout">
  <meta content="center" data-field="align">
  <meta content="1 1" data-field="ratio">
  <meta content="top" data-field="valign">
  <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>
      <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">&lt;section class=&quot;rf-hero&quot;&gt;
  &lt;header class=&quot;rf-hero__body&quot;&gt;
    &lt;h1&gt;Welcome&lt;/h1&gt;
  &lt;/header&gt;
&lt;/section&gt;
</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--split-reverse rf-feature--center rf-feature--full" data-layout="split-reverse" data-align="center" data-ratio="1 1" data-valign="top" data-gap="default" data-width="full" data-rune="feature" data-density="full" style="--split-ratio: 1fr 1fr; --split-valign: start; --split-gap: var(--rf-spacing-md)">
  <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-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">
      <div data-name="topbar" class="rf-codegroup__topbar" data-section="header">
        <span data-name="dot" class="rf-codegroup__dot"></span>
        <span data-name="dot" class="rf-codegroup__dot"></span>
        <span data-name="dot" class="rf-codegroup__dot"></span>
      </div>
      <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">&lt;section class=&quot;rf-hero&quot;&gt;
  &lt;header class=&quot;rf-hero__body&quot;&gt;
    &lt;h1&gt;Welcome&lt;/h1&gt;
  &lt;/header&gt;
&lt;/section&gt;
</code></pre>
          </div>
        </div>
      </div>
    </section>
  </div>
</section>

Attributes

AttributeTypeDefaultDescription
layoutstringstackedLayout mode: stacked, split, or split-reverse
alignstringcenterContent alignment: left, center, or right
ratiostring1 1Column width ratio in split layout (e.g., 2 1, 1 2)
valignstringtopVertical alignment in split layout: top, center, or bottom
gapstringdefaultGap between columns: none, tight, default, or loose
collapsestringCollapse to single column at breakpoint: sm, md, lg, or never

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.

Common attributes

All block runes share these attributes for layout and theming.

AttributeTypeDefaultDescription
widthstringcontentPage grid width: content, wide, or full
spacingstringVertical spacing: flush, tight, default, loose, or breathe
insetstringHorizontal padding: flush, tight, default, loose, or breathe
tintstringNamed colour tint from theme configuration
tint-modestringautoColour scheme override: auto, dark, or light
bgstringNamed background preset from theme configuration