Malevich

Blocks/

Code-block

Multi-line code with optional highlighter adapter + copy + line numbers.

Code-block

Multi-line code surface with optional syntax highlighting (via adapter), language label, line numbers, and built-in copy button (via the copyable behavior, default-on).

Per the answered design question:

When to use

For inline code inside prose, use Code (elements/display/code/).

Anatomy

<!-- Plain block, no highlighter registered: renders as plain text + copy -->
<pre class="code-block" data-lang="ts">
<code>const x = 1;
const y = 2;
const z = x + y;</code>
</pre>

<!-- With language label, line numbers, copy on hover -->
<pre class="code-block" data-lang="bash" data-line-numbers="true">
<code>pnpm install
pnpm dev</code>
</pre>

<!-- Variants -->
<pre class="code-block -flat" data-lang="css">…</pre>
<pre class="code-block -on-inverse" data-lang="md">…</pre>

Registering a highlighter

import { registerHighlighter, refreshCodeBlocks } from "@malevich/components";
import { codeToHtml } from "shiki";

registerHighlighter((code, lang) => {
  // Shiki returns full <pre><code>…; strip the outer wrapper to
  // return inner HTML only.
  const html = codeToHtml(code, { lang, theme: "github-light" });
  const match = html.match(/<code[^>]*>([\s\S]*?)<\/code>/);
  return match ? match[1] : code;
});

refreshCodeBlocks();

The adapter contract is intentionally narrow:

type Highlighter = (code: string, lang: string) => string;

Return the inner HTML for the <code> element. The component owns the surrounding chrome (border, padding, copy button, language label). Authors may use Shiki, Prism, Highlight.js, or a custom highlighter — any function matching the signature works.

Variants

Variant Class Effect
Default .code-block Bordered card on canvas
Flat .code-block.-flat No border, raised background
Inverse .code-block.-on-inverse Dark surface

Tokens used

Copy button

The copy button comes from the copyable behavior modifier. By default data-copyable="true" is implicit per v1-master-diff §4.8. Opt out per instance:

<pre class="code-block" data-copyable="false">…</pre>

The runtime injects the copy button automatically once init() runs. Clipboard text excludes the language label and copy button itself.

Accessibility

Edge cases

Do

Don't