Code & DataDiff

Diff

Renders the difference between two code blocks. The first code block is the "before" state, the second is the "after" state.

Split diff

Use mode="split" to show before and after side by side. Lines that match appear on both sides, removed lines carry a red edge on the left, and added lines carry a green edge on the right — the column they sit in disambiguates direction, so no per-column label is needed. Pass an optional title to render a filename or context line as a full-width header above the diff.

{% diff mode="split" language="javascript" title="src/server.js" %}
```javascript
import express from 'express';

const app = express();

app.get('/users', (req, res) => {
  const users = db.query('SELECT * FROM users');
  res.json(users);
});

app.listen(3000);
```

```javascript
import express from 'express';

const app = express();
app.use(express.json());

app.get('/users', async (req, res) => {
  const { page = 1, limit = 20 } = req.query;
  const users = await db.query('SELECT * FROM users LIMIT ? OFFSET ?', [limit, (page - 1) * limit]);
  res.json({ users, page, limit });
});

app.listen(3000);
```
{% /diff %}
<div data-rune="diff" data-rune-fields="{&quot;mode&quot;:&quot;split&quot;,&quot;language&quot;:&quot;javascript&quot;}" data-code-host>
  <div data-name="header">src/server.js</div>
  <div data-name="split-container">
    <div data-name="panel">
      <pre data-name="code" data-copy-selector="[data-name=&quot;line-content&quot;]">
        <div data-name="rows">
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="before">1</span>
            <span data-name="line-content" data-language="javascript">import express from 'express';</span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="before">2</span>
            <span data-name="line-content" data-language="javascript"></span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="before">3</span>
            <span data-name="line-content" data-language="javascript">const app = express();</span>
          </span>
          <span data-name="line" data-line-status="empty">
            <span data-name="gutter-num" data-side="before"></span>
            <span data-name="line-content"></span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="before">4</span>
            <span data-name="line-content" data-language="javascript"></span>
          </span>
          <span data-name="line" data-line-status="remove">
            <span data-name="gutter-num" data-side="before">5</span>
            <span data-name="line-content" data-language="javascript">app.get('/users', (req, res) =&gt; {</span>
          </span>
          <span data-name="line" data-line-status="remove">
            <span data-name="gutter-num" data-side="before">6</span>
            <span data-name="line-content" data-language="javascript">  const users = db.query('SELECT * FROM users');</span>
          </span>
          <span data-name="line" data-line-status="remove">
            <span data-name="gutter-num" data-side="before">7</span>
            <span data-name="line-content" data-language="javascript">  res.json(users);</span>
          </span>
          <span data-name="line" data-line-status="empty">
            <span data-name="gutter-num" data-side="before"></span>
            <span data-name="line-content"></span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="before">8</span>
            <span data-name="line-content" data-language="javascript">});</span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="before">9</span>
            <span data-name="line-content" data-language="javascript"></span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="before">10</span>
            <span data-name="line-content" data-language="javascript">app.listen(3000);</span>
          </span>
        </div>
      </pre>
    </div>
    <div data-name="panel">
      <pre data-name="code" data-copy-selector="[data-name=&quot;line-content&quot;]">
        <div data-name="rows">
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="after">1</span>
            <span data-name="line-content" data-language="javascript">import express from 'express';</span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="after">2</span>
            <span data-name="line-content" data-language="javascript"></span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="after">3</span>
            <span data-name="line-content" data-language="javascript">const app = express();</span>
          </span>
          <span data-name="line" data-line-status="add">
            <span data-name="gutter-num" data-side="after">4</span>
            <span data-name="line-content" data-language="javascript">app.use(express.json());</span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="after">5</span>
            <span data-name="line-content" data-language="javascript"></span>
          </span>
          <span data-name="line" data-line-status="add">
            <span data-name="gutter-num" data-side="after">6</span>
            <span data-name="line-content" data-language="javascript">app.get('/users', async (req, res) =&gt; {</span>
          </span>
          <span data-name="line" data-line-status="add">
            <span data-name="gutter-num" data-side="after">7</span>
            <span data-name="line-content" data-language="javascript">  const { page = 1, limit = 20 } = req.query;</span>
          </span>
          <span data-name="line" data-line-status="add">
            <span data-name="gutter-num" data-side="after">8</span>
            <span data-name="line-content" data-language="javascript">  const users = await db.query('SELECT * FROM users LIMIT ? OFFSET ?', [limit, (page - 1) * limit]);</span>
          </span>
          <span data-name="line" data-line-status="add">
            <span data-name="gutter-num" data-side="after">9</span>
            <span data-name="line-content" data-language="javascript">  res.json({ users, page, limit });</span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="after">10</span>
            <span data-name="line-content" data-language="javascript">});</span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="after">11</span>
            <span data-name="line-content" data-language="javascript"></span>
          </span>
          <span data-name="line" data-line-status="equal">
            <span data-name="gutter-num" data-side="after">12</span>
            <span data-name="line-content" data-language="javascript">app.listen(3000);</span>
          </span>
        </div>
      </pre>
    </div>
  </div>
</div>
src/server.js
1import express from 'express';23const app = express();45app.get('/users', (req, res) => {6 const users = db.query('SELECT * FROM users');7 res.json(users);8});910app.listen(3000);
1import express from 'express';23const app = express();4app.use(express.json());56app.get('/users', async (req, res) => {7 const { page = 1, limit = 20 } = req.query;8 const users = await db.query('SELECT * FROM users LIMIT ? OFFSET ?', [limit, (page - 1) * limit]);9 res.json({ users, page, limit });10});1112app.listen(3000);
<div class="rf-diff rf-diff--split" data-code-host data-mode="split" data-rune="diff" data-density="full">
  <div data-name="header" class="rf-diff__header">src/server.js</div>
  <div data-name="split-container" class="rf-diff__split-container">
    <div data-name="panel" class="rf-diff__panel">
      <pre data-name="code" data-copy-selector="[data-name=&quot;line-content&quot;]" class="rf-diff__code"><div data-name="rows" class="rf-diff__rows"><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">1</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">import express from 'express';</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">2</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">3</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">const app = express();</span></span><span data-name="line" data-line-status="empty" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="line-content" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">4</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="remove" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">5</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">app.get('/users', (req, res) =&gt; {</span></span><span data-name="line" data-line-status="remove" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">6</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">  const users = db.query('SELECT * FROM users');</span></span><span data-name="line" data-line-status="remove" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">7</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">  res.json(users);</span></span><span data-name="line" data-line-status="empty" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="line-content" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">8</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">});</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">9</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">10</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">app.listen(3000);</span></span></div></pre>
    </div>
    <div data-name="panel" class="rf-diff__panel">
      <pre data-name="code" data-copy-selector="[data-name=&quot;line-content&quot;]" class="rf-diff__code"><div data-name="rows" class="rf-diff__rows"><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">1</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">import express from 'express';</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">2</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">3</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">const app = express();</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">4</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">app.use(express.json());</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">5</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">6</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">app.get('/users', async (req, res) =&gt; {</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">7</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">  const { page = 1, limit = 20 } = req.query;</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">8</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">  const users = await db.query('SELECT * FROM users LIMIT ? OFFSET ?', [limit, (page - 1) * limit]);</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">9</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">  res.json({ users, page, limit });</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">10</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">});</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">11</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content"></span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">12</span><span data-name="line-content" data-language="javascript" class="rf-diff__line-content">app.listen(3000);</span></span></div></pre>
    </div>
  </div>
</div>

Unified diff

The default mode shows changes in a single column with line numbers, +/- prefixes, and colored backgrounds.

{% diff mode="unified" language="css" %}
```css
.button {
  display: inline-block;
  padding: 8px 16px;
  background: blue;
  color: white;
  border: none;
  cursor: pointer;
}
```

```css
.button {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  background: var(--color-primary);
  color: white;
  border: none;
  border-radius: 0.375rem;
  cursor: pointer;
}
```
{% /diff %}
<div data-rune="diff" data-rune-fields="{&quot;mode&quot;:&quot;unified&quot;,&quot;language&quot;:&quot;css&quot;}" data-code-host>
  <pre data-name="code" data-copy-selector="[data-name=&quot;line-content&quot;]">
    <div data-name="rows">
      <span data-name="line" data-line-status="equal">
        <span data-name="gutter-num" data-side="before">1</span>
        <span data-name="gutter-num" data-side="after">1</span>
        <span data-name="line-content" data-language="css">.button {</span>
      </span>
      <span data-name="line" data-line-status="remove">
        <span data-name="gutter-num" data-side="before">2</span>
        <span data-name="gutter-num" data-side="after"></span>
        <span data-name="line-content" data-language="css">  display: inline-block;</span>
      </span>
      <span data-name="line" data-line-status="remove">
        <span data-name="gutter-num" data-side="before">3</span>
        <span data-name="gutter-num" data-side="after"></span>
        <span data-name="line-content" data-language="css">  padding: 8px 16px;</span>
      </span>
      <span data-name="line" data-line-status="remove">
        <span data-name="gutter-num" data-side="before">4</span>
        <span data-name="gutter-num" data-side="after"></span>
        <span data-name="line-content" data-language="css">  background: blue;</span>
      </span>
      <span data-name="line" data-line-status="add">
        <span data-name="gutter-num" data-side="before"></span>
        <span data-name="gutter-num" data-side="after">2</span>
        <span data-name="line-content" data-language="css">  display: inline-flex;</span>
      </span>
      <span data-name="line" data-line-status="add">
        <span data-name="gutter-num" data-side="before"></span>
        <span data-name="gutter-num" data-side="after">3</span>
        <span data-name="line-content" data-language="css">  align-items: center;</span>
      </span>
      <span data-name="line" data-line-status="add">
        <span data-name="gutter-num" data-side="before"></span>
        <span data-name="gutter-num" data-side="after">4</span>
        <span data-name="line-content" data-language="css">  gap: 0.5rem;</span>
      </span>
      <span data-name="line" data-line-status="add">
        <span data-name="gutter-num" data-side="before"></span>
        <span data-name="gutter-num" data-side="after">5</span>
        <span data-name="line-content" data-language="css">  padding: 0.5rem 1rem;</span>
      </span>
      <span data-name="line" data-line-status="add">
        <span data-name="gutter-num" data-side="before"></span>
        <span data-name="gutter-num" data-side="after">6</span>
        <span data-name="line-content" data-language="css">  background: var(--color-primary);</span>
      </span>
      <span data-name="line" data-line-status="equal">
        <span data-name="gutter-num" data-side="before">5</span>
        <span data-name="gutter-num" data-side="after">7</span>
        <span data-name="line-content" data-language="css">  color: white;</span>
      </span>
      <span data-name="line" data-line-status="equal">
        <span data-name="gutter-num" data-side="before">6</span>
        <span data-name="gutter-num" data-side="after">8</span>
        <span data-name="line-content" data-language="css">  border: none;</span>
      </span>
      <span data-name="line" data-line-status="add">
        <span data-name="gutter-num" data-side="before"></span>
        <span data-name="gutter-num" data-side="after">9</span>
        <span data-name="line-content" data-language="css">  border-radius: 0.375rem;</span>
      </span>
      <span data-name="line" data-line-status="equal">
        <span data-name="gutter-num" data-side="before">7</span>
        <span data-name="gutter-num" data-side="after">10</span>
        <span data-name="line-content" data-language="css">  cursor: pointer;</span>
      </span>
      <span data-name="line" data-line-status="equal">
        <span data-name="gutter-num" data-side="before">8</span>
        <span data-name="gutter-num" data-side="after">11</span>
        <span data-name="line-content" data-language="css">}</span>
      </span>
    </div>
  </pre>
</div>
11.button {2 display: inline-block;3 padding: 8px 16px;4 background: blue;2 display: inline-flex;3 align-items: center;4 gap: 0.5rem;5 padding: 0.5rem 1rem;6 background: var(--color-primary);57 color: white;68 border: none;9 border-radius: 0.375rem;710 cursor: pointer;811}
<div class="rf-diff rf-diff--unified" data-code-host data-mode="unified" data-rune="diff" data-density="full">
  <pre data-name="code" data-copy-selector="[data-name=&quot;line-content&quot;]" class="rf-diff__code"><div data-name="rows" class="rf-diff__rows"><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">1</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">1</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">.button {</span></span><span data-name="line" data-line-status="remove" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">2</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num"></span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  display: inline-block;</span></span><span data-name="line" data-line-status="remove" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">3</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num"></span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  padding: 8px 16px;</span></span><span data-name="line" data-line-status="remove" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">4</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num"></span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  background: blue;</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">2</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  display: inline-flex;</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">3</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  align-items: center;</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">4</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  gap: 0.5rem;</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">5</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  padding: 0.5rem 1rem;</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">6</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  background: var(--color-primary);</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">5</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">7</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  color: white;</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">6</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">8</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  border: none;</span></span><span data-name="line" data-line-status="add" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num"></span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">9</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  border-radius: 0.375rem;</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">7</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">10</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">  cursor: pointer;</span></span><span data-name="line" data-line-status="equal" class="rf-diff__line"><span data-name="gutter-num" data-side="before" class="rf-diff__gutter-num">8</span><span data-name="gutter-num" data-side="after" class="rf-diff__gutter-num">11</span><span data-name="line-content" data-language="css" class="rf-diff__line-content">}</span></span></div></pre>
</div>

Header from fence source

When no title= is set, diff derives the header from each panel's fence source annotation (auto-populated when the panel is a {% snippet %}, authorable on hand-written fences). Matching paths collapse to a single label; differing paths render as before → after. SPEC-062 / WORK-304.

src/server.js
11const app = express();2app.get('/users', (req, res) => {3 res.json(db.query('SELECT * FROM users'));2app.use(express.json());3app.get('/users', async (req, res) => {4 res.json(await db.query('SELECT * FROM users LIMIT 20'));45});

The fence on each side annotates the diff header with src/server.js. Explicit title= always wins when you'd rather pin the label; differing source paths between sides render as before → after.

Line numbers using file coordinates

linenumbers=true on each panel's fence shifts that side's gutter to start at the fence's lines offset rather than 1 — so a diff between two slices of the same file shows the actual file line numbers per side.

theme.ts
74contentDir: string;75theme: string | SiteThemeConfig;76target?: string;77overrides?: Record<string, string>;78routeRules?: RouteRule[];
74contentDir: string;75theme: string | SiteThemeConfig;76target?: string;77overrides?: Record<string, string>;78routeRules?: RouteRule[];79entityRoutes?: EntityRoute[];

Both gutter columns start at 74 (matching the file's real coordinates), the header reads theme.ts, and the added line shows in the right column with entityRoutes highlighted as the new value.

note

highlight= on a fence inside {% diff %} is silently ignored. Diff's add/remove channel is the primary line-level signal; a separate highlight layer on top would muddy the +/- semantics. Use a standalone {% snippet highlight="..." /%} instead when you want emphasis.

Attributes

AttributeTypeRequiredDescription
mode"unified" | "split" | "inline"Diff display style: unified, split, or inline
languagestringProgramming language for syntax highlighting
titlestringOptional title or filename displayed above the diff

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
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)
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
framethis rune declares neither a `frameTarget` nor a media section
prominencethis rune has no page-section header