<details class="va-collapse">
<summary>
<strong>The purpose of thought</strong>
</summary>
<p>
We can attain wisdom by three ways: first, by way of experience,
this is the most difficult way; second, by way of imitation,
this is the easiest way; and third by way of meditation,
and this is the noblest way.
</p>
<p>Confucius</p>
</details><va-code-block>
<pre><code class="language-html"><details class="va-collapse">
<summary>
<strong>The purpose of thought</strong>
</summary>
<p>
We can attain wisdom by three ways: first, by way of experience,
this is the most difficult way; second, by way of imitation,
this is the easiest way; and third by way of meditation,
and this is the noblest way.
</p>
<p>Confucius</p>
</details></code></pre>
</va-code-block>Installation
Using a CDN
Add the script to your page and the <va-code-block> element is ready to use:
<script src="https://unpkg.com/va-code-block"></script>Using NPM
npm i va-code-blockThen import the package once in your JavaScript. Importing it registers the element:
import 'va-code-block'Styles are bundled inside the component, so you don’t need to include Varvara CSS to use it.
Usage
With a code fallback
Place the code inside the element as a regular <pre><code> block, with the language as a language-* class. It stays readable for crawlers, AI agents and browsers without JavaScript, and is replaced by the highlighted version once the component loads:
<va-code-block>
<pre><code class="language-html"><button class="va-button">Example button</button></code></pre>
</va-code-block>As in any HTML, write < and & inside the code as < and &.
With the text attribute
Alternatively, pass the code as a string in the text attribute:
<va-code-block
language="html"
text='<button class="va-button">Example button</button>'
></va-code-block>The code can’t contain the quote character that wraps the attribute: use single quotes around code with double quotes, or the other way round. If both text and child content are present, text takes precedence.
Multiple languages
Place several <pre><code> blocks inside the element and a select appears to switch between them. Each block takes its language from its language-* class, and its label from data-label on the <pre> (or the uppercased language):
<button class="va-button">Example button</button>
const Button = () => <button className="va-button">Example button</button>
.va-button {
--va-button-padding: var(--va-space-4);
}
<va-code-block>
<pre><code class="language-html"><button class="va-button">Example button</button></code></pre>
<pre data-label="JSX"><code class="language-jsx">const Button = () => <button className="va-button">Example button</button></code></pre>
<pre><code class="language-css">.va-button {
--va-button-padding: var(--va-space-4);
}</code></pre>
</va-code-block>Labels are useful when several blocks share a language. Use selected to choose the initial block:
npm i va-code-block
pnpm add va-code-block
bun add va-code-block
<va-code-block selected="bun">
<pre data-label="npm"><code class="language-bash">npm i va-code-block</code></pre>
<pre data-label="pnpm"><code class="language-bash">pnpm add va-code-block</code></pre>
<pre data-label="bun"><code class="language-bash">bun add va-code-block</code></pre>
</va-code-block>The copy button copies the active block. Without JavaScript, all blocks remain readable one after another.
Dynamic updates
The component reacts to changes in its attributes and in its child content, so you can update it after the initial render:
const el = document.querySelector('va-code-block')
el.setAttribute('text', 'console.log("hello")')
el.setAttribute('language', 'js')Attributes
| Attribute | Description | Default value |
|---|---|---|
text |
The code snippet to display. Falls back to the child content. | '' |
language |
Language for syntax highlighting. A block’s language-* class takes precedence. Also available as lang. |
'text' |
selected |
Active block when there are several, matched by data-label or language. Updated when the user picks another block. |
First block |
Events
| Event | Description |
|---|---|
change |
Fired when the user selects another block. event.detail contains label, language and value. |
el.addEventListener('change', (event) => {
console.log(event.detail.value)
})Styling
The component renders inside a shadow root. Its internal elements are exposed as parts that you can style with ::part():
| Part | Element |
|---|---|
card |
The outer container. |
pre-content |
The <pre> that holds the highlighted code. |
select |
The language select, shown when there are several blocks. |
va-code-block::part(pre-content) {
max-height: 20rem;
overflow: auto;
}Supported languages
Syntax highlighting is built-in with a lightweight tokenizer. No external dependencies required.
| Language | Values |
|---|---|
| JavaScript | javascript, js, jsx |
| TypeScript | typescript, ts, tsx |
| CSS | css |
| HTML | html, markup, xml |
Other languages, such as bash, render as plain text without errors and still show their label.