Skip to the page
Tactile UI

Kit part · Code & devtools

Code block

Code on display glass. A bar along the top holds the file name on a tab, with a lit edge under it, and the copy key at the far end. Under it the lines run, each with its number engraved in a gutter that stays where it is when a long line scrolls sideways. The numbers are drawn by the stylesheet, so they are never selected, never copied and never read aloud. Highlighting is optional and small: a few strengths of the display’s own light, told apart by weight and italic, never a colour of its own.

Element
<figure class="tui-glass tui-code-block">
Needs
kit.css + kit.mjs
Rules
code-block.rules.md · 6
Finishes
Aluminium · Graphite · Oak · Walnut · Porcelain · Black · Daylight · Night · Paper · Phosphor · E-paper · AMOLED
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts

Seats open in 11 days. Want one email that morning?

Variants

feed.mjs
import { connect } from '@northwind/cli';

// Open the harbour feed and log each berth change.
const feed = await connect({ site: 'quay-street', retries: 3 });
feed.on('berth', (change) => {
  console.log('Berth ' + change.id + ' is now ' + change.state);
});
With a file name · lit JavaScript
sh
$ npm install @northwind/cli
$ northwind init quay-street
$ northwind deploy --dry-run   # nothing is changed
A language legend, no file name · shell
northwind.json
{
  "site": "quay-street",
  "retries": 3,
  "berths": ["A1", "A2", "B1"],
  "dryRun": true
}
Plain text · no data-lang, nothing lit
schedule.js
const schedule = 'https://harbour.northwind.example/api/v2/berths/quay-street/schedules?from=2026-10-07&to=2026-10-14&include=tides,pilots,tugs,fuel';
const rows = await fetch(schedule).then((r) => r.json());
A long line scrolls sideways, the gutter stays
feed.mjs
import { connect } from '@northwind/cli';

// Open the harbour feed and log each berth change.
const feed = await connect({ site: 'quay-street', retries: 3 });
feed.on('berth', (change) => {
  console.log('Berth ' + change.id + ' is now ' + change.state);
});
From line 24, with two lines marked
sh
$ npm install @northwind/cli
$ northwind init quay-street
$ northwind deploy --dry-run   # nothing is changed
No gutter · .tui-code-block--bare
feed.mjs
import { connect } from '@northwind/cli';

// Open the harbour feed and log each berth change.
const feed = await connect({ site: 'quay-street', retries: 3 });
feed.on('berth', (change) => {
  console.log('Berth ' + change.id + ' is now ' + change.state);
});
A fixed height scrolls both ways · --cb-max

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartRestCopy key focusedLines focusedA marked line
Block
berth.js
const berth = 'A1';
return berth;
berth.js
const berth = 'A1';
return berth;
berth.js
const berth = 'A1';
return berth;
berth.js
const berth = 'A1';
return berth;

Graphite

PartRestCopy key focusedLines focusedA marked line
Block
berth.js
const berth = 'A1';
return berth;
berth.js
const berth = 'A1';
return berth;
berth.js
const berth = 'A1';
return berth;
berth.js
const berth = 'A1';
return berth;

API

NameKindValuesWhat it does
figure.tui-glass.tui-code-blockelementboth classesThe block: display glass with no padding of its own, so the bar and the lines reach its edges.
.tui-code-block__barelementthe top rowHolds the tab at the start and the copy key at the end, with a rule under it.
figcaption.tui-code-block__tab > spanelementthe file nameThe tab. A lit edge under it says it is the file that is open. Use a figcaption.tui-label.tui-code-block__lang instead when there is only a language to say.
button.tui-copy-key[data-copy-from]elementpoints at the <code>’s idThe kit’s copy key, flat and small. It copies the text of the code, which has no numbers in it. Give it an aria-label that names the file.
.tui-code-block__scroll[tabindex="0"][role="region"]elementnamedThe scrolling region, a stop of its own so the keyboard can scroll long lines. Its aria-label says what the code is.
code.tui-code-block__code[data-lang]elementjs · ts · json · sh · css · yamlThe code. With data-lang the script lights a few tokens; with none it only splits the lines. data-mark="3,4" lights those lines.
--cb-start · --cb-maxcustom propertya number · a lengthThe number the first line has, and a height after which the block scrolls up and down.
--baremodifiertui-code-block--bareTakes the gutter away, for a snippet that is not a listing.
codeBlockHTML() · codeLinesHTML() · wireCodeBlocks()functionneeds kit.mjsWrite the whole block, write only the lines, or dress every plain code element in a page.
codeLines() · codeHighlight() · codeTokens() · codeLang()functionpureThe text as lines, as lit lines, as tokens, and the language a name means.

Markup

<!-- A figure, so the file name is its caption. The lines scroll, so they are a labelled region the keyboard can reach. -->
<figure class="tui-glass tui-code-block">
  <div class="tui-code-block__bar">
    <figcaption class="tui-code-block__tab"><span>feed.mjs</span></figcaption>
    <button class="tui-key tui-key--flat tui-key--sm tui-copy-key" type="button" data-copy-from="#cb-1" aria-label="Copy feed.mjs"></button>
  </div>
  <div class="tui-code-block__scroll" tabindex="0" role="region" aria-label="feed.mjs, code">
    <pre class="tui-code-block__pre"><code class="tui-code-block__code" id="cb-1" data-lang="js">import { connect } from '@northwind/cli';
const feed = await connect({ site: 'quay-street' });</code></pre>
  </div>
</figure>

<!-- No file name: a language legend in its place, <figcaption class="tui-label tui-code-block__lang">Shell</figcaption>.
     Start the count elsewhere with style="--cb-start:24"; mark a line with data-mark="24,25" on the <code>. -->
import { codeBlockHTML } from '/kit/kit.mjs';

// Write the whole block from a string:
host.innerHTML = codeBlockHTML({ code, file: 'feed.mjs', lang: 'js', mark: [4] });

// Or write plain text in <code data-lang="js"> and kit.mjs splits it into lines and lights it.

Needs kit.css and kit.mjs. How to install the kit.

Accessibility

  • The line numbers are drawn by the stylesheet with an empty alternative text, so a screen reader reads the code and not a count down the side, and copying or selecting the code never picks them up. The code itself is real text in a real <code> element, so nothing is lost to find in the page.
  • The scrolling region is a stop of its own with a name, so a keyboard user can reach it and scroll long lines with the arrow keys. It has a visible 2px ring in the display’s own light.
  • Lit tokens are never the only difference: a keyword is bold, a comment is italic and dimmer, and every strength is above 4.5:1 on the glass in every finish. A marked line has a bar at its edge as well as a lift of light.
  • The copy key is the kit’s: it says Copied aloud, and when the browser refuses the clipboard it selects the text for the keyboard instead. The file name is the figure’s caption, so the block is named.
  • On a phone the key is 44px, long lines scroll inside the block and the page itself never scrolls sideways.

How it’s built

It is .tui-glass, so the glass, its scan lines, its ink and its focus light are the kit’s in every finish and volume. The gutter is a CSS counter on each line, drawn as a sticky plate over the lines that pass under it, with an engraved edge. The highlighter is a single pattern in code-block.mjs: it is not a parser, and a line it misreads is still the right text.

Rules

code-block.rules.mdIn the brain

Show code the way a lit display shows it: a file name tab, numbered lines in a gutter that holds still, a copy key, and long lines that scroll instead of wrapping.

6 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

Glass

Dark display glass for plots and live readouts, whatever the finish around it. Everything printed inside it is lit.

Copy key

A key that copies a value and says so. Its lamp sits dark in the cap; when the text is on the clipboard the lamp lights, winks once, and the legend says Copied before it goes back to Copy. If the browser refuses the clipboard, the text is selected for the keyboard and the lamp lights amber.

Kbd

The key to press, drawn as the key: a small cap on a skirt with a dish worked into its top. Shortcuts come as combos and follow the platform, so ⌘ K on a Mac is Ctrl K elsewhere.