Skip to content

Code

hc-code is a code surface styled from the kit’s tokens. Its three read-only modes share one decoration mechanism and need no script — all state lives in HTML attributes, so they work under a strict default-src 'self' Content-Security-Policy. A fourth, editable mode is a real <textarea> that installCodeEditor() upgrades with a line-number gutter and an optional live syntax-highlight overlay. Syntax highlighting (read-only or live) reuses one token palette and stays CSP-safe.

Apply hc-code to a <pre>. Lines overflow into a horizontal scroll by default. Because a scrollable region must be keyboard-reachable, make a scrollable block a focusable, labelled region (tabindex="0", role="region", aria-label).

SELECT id, email
FROM members
WHERE active = true

Long lines scroll horizontally by default. Set data-wrap="on" to soft-wrap them instead.

<pre class="hc-code" data-wrap="on"><code>SELECT a_very_long_column_name, another_long_one FROM a_table_with_a_long_name</code></pre>

Apply hc-code to an <ol> with data-gutter="line-numbers", one <li class="hc-code__line"> per line. Each line takes an optional data-state that tints it from the semantic status tokens — covered (success) and missed (error) are built in, for test-coverage gutters. hc-code__swatch[data-state] is a matching legend chip.

  1. SELECT *
  2. FROM orders
  3. WHERE total > 0

Covered Missed

Set data-mode="diff" on the <ol>. Each line carries a data-state of added, removed, or context, plus data-old / data-new line numbers. The gutter prints a + / - sign alongside the tint, so the change is not conveyed by colour alone. The server computes the hunks — the kit only styles them, so there is no client-side diffing and no script.

  1. SELECT id
  2. FROM users
  3. FROM app_users

A side-by-side (split) layout is not part of this release; the unified view above is the supported diff presentation.

Apply data-editable to a <div class="hc-code"> wrapping a real <textarea class="hc-code__input" name="…">. The value is a native form control, so it submits in forms and with htmx (data-hx-post, data-hx-include) and degrades to a plain monospace textarea with no JavaScript. Add data-gutter="line-numbers" and installCodeEditor() (shipped in the auto-init behaviors bundle) overlays a line-number gutter that re-numbers on input and scrolls in sync.

To keep the numbers aligned the behavior sets the textarea to not soft-wrap (wrap="off"); long lines scroll horizontally. Label the textarea (aria-label or a <label for>) — it is a form control.

Add data-lang to opt the editable field into a live highlight overlay. When the value resolves to a registered grammar, installCodeEditor() inserts a decorative, aria-hidden hc-code__highlight layer behind the textarea, re-tokenizes on input, and matches the textarea’s scroll. The textarea glyphs are hidden (its caret stays visible) so the coloured overlay — the same hc-code__tok spans and --hc-code-tok-* palette as the read-only syntax highlighting below — shows through. It is CSP-safe (self-hosted, no eval), and purely additive: with no JS, an unknown data-lang, or no registered grammar, the field stays the plain monospace textarea and its value still submits.

Built-in grammars cover sql, json, yaml, and html (with yml / xml aliases). Tokenization is throttled to one render per animation frame, so large buffers stay responsive.

A generic grammar can’t know dialect constructs — TesseraQL’s 2-way SQL directives (/*%if … */, binds) should read as meta, which only its own tokenizer knows. Register one with registerCodeLanguage(name, tokenizer). A tokenizer maps source text to { tok, text } tokens whose text parts reconstruct the input exactly; tok is an hc-code__tok value (or falsy for plain text).

Register before the field is enhanced — like setMessages(), the auto-init behaviors bundle scans on load, so use the named installers from the main entry for full control over ordering:

import { registerCodeLanguage, installCodeEditor } from '@hypermedia-components/core';
// A dialect tokenizer that classifies 2-way-SQL directives as `meta`.
registerCodeLanguage('tql-sql', (text) => {
const tokens = [];
const re = /\/\*[%#@][\s\S]*?\*\/|\b(?:SELECT|FROM|WHERE|LIMIT)\b/gi;
let last = 0;
let m;
while ((m = re.exec(text))) {
if (m.index > last) tokens.push({ tok: '', text: text.slice(last, m.index) });
tokens.push({ tok: m[0].startsWith('/*') ? 'meta' : 'keyword', text: m[0] });
last = m.index + m[0].length;
}
if (last < text.length) tokens.push({ tok: '', text: text.slice(last) });
return tokens;
});
installCodeEditor(); // now enhances <div class="hc-code" data-editable data-lang="tql-sql">

If a tokenizer throws or its tokens don’t reconstruct the source, the overlay declines to highlight that buffer rather than desync from the textarea — the text stays visible, just uncoloured. A registration overrides a built-in of the same name and returns an uninstaller. Registering server-side? Port the same tokenizer to JS and the editor matches the server-rendered read-only view.

Read-only highlighting is server-tokenized: the server wraps each token in <span class="hc-code__tok" data-tok="…">, coloured from the --hc-code-tok-* palette. There is no client tokenizer — like the diff hunks, the kit only styles what the server emits, so it stays CSP-safe and deterministic. Tokens nest inside <pre><code> (plain) or hc-code__line (line-numbered / diff) and compose with data-state tints and the diff gutter.

data-tok is a generic, language-agnostic vocabulary — keyword, string, number, comment, operator, identifier, the structured-markup set property (object / mapping keys), tag, and attribute, plus meta (a catch-all for language-specific constructs, e.g. 2-way SQL directives and binds). An unknown or absent data-tok renders as plain code. The editable field’s live highlight overlay renders these very same spans, so the editor matches the read-only / diff surfaces.

SELECT id, 1
FROM members — active only

Token colour wins over a context line’s muted text; line tints (data-state) and the diff gutter are unaffected. The palette meets WCAG AA contrast on the code surface in both themes (verified by code-syntax.spec.mjs).

  • A horizontally scrollable block must be reachable by keyboard. Make it a focusable, labelled region: tabindex="0" plus role="region" and an aria-label naming the file or snippet.
  • In diff mode, colour is never the only cue — the gutter prints a + (added) or - (removed) sign. The sign rides the list marker, so copying the block copies the code text without the signs or line numbers.
  • The line text is rendered verbatim; preserve meaningful leading whitespace in the markup (white-space: pre). Set data-wrap="on" only when wrapping long lines is preferable to a scroll.
  • The editable field’s live-highlight overlay is decorative (aria-hidden): the textarea stays the labelled control and the value submits unchanged. Tokenization is throttled to one render per frame, so large buffers stay responsive while typing.
Token pathPurpose
code.bgSurface background.
code.fgCode text colour.
code.borderBorder colour.
code.radiusCorner radius.
code.padding-block / code.padding-inlineBlock padding.
code.font-familyMonospace font stack.
code.font-sizeCode font size.
code.line-heightCode line height.
code.gutter-fgLine-number / sign colour.
code.gutter-widthWidth of the line-number gutter.
code.num-widthWidth of each diff line-number column.
code.gutter-gapGap between gutter and code.
code.context-fgDiff context-line text colour.
code.added-bg / code.added-markeradded / covered tint & bar.
code.removed-bg / code.removed-markerremoved / missed tint & bar.
code.focus-borderFocus ring on the editable field.
code.input-min-heightMinimum height of the editable textarea.
code.tok-keyword / -string / -number / -comment / -operator / -identifier / -metaSyntax-token foregrounds (data-tok).
code.tok-property / -tag / -attributeStructured-markup token foregrounds (keys, HTML tags & attributes).

The tinted-state tokens resolve through the semantic status palette, so they re-resolve automatically under data-theme="dark".

Show the generated CSS variables
--hc-code-bg | -fg | -border | -radius
--hc-code-padding-block | -padding-inline
--hc-code-font-family | -font-size | -line-height
--hc-code-gutter-fg | -gutter-width | -num-width | -gutter-gap
--hc-code-context-fg
--hc-code-added-bg | -added-marker
--hc-code-removed-bg | -removed-marker
--hc-code-focus-border | -input-min-height
--hc-code-tok-keyword | -tok-string | -tok-number | -tok-comment
--hc-code-tok-operator | -tok-identifier | -tok-meta
--hc-code-tok-property | -tok-tag | -tok-attribute
  • Table — for tabular data; often hosts a code or status column.
  • Status colors — the same semantic palette the per-line data-state tints resolve through.
  • kbd — inline monospace key hints, as opposed to a multi-line block.