Code block
Code block — a block of source code, with an optional filename header and optional line numbers.
Examples
@layer ui.components {
.btn {
--btn-bg: var(--color-primary);
--btn-color: var(--color-on-primary);
display: inline-flex;
align-items: center;
gap: var(--btn-gap);
min-block-size: 2.5rem;
padding-inline: var(--btn-padding-inline);
color: var(--btn-color);
background-color: var(--btn-bg);
border-radius: var(--radius-field);
}
.btn:focus-visible {
outline: var(--focus-ring);
outline-offset: var(--focus-ring-offset);
}
}<figure class="code">
<figcaption class="code-name" id="code-btn">src/css/components/button.css</figcaption>
<pre class="code-body" tabindex="0" role="region" aria-labelledby="code-btn"><code>/* the highlighter puts its spans in here */</code></pre>
</figure>Syntax highlighting is not this component’s job, and that is why the other examples on this page have none. A code block is a surface: a background, a family, a scroll container, a filename.
What colours the tokens inside it is a highlighter — Shiki, Prism, Highlight.js, a server — and every one of them emits its own <span>s. This preview is the docs being that consumer: Astro runs Shiki at build time with defaultColor={false}, which writes --shiki-light and --shiki-dark on each span and no color at all, and one rule in docs.css picks the one matching the current mode. The site therefore ships no tokeniser, no runtime and no extra request. The rule a highlighting theme has to follow is in this component’s docblock and it is one line: colour your tokens, leave the surface alone — no background, no font-family, no padding. A theme that repaints the box is a theme that undoes the component.
@layer ui.reset, ui.tokens, ui.base, ui.components, ui.blocks, ui.utilities;
<figure class="code">
<figcaption class="code-name" id="code-layers">src/css/layers.css</figcaption>
<pre class="code-body" tabindex="0" role="region" aria-labelledby="code-layers"><code>@layer ui.reset, ui.tokens, ui.base, ui.components, ui.blocks, ui.utilities;</code></pre>
</figure>The <pre> is focusable and named for the same reason the table wrapper is: code must not wrap, so a long line scrolls, and a scroll container that is not focusable is unreachable without a pointer.
npm install mostlycss
:root { --space-4: 1rem; --radius-field: 0.5rem;}
<figure class="code">
<pre class="code-body" tabindex="0" role="region" aria-label="Installing from npm"><code>npm install mostlycss</code></pre>
</figure>
<figure class="code code-numbered">
<figcaption class="code-name" id="code-num">tokens.css</figcaption>
<pre class="code-body" tabindex="0" role="region" aria-labelledby="code-num"><code><span class="code-line">:root {</span><span class="code-line"> --space-4: 1rem;</span><span class="code-line"> --radius-field: 0.5rem;</span><span class="code-line">}</span></code></pre>
</figure>No caption, so the region is named directly. Keep the tabindex even on a short snippet — whether a line overflows depends on the reader’s window, not on yours.
The numbers are counter-increment on .code-line, drawn in a ::before that is user-select: none, so copying the block copies the code and not the numbers.
Tokens 21
Level 2, declared on .code itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --code-gutter-color | var(--color-on-surface-muted) |
| --code-gutter-gap | var(--space-4) |
| --code-gutter-size | 2.5ch |
| --code-inline-bg | var(--color-surface-sunken) |
| --code-inline-color | inherit |
| --code-inline-radius | var(--radius-sm) |
| --code-name-bg | var(--color-surface) |
| --code-name-color | var(--color-on-surface-muted) |
| --code-name-font-size | var(--font-size-xs) |
| --code-name-padding-block | var(--space-2) |
| --code-bg | var(--color-surface-sunken) |
| --code-border-color | var(--color-border-subtle) |
| --code-border-width | var(--border-width) |
| --code-color | var(--color-on-surface) |
| --code-font-family | var(--font-mono) |
| --code-padding-block | var(--space-4) |
| --code-padding-inline | var(--space-4) |
| --code-radius | var(--radius-box) |
| --code-font-size | var(--font-size-xs) |
| --code-line-height | var(--line-height-normal) |
| --code-tab-size | 2 |
Variants and states
Variants
.code-numbered.code-sm
Inside it
.code-body.code-line.code-inline.code-name.code-wrap
State it reads
Read from the platform, never mirrored into a class that could disagree with it.
:focus
Before you ship it
What you have to do 1 requirements
- Name the region.
aria-labelledbypointing at the.code-nameis the best version, because the name is then on screen too;aria-labelis correct when there is no filename. An unnamedrole="region"is dropped from the landmarks list entirely, which puts you back where you started.
src/css/components/code-block.css · npx mostlycss add code-block