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">
  <div data-name="topbar" class="rf-codegroup__topbar" data-section="header">
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
  </div>
  <div role="tablist" data-name="tabs" class="rf-codegroup__tabs">
    <button data-name="tab" role="tab" class="rf-codegroup__tab">
      <span>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 &lt;h1&gt;Hello&lt;/h1&gt;;
}
</code>
        </pre>
      </div>
    </div>
    <div role="tabpanel" data-name="panel">
      <div class="rf-codeblock">
        <pre data-language="html">
          <code data-language="html">&lt;template&gt;
  &lt;h1&gt;Hello&lt;/h1&gt;
&lt;/template&gt;
</code>
        </pre>
      </div>
    </div>
    <div role="tabpanel" data-name="panel">
      <div class="rf-codeblock">
        <pre data-language="html">
          <code data-language="html">&lt;h1&gt;Hello&lt;/h1&gt;
</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">
  <div data-name="topbar" class="rf-codegroup__topbar" data-section="header">
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
  </div>
  <div role="tablist" data-name="tabs" class="rf-codegroup__tabs">
    <button data-name="tab" role="tab" class="rf-codegroup__tab">
      <span>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 &lt;h1&gt;Hello&lt;/h1&gt;;
}
</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;template&gt;
  &lt;h1&gt;Hello&lt;/h1&gt;
&lt;/template&gt;
</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;h1&gt;Hello&lt;/h1&gt;
</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">
  <meta content="app.js" data-field="title">
  <div class="rf-codeblock">
    <pre data-language="js">
      <code data-language="js">import express from 'express';

const app = express();
app.get('/', (req, res) =&gt; {
  res.send('Hello World');
});
</code>
    </pre>
  </div>
</section>
app.js
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">
  <div data-name="topbar" class="rf-codegroup__topbar" data-section="header">
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="title" class="rf-codegroup__title" data-section="title">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) =&gt; {
  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">
  <meta content="server" data-field="title">
  <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) =&gt; {
  res.send('Hello World');
});
</code>
        </pre>
      </div>
    </div>
  </div>
</section>
server
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">
  <div data-name="topbar" class="rf-codegroup__topbar" data-section="header">
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="title" class="rf-codegroup__title" data-section="title">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) =&gt; {
  res.send('Hello World');
});
</code></pre>
      </div>
    </div>
  </div>
</section>

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">
  <meta content="wrapped.ts" data-field="title">
  <meta content="wrap" data-field="overflow">
  <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>
wrapped.ts
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">
  <div data-name="topbar" class="rf-codegroup__topbar" data-section="header">
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="dot" class="rf-codegroup__dot"></span>
    <span data-name="title" class="rf-codegroup__title" data-section="title">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

AttributeTypeDefaultDescription
labelsstringComma-separated custom tab names
titlestringFilename or label shown in the topbar
overflowstringscrollLine overflow: scroll, wrap, or hide

Common attributes

All block runes share these attributes for layout and theming.

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