MarketingFeature
note

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="{&quot;media-position&quot;:&quot;bottom&quot;,&quot;layout&quot;:&quot;grid&quot;,&quot;align&quot;:&quot;center&quot;}">
  <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--full" data-media-position="bottom" data-layout="grid" data-align="center" data-width="full" data-content-measure="anchored" data-elevation="flush" 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="items" class="rf-feature__items">
      <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="{&quot;media-position&quot;:&quot;bottom&quot;,&quot;layout&quot;:&quot;grid&quot;,&quot;align&quot;:&quot;center&quot;}">
  <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--full" data-media-position="bottom" data-layout="grid" data-align="center" data-width="full" data-content-measure="anchored" data-elevation="flush" 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="items" class="rf-feature__items">
      <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="{&quot;media-position&quot;:&quot;end&quot;,&quot;layout&quot;:&quot;list&quot;,&quot;align&quot;:&quot;center&quot;}">
  <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-layout="list" data-align="center" data-width="full" data-content-measure="anchored" data-elevation="flush" 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="items" class="rf-feature__items">
      <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" data-guest-fit="bleed">
    <section class="rf-codegroup rf-codegroup--scroll" data-overflow="scroll" data-elevation="flat" 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="{&quot;media-position&quot;:&quot;start&quot;,&quot;layout&quot;:&quot;list&quot;,&quot;align&quot;:&quot;center&quot;}">
  <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">&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--center rf-feature--full" data-media-position="start" data-layout="list" data-align="center" data-width="full" data-content-measure="anchored" data-elevation="flush" 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="items" class="rf-feature__items">
      <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" data-guest-fit="bleed">
    <section class="rf-codegroup rf-codegroup--scroll" data-overflow="scroll" data-elevation="flat" 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">&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>

Item arrangement (layout)

The layout axis controls how feature-items are arranged, independently of where the media sits:

  • layout="grid" — items tile as a grid.
  • layout="list" — items stack in a single column.
  • layout="carousel" — items lay out on a horizontal scroll-snap track with prev/next nav (the shared carousel layout mode), at every viewport.

When layout is unset it derives from media-position (stacked media → grid, beside media → list), so existing content is unchanged. Set it explicitly to override. For example, layout="carousel" lays the feature-items out on a horizontal scroll-snap track with prev/next nav, at every viewport:

{% feature layout="carousel" %}

why Refrakt

## Built for versatility

- **Zero config**

  Drop Markdown files into your content directory.

- **Framework agnostic**

  The identity transform is pure data — render with anything.

- **Type-safe output**

  Every rune produces validated content.

- **Incremental adoption**

  Add runes to existing Markdown one block at a time.
{% /feature %}
<section data-field="content-section" data-rune="feature" data-rune-fields="{&quot;media-position&quot;:&quot;bottom&quot;,&quot;layout&quot;:&quot;carousel&quot;,&quot;align&quot;:&quot;center&quot;}">
  <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="4">
      <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.</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 anything.</dd>
      </div>
      <div data-name="feature-item">
        <dt>
          <span data-name="title">Type-safe output</span>
        </dt>
        <dd data-name="description">Every rune produces validated content.</dd>
      </div>
      <div data-name="feature-item">
        <dt>
          <span data-name="title">Incremental adoption</span>
        </dt>
        <dd data-name="description">Add runes to existing Markdown one block at a time.</dd>
      </div>
    </dl>
  </div>
</section>

why Refrakt

Built for versatility

Zero config
Drop Markdown files into your content directory.
Framework agnostic
The identity transform is pure data — render with anything.
Type-safe output
Every rune produces validated content.
Incremental adoption
Add runes to existing Markdown one block at a time.
<section data-field="content-section" class="rf-feature rf-feature--center rf-feature--full" data-media-position="bottom" data-layout="carousel" data-align="center" data-width="full" data-content-measure="anchored" data-elevation="flush" 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="4" data-name="items" class="rf-feature__items">
      <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.</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 anything.</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 validated content.</dd>
      </div>
      <div data-name="feature-item" class="rf-feature__feature-item">
        <dt>
          <span data-name="title">Incremental adoption</span>
        </dt>
        <dd data-name="description" class="rf-feature__description">Add runes to existing Markdown one block at a time.</dd>
      </div>
    </dl>
  </div>
</section>

A grid is great when the feature has room, but cramped on a phone. Add collapse-to="carousel" to make the collapsed form a swipeable scroll-snap row instead of a stack — a grid on desktop, a carousel on mobile. The breakpoint is the feature's own width (a container query), so it also flips when the feature sits in a narrow column. Switch the viewport selector to mobile to see it flip:

{% feature layout="grid" collapse-to="carousel" %}

why Refrakt

## Built for versatility

- **Zero config**

  Drop Markdown files into your content directory.

- **Framework agnostic**

  The identity transform is pure data — render with anything.

- **Type-safe output**

  Every rune produces validated content.

- **Incremental adoption**

  Add runes to existing Markdown one block at a time.
{% /feature %}
<section data-field="content-section" data-rune="feature" data-rune-fields="{&quot;media-position&quot;:&quot;bottom&quot;,&quot;layout&quot;:&quot;grid&quot;,&quot;collapse-to&quot;:&quot;carousel&quot;,&quot;align&quot;:&quot;center&quot;}">
  <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="4">
      <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.</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 anything.</dd>
      </div>
      <div data-name="feature-item">
        <dt>
          <span data-name="title">Type-safe output</span>
        </dt>
        <dd data-name="description">Every rune produces validated content.</dd>
      </div>
      <div data-name="feature-item">
        <dt>
          <span data-name="title">Incremental adoption</span>
        </dt>
        <dd data-name="description">Add runes to existing Markdown one block at a time.</dd>
      </div>
    </dl>
  </div>
</section>

why Refrakt

Built for versatility

Zero config
Drop Markdown files into your content directory.
Framework agnostic
The identity transform is pure data — render with anything.
Type-safe output
Every rune produces validated content.
Incremental adoption
Add runes to existing Markdown one block at a time.
<section data-field="content-section" class="rf-feature rf-feature--center rf-feature--full" data-media-position="bottom" data-layout="grid" data-collapse-to="carousel" data-align="center" data-width="full" data-content-measure="anchored" data-elevation="flush" 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="4" data-name="items" class="rf-feature__items">
      <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.</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 anything.</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 validated content.</dd>
      </div>
      <div data-name="feature-item" class="rf-feature__feature-item">
        <dt>
          <span data-name="title">Incremental adoption</span>
        </dt>
        <dd data-name="description" class="rf-feature__description">Add runes to existing Markdown one block at a time.</dd>
      </div>
    </dl>
  </div>
</section>

Attributes

AttributeTypeRequiredDescription
align"left" | "center" | "right"Horizontal alignment of headline and body text
layout"grid" | "list" | "carousel"Arrangement of feature-items: grid (tiled), list (single column), or carousel (scroll-snap track). Defaults from media-position when unset.
collapse-to"stack" | "carousel"Collapsed (mobile) form below the `collapse` breakpoint: stack (default) or carousel (a scroll-snap row).
justifystring

Inherited from the split layout preset

AttributeTypeRequiredDescription
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.

AttributeTypeRequiredDescription
bgstringBackground preset applied to this block
bg-fromstringGradient 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-tostringGradient end colour — a semantic token name
bg-viastringOptional 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)
elevation

The chrome/depth ladder (SPEC-107). The skin maps each rung to a chrome bundle by attribute, so there is no BEM class.

AttributeTypeRequiredDescription
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.

AttributeTypeRequiredDescription
framestringNamed frame preset presenting this block's media surface
frame-anchorstringCrop focal point when the guest is cut (object-position)
frame-aspectstringAspect ratio of the framed media, e.g. "16/9"
frame-displace"top" | "bottom" | "end" | "bottom-end" | "top-end"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-oversizestringHow far the guest exceeds its slot (scale factor)
frame-placestringGuest-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.

AttributeTypeRequiredDescription
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.

AttributeTypeRequiredDescription
reveal"none" | "fade" | "slide" | "scale" | "blur"Scroll-reveal entrance character (none|fade|slide|scale|blur); the theme owns the choreography
staggerbooleanCascade 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.

AttributeTypeRequiredDescription
prominence"quiet" | "normal" | "prominent" | "display"Section-header emphasis (only on page-section-header family runes)
spacing

Block-level rhythm override.

AttributeTypeRequiredDescription
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.

AttributeTypeRequiredDescription
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.

AttributeTypeRequiredDescription
tintstringColor 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.

AttributeTypeRequiredDescription
width"compact" | "narrow" | "content" | "wide" | "full"Maximum width constraint for this block
Not availableWhy
dropcap, readingthis rune declares no prose body

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. Item arrangement (grid vs. single column) is the separate layout axis — when unset it derives from media-position (stacked → grid, beside → list), but the two are independently controllable.

AttributeTypeDefaultDescription
media-positionstringbottomWhere the media sits: top, bottom (the default — media beneath the text), start (left), end (right)
media-ratiostringMedia's share of the row when beside content (start/end): 1/3, 2/5, 1/2, 3/5, 2/3
valignstringCross-axis alignment when media is beside content: top, center, bottom, stretch
collapsestringBreakpoint at which beside layouts collapse to a stack and a grid of items reflows to one column: sm, md, lg, never