Codegroup
Display code blocks with terminal-style chrome. Multiple fences become a tabbed view with language-based tab names. A single fence without labels renders as a clean code block with just the chrome — no tabs.
Basic usage
Tabs are automatically labeled by their code block language.
{% codegroup %}
```js
const x = 1;
```
```python
x = 1
```
{% /codegroup %}<section data-rune="code-group">
<div role="tablist" data-name="tabs">
<button data-name="tab" role="tab">
<span>JavaScript</span>
</button>
<button data-name="tab" role="tab">
<span>Python</span>
</button>
</div>
<div data-name="panels">
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="js">
<code data-language="js">const x = 1;
</code>
</pre>
</div>
</div>
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="python">
<code data-language="python">x = 1
</code>
</pre>
</div>
</div>
</div>
</section>const x = 1;
x = 1
<section class="rf-codegroup rf-codegroup--scroll" data-overflow="scroll" data-rune="code-group" data-density="compact" data-code-host="true">
<div role="tablist" data-name="tabs" class="rf-codegroup__tabs">
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>JavaScript</span>
</button>
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>Python</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="js"><code data-language="js">const x = 1;
</code></pre>
</div>
</div>
<div role="tabpanel" data-name="panel" class="rf-codegroup__panel">
<div class="rf-codeblock">
<pre data-language="python"><code data-language="python">x = 1
</code></pre>
</div>
</div>
</div>
</section>Custom labels
Use the labels attribute to override the default language-based tab names. Useful when multiple tabs share the same language.
{% codegroup labels="React, Vue, Svelte" %}
```jsx
export default function App() {
return <h1>Hello</h1>;
}
```
```html
<template>
<h1>Hello</h1>
</template>
```
```html
<h1>Hello</h1>
```
{% /codegroup %}<section data-rune="code-group">
<div role="tablist" data-name="tabs">
<button data-name="tab" role="tab">
<span>React</span>
</button>
<button data-name="tab" role="tab">
<span>Vue</span>
</button>
<button data-name="tab" role="tab">
<span>Svelte</span>
</button>
</div>
<div data-name="panels">
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="jsx">
<code data-language="jsx">export default function App() {
return <h1>Hello</h1>;
}
</code>
</pre>
</div>
</div>
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="html">
<code data-language="html"><template>
<h1>Hello</h1>
</template>
</code>
</pre>
</div>
</div>
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="html">
<code data-language="html"><h1>Hello</h1>
</code>
</pre>
</div>
</div>
</div>
</section>export default function App() {
return <h1>Hello</h1>;
}
<template>
<h1>Hello</h1>
</template>
<h1>Hello</h1>
<section class="rf-codegroup rf-codegroup--scroll" data-overflow="scroll" data-rune="code-group" data-density="compact" data-code-host="true">
<div role="tablist" data-name="tabs" class="rf-codegroup__tabs">
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>React</span>
</button>
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>Vue</span>
</button>
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>Svelte</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="jsx"><code data-language="jsx">export default function App() {
return <h1>Hello</h1>;
}
</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"><template>
<h1>Hello</h1>
</template>
</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"><h1>Hello</h1>
</code></pre>
</div>
</div>
</div>
</section>With title
Use the title attribute to display a filename or label in the topbar. With a single code fence and no labels, the codegroup renders as chrome-only — the topbar and code block without any tabs.
{% codegroup title="app.js" %}
```js
import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
```
{% /codegroup %}<section data-rune="code-group" data-rune-fields="{"title":"app.js"}">
<div class="rf-codeblock">
<pre data-language="js">
<code data-language="js">import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
</code>
</pre>
</div>
</section>import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
<section class="rf-codegroup rf-codegroup--scroll" data-title="app.js" data-overflow="scroll" data-rune="code-group" data-density="compact" data-code-host="true">
<div data-name="topbar" data-zone="topbar" data-zone-layout="bar" class="rf-codegroup__topbar" data-section="header">
<span data-meta-type="code">app.js</span>
</div>
<div class="rf-codeblock">
<pre data-language="js"><code data-language="js">import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
</code></pre>
</div>
</section>To force a tab bar on a single fence, provide the labels attribute.
{% codegroup title="server" labels="Express" %}
```js
import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
```
{% /codegroup %}<section data-rune="code-group" data-rune-fields="{"title":"server"}">
<div role="tablist" data-name="tabs">
<button data-name="tab" role="tab">
<span>Express</span>
</button>
</div>
<div data-name="panels">
<div role="tabpanel" data-name="panel">
<div class="rf-codeblock">
<pre data-language="js">
<code data-language="js">import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
</code>
</pre>
</div>
</div>
</div>
</section>import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
<section class="rf-codegroup rf-codegroup--scroll" data-title="server" data-overflow="scroll" data-rune="code-group" data-density="compact" data-code-host="true">
<div data-name="topbar" data-zone="topbar" data-zone-layout="bar" class="rf-codegroup__topbar" data-section="header">
<span data-meta-type="code">server</span>
</div>
<div role="tablist" data-name="tabs" class="rf-codegroup__tabs">
<button data-name="tab" role="tab" class="rf-codegroup__tab">
<span>Express</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="js"><code data-language="js">import express from 'express';
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
</code></pre>
</div>
</div>
</div>
</section>Tab labels from fence annotations
When neither labels= nor a per-fence label annotation is set, codegroup derives each tab label from the fence's source annotation — automatically populated when the panel is a {% snippet %}, and authorable on hand-written fences. SPEC-062 / WORK-304.
{% codegroup %}
{% snippet path="packages/runes/src/lang-map.ts" lines="3-7" linenumbers=true highlight="4-5" /%}
{% snippet path="packages/runes/src/tags/codegroup.ts" lines="7-14" linenumbers=true highlight="8-9" /%}
{% /codegroup %}
Renders with tabs lang-map.ts:3-7 and codegroup.ts:7-14, each panel's gutter starting at the file's real line offset (3 and 7 respectively), and each panel's highlight range emphasized — all four annotations (source, lines, linenumbers, highlight) propagate through the snippet → fence → codegroup chain uniformly:
*
* Shared across the snippet rune, the inspect tool, the contracts generator,
* and any future rune that needs to infer a syntax-highlighting language
* from a file extension. Lives in `@refrakt-md/runes` (not in a plugin)
* because every consumer already depends on this package and pluginsconst languageNames: Record<string, string> = {
js: 'JavaScript', ts: 'TypeScript', py: 'Python',
rb: 'Ruby', rs: 'Rust', go: 'Go', sh: 'Shell',
bash: 'Bash', zsh: 'Zsh', shell: 'Shell',
html: 'HTML', css: 'CSS', json: 'JSON', yaml: 'YAML',
sql: 'SQL', swift: 'Swift', kt: 'Kotlin', java: 'Java',
cpp: 'C++', c: 'C', cs: 'C#', php: 'PHP',
};The label resolution chain (first match wins):
- Group-level
labels=— positional override (labels="A, B, C"). - Per-fence
labelannotation —```ts {% label="SiteConfig" %}. - Derived from
source— basename of the path, with:linessuffix whenlines=is also set (e.g.theme.ts:74-125). - Prettified language name — today's default (
JavaScript,Python, etc.).
Same composition story snippet has elsewhere, just propagated through the fence-annotation surface.
Overflow control
Use the overflow attribute to control how long lines are handled. The default is scroll (horizontal scrollbar). Use wrap to wrap lines, or hide to clip without a scrollbar.
{% codegroup overflow="wrap" title="wrapped.ts" %}
```ts
const result = await fetchUserDataFromRemoteService(userId, { includeMetadata: true, resolveReferences: true, maxDepth: 3, timeout: 5000 });
```
{% /codegroup %}<section data-rune="code-group" data-rune-fields="{"title":"wrapped.ts","overflow":"wrap"}">
<div class="rf-codeblock">
<pre data-language="ts">
<code data-language="ts">const result = await fetchUserDataFromRemoteService(userId, { includeMetadata: true, resolveReferences: true, maxDepth: 3, timeout: 5000 });
</code>
</pre>
</div>
</section>const result = await fetchUserDataFromRemoteService(userId, { includeMetadata: true, resolveReferences: true, maxDepth: 3, timeout: 5000 });
<section class="rf-codegroup rf-codegroup--wrap" data-title="wrapped.ts" data-overflow="wrap" data-rune="code-group" data-density="compact" data-code-host="true">
<div data-name="topbar" data-zone="topbar" data-zone-layout="bar" class="rf-codegroup__topbar" data-section="header">
<span data-meta-type="code">wrapped.ts</span>
</div>
<div class="rf-codeblock">
<pre data-language="ts"><code data-language="ts">const result = await fetchUserDataFromRemoteService(userId, { includeMetadata: true, resolveReferences: true, maxDepth: 3, timeout: 5000 });
</code></pre>
</div>
</section>Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
labels | string | — | Comma-separated custom tab names |
title | string | — | Filename or label shown in the topbar |
overflow | string | scroll | Line overflow: scroll, wrap, or hide |
Common attributes
All block runes share these attributes for layout and theming.
| Attribute | Type | Default | Description |
|---|---|---|---|
width | string | content | Page grid width: content, wide, or full |
spacing | string | — | Vertical spacing: flush, tight, default, loose, or breathe |
inset | string | — | Horizontal padding: flush, tight, default, loose, or breathe |
tint | string | — | Named colour tint from theme configuration |
tint-mode | string | auto | Colour scheme override: auto, dark, or light |
bg | string | — | Named background preset from theme configuration |