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="{"mode":"split","language":"javascript"}" 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="line-content"]">
<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) => {</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="line-content"]">
<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) => {</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>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="line-content"]" 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) => {</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="line-content"]" 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) => {</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="{"mode":"unified","language":"css"}" data-code-host>
<pre data-name="code" data-copy-selector="[data-name="line-content"]">
<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="line-content"]" 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.
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.
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.
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
| Attribute | Type | Required | Description |
|---|---|---|---|
mode | "unified" | "split" | "inline" | — | Diff display style: unified, split, or inline |
language | string | — | Programming language for syntax highlighting |
title | string | — | Optional 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.
| Attribute | Type | Required | Description |
|---|---|---|---|
bg | string | — | Background preset applied to this block |
bg-from | string | — | Gradient 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-to | string | — | Gradient end colour — a semantic token name |
bg-via | string | — | Optional 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.
| Attribute | Type | Required | Description |
|---|---|---|---|
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.
| Attribute | Type | Required | Description |
|---|---|---|---|
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.
| Attribute | Type | Required | Description |
|---|---|---|---|
reveal | "none" | "fade" | "slide" | "scale" | "blur" | — | Scroll-reveal entrance character (none|fade|slide|scale|blur); the theme owns the choreography |
stagger | boolean | — | Cascade this block's items in as it reveals (no-op on single-child runes) |
spacing
Block-level rhythm override.
| Attribute | Type | Required | Description |
|---|---|---|---|
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.
| Attribute | Type | Required | Description |
|---|---|---|---|
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.
| Attribute | Type | Required | Description |
|---|---|---|---|
tint | string | — | Color 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.
| Attribute | Type | Required | Description |
|---|---|---|---|
width | "compact" | "narrow" | "content" | "wide" | "full" | — | Maximum width constraint for this block |
| Not available | Why |
|---|---|
| dropcap, reading | this rune declares no prose body |
| frame | this rune declares neither a `frameTarget` nor a media section |
| prominence | this rune has no page-section header |