registry:ui · Display
code-card — the readonly code surface, on Shiki
A figure + pre/code base with a filename-tab head and a compact copy control, highlighted by Shiki through lib/shiki: grammars and themes are separate lazy chunks fetched exactly when a card first needs them (shiki/core + the JavaScript regex engine — no WASM), and the default jixoai theme is Shiki's own css-variables recipe bound to the --tok-* palette, so token paint rides the design tokens in both themes. Code is always a runtime prop — Shiki escapes it, so samples containing literal script-closing tags are inert data; the pre is the scrollport: horizontal always, vertical when maxHeight caps it.
$npx jixoai-ui add code-card
https://ui.jixoai.com/r/code-card.json
overview
Overview
A readonly code surface that owns its highlight pipeline: six installable backends, on-demand grammars, and a token palette that rides the design tokens.
CodeCard renders one figure — filename tab, header snippet, the scrollport pre, footer
snippet, copy control — around a runtime code string. Highlighting is the
ENGINE SEAM: a backend prop outranks the HIGHLIGHT_KEY context, which outranks the stock shiki default — six
installable backends (shiki, prismjs, highlightJs, sugarHigh, treeSitter,
microLighter) over the same contract. Grammars and themes are lazy chunks fetched the
first time a card needs them; a page pays only for the engines it actually paints.
Unlike the inline-code chip's zero-markup range engine, shiki backends emit REAL spans
into the pre — the trade is markup for print survival and grammar depth. Kinship, named
precisely: inline-code shares the context KEY and the structural types (the same HIGHLIGHT_KEY seam) but ships the microlighter range engine as its stock —
the two families never mount each other; the engine matrix is the shared surface.
The eight-axis surface is SEVEN lanes by law: the shiki theme literal owns
the theme name (the terminal bezel twins' §13 precedent), and the universal theme lane is dropped at the source (never stamped, never broadcast) — the
card's own dark adaptation answers to tree ancestry instead, through the family's
declared .dark/.jx-light token re-declarations. Density is
inert on this fixed-voice surface; the carriers stamp and broadcast to nothing. Per-axis
below.
code-card
The complete card: filename tab (head left), header snippet (head right, replacing the default lang label), footer snippet (foot left), and the copy control (foot right). The Playground swaps the backend engine, the language and the theme — each first paint fetches exactly that engine's grammar/theme chunks (nothing of an engine joins the page until its card paints).
import { UniPty } from '@unipty/core';
import { createNodePtyBackend } from '@unipty/backend-node-pty';
// readiness happens before Core construction
const backend = await createNodePtyBackend({ runtime: 'node' });
const unipty = new UniPty({ backend });
const pty = unipty.spawn(['bash'], {
terminal: { cols: 80, rows: 24 },
});
pty.resize(120, 36); // Character-Cell Size<script lang="ts">
import CodeCard from '@ui/code-card.svelte';
</script>
<!-- the sample is a runtime prop: the engine escapes it, so a literal </script>
inside it is inert data — nothing to escape at the template level -->
<CodeCard filename="spawn.ts" lang="ts" theme="jixoai" code={sample}>
{#snippet header()}
<span class="pill">shiki · ts</span>
{/snippet}
{#snippet footer()}
<span>powered by Shiki</span>
{/snippet}
</CodeCard>scroll law
The pre is the single scrollport: long lines scroll horizontally (Tab characters stay tabs, never wrapped), maxHeight caps the body into vertical scrolling, scrollbars are thin currentColor lanes with overscroll containment, and the region is keyboard-focusable.
// one rule: the <pre> is the scrollport — Tab stays Tab, long lines scroll
const manifest = {
route: 'registry/files/ui/code-card.svelte',
engine: 'shiki/core + JavaScript regex engine (no WASM)',
langs: 'typescript tsx javascript jsx svelte html css scss json bash markdown yaml vue',
themes: 'jixoai (zero-download, --tok-*) + github/vitesse/min families',
fallback: 'prerendered plain text → upgraded after hydration, zero layout shift',
};
// ── a deliberately wide line to force the horizontal lane ────────────────────────────────────────
console.table(Object.entries(manifest).flatMap(([key, value]) => [{ key, value }]));// one rule: the <pre> is the scrollport — Tab stays Tab, long lines scroll
const manifest = {
route: 'registry/files/ui/code-card.svelte',
engine: 'shiki/core + JavaScript regex engine (no WASM)',
langs: 'typescript tsx javascript jsx svelte html css scss json bash markdown yaml vue',
themes: 'jixoai (zero-download, --tok-*) + github/vitesse/min families',
fallback: 'prerendered plain text → upgraded after hydration, zero layout shift',
};
// ── a deliberately wide line to force the horizontal lane ────────────────────────────────────────
console.table(Object.entries(manifest).flatMap(([key, value]) => [{ key, value }]));law
Based on Shiki — a facade, not a wrapper
lib/shiki adds loading strategy only: a lazy singleton over shiki/core with the JavaScript regex engine, one dynamic import per grammar and theme, and a zero-download default theme from Shiki's css-variables factory. It never re-interprets Shiki output — getHighlighter() hands back the stock HighlighterCore and highlightCode() forwards Shiki's own codeToHtml options (transformers, dual themes, decorations) untouched, so the whole Shiki ecosystem works here as-is.
what the card owns
- progressive paint: the prerendered sample is escaped plain text; after hydration Shiki upgrades the SAME
<code>element — zero layout shift - on-demand loading: shiki/core + engine on first highlight; every grammar/theme its own lazy chunk, fetched only when requested
- named themes ride along — the theme's editor colors from Shiki's pre output apply verbatim to the card's pre
- scroll law: horizontal always, vertical under
maxHeight, thin scrollbars, keyboard-focusable pre
what the consumer owes
- a runtime string — never inlined markup between component tags
- a Shiki language id for
lang(aliases like ts/sh/md resolve in lib/shiki) - extra grammars/themes in one line:
registerLanguage('python', () => import('shiki/langs/python.mjs')) - nothing else — npm
shikiinstalls with the registry item
matrix
The engine matrix — one contract, six installable engines
shiki is the pinned default: installing code-card installs shiki and nothing else. Five more engines exist as their own registry items, each a factory returning the same HighlightBackend — markup backends write token spans into the card's code element (the paint survives the print pipeline's freeze clone), microlighter is the range model: zero markup, ranges registered in the CSS Custom Highlight API over the plain text (which is why it prints plain — see the table). Every engine is lazy: importing a factory pulls no engine code; the download happens at a card's first paint.
| engine | output | size posture | languages | tailoring | in one line | |
|---|---|---|---|---|---|---|
shiki() default @jixoai/highlight-shiki | markup | shiki/core + the JavaScript regex engine at first paint (no WASM); every grammar and theme its own lazy chunk | 13 curated — typescript tsx javascript jsx svelte html css scss json bash markdown yaml vue, plus the alias table | langs subset per instance | spans survive the freeze clone | the default — TextMate accuracy, the zero-download jixoai theme |
prismjs() @jixoai/highlight-prismjs | markup | core + one lazy chunk per grammar; themes are global stylesheets — one active per document | the curated set minus svelte/vue (prismjs 1.30 ships neither): markup css clike js ts jsx tsx json bash markdown yaml scss | langs subset per instance | spans survive the freeze clone | the classic class-based workhorse |
highlightJs() @jixoai/highlight-highlightjs | markup | lib/core — the zero-language kernel — plus exactly the grammars you register: unselected grammars never import or fetch (the runtime-size flagship; lazy chunks of the curated set still emit) | typescript javascript xml css scss json bash markdown yaml; highlight.js 11 ships no tsx/jsx/svelte/vue module — those reject with a shiki hint | langs subset per instance | spans survive the freeze clone | core + selective registration |
sugarHigh() @jixoai/highlight-sugar-high | markup | one whole bundle — 9.90 KiB min / 4.35 KiB gzip for a TS scene, its README benchmark measured on 2.2.2 (same table: prism 14.63, hljs 29.54) | 29 canonical languages; no tsx (typescript covers it only as an input alias) — rejects with a shiki hint | none — the whole bundle IS the minimal form (the minimal-engine exemption) | spans survive the freeze clone | the featherweight |
treeSitter() @jixoai/highlight-tree-sitter | markup | the wasm core runtime + one wasm per grammar, emitted from npm assets by your bundler (?url) at first paint — the heavyweight | typescript tsx javascript jsx only (jsx is the javascript grammar’s native dialect) | langs subset · wasmBase / wasmLoader asset channels | spans survive the freeze clone | real syntax trees, query captures |
microLighter() @jixoai/highlight-microlighter | range | a ~2 kB engine core (its own size budget) + grammars + one theme sheet, all lazy — the code element keeps its plain text node | its 37 bundled TextMate grammars (typescript tsx javascript vue html css json bash markdown yaml …) | none — the range model ships no langs gate (engines without the capability are exempt) | ranges do NOT survive the freeze clone — prints plain; pin a markup backend for paper | the range-model proof (CSS Custom Highlight API) |
add an engine — one item per engine
# code-card ships ONE engine by default: shiki (via @jixoai/highlight-shiki)
npx jixoai-ui add @jixoai/highlight-shiki # already aboard with code-card
npx jixoai-ui add @jixoai/highlight-prismjs # + prismjs
npx jixoai-ui add @jixoai/highlight-highlightjs # + highlight.js
npx jixoai-ui add @jixoai/highlight-sugar-high # + sugar-high
npx jixoai-ui add @jixoai/highlight-tree-sitter # + web-tree-sitter + grammar wasm
npx jixoai-ui add @jixoai/highlight-microlighter # + microlighterEach item lands its factory in $lib/highlight/<engine> and declares only its own npm dependencies — sibling engines never ride along. microlighter + vite: its grammar
loading is runtime-templated inside the package and misses fail silently, so hosts owe
two lines — dev excludes it from the optimizer, builds emit its dist/grammars/*.js next to the engine chunk (this
site's vite.config.ts is the reference implementation). Breaking (2026-09-06): code-card used to bundle prismjs and microlighter as hard npm dependencies — it does
not anymore, and nothing is shimmed. If you consumed those factories, add the matching
item above; the $lib/highlight/<engine> import
path keeps working for every engine you install, and engines you never install simply
never download. The full migration table lives in the registry docs.
three configuration tiers
① per instance — the backend prop
Any consumer, any card: pass a factory product. The langs subset is the size lever — one instance's gate never narrows another's.
<script lang="ts">
import CodeCard from '@ui/code-card.svelte';
import { highlightJs } from '@lib/highlight/highlight-js';
</script>
<!-- one prop, one instance: this card registers ts + bash grammars only -->
<CodeCard lang="ts" code={src} backend={highlightJs({ langs: ['ts', 'bash'] })} />② subtree default — your ~10-line provider
The seam ships with the items (zero dependencies): HIGHLIGHT_KEY.
Cards without a backend prop eat the nearest
provider's default — the prop always wins.
<script lang="ts">
// highlight-default.svelte — any subtree root, ~10 lines, yours to own
import { setContext } from 'svelte';
import { HIGHLIGHT_KEY } from '$lib/highlight/context-key';
import { prismjs } from '$lib/highlight/prismjs';
// every card BELOW this root without a backend prop eats prism
setContext(HIGHLIGHT_KEY, { backend: prismjs() });
</script>
<slot /> <!-- your subtree -->③ kernel plugin — the site form
Sites that installed @jixoai/context-plugin can project
the default through the plugin kernel — here repairing microlighter's one known
limitation: under the print projection, swap the range backend for a markup one so
the freeze clone carries real spans. The kernel never rides the registry items;
this composition is app-side by law.
// print-markup-backend.ts — the kernel-plugin form (the shape sites
// that installed @jixoai/context-plugin run; the kernel never rides
// the registry items themselves)
import { definePlugin } from '$lib/context-plugin.svelte';
import { isPrintProjection } from '$lib/medium.svelte';
import { HIGHLIGHT_DEF } from '$lib/highlight/context.svelte';
import { shiki } from '$lib/highlight/shiki';
// print survival: under the print projection, swap the range backend
// for a markup one — spans survive the freeze clone, ranges do not
export const printMarkupBackend = definePlugin({
name: 'print-markup-backend',
targets: [HIGHLIGHT_DEF],
filter: (_def, env) => isPrintProjection(env.medium),
before: (backend) => (backend.id === 'microlighter' ? shiki() : backend),
});detection
lang="auto" — three detection rings, one optional item
Setting lang to the AUTO_LANG sentinel (strict equality — 'AUTO' and ' auto ' are ordinary language ids) runs detection before highlighting. The chain has exactly three rings: the langDetector prop, the HIGHLIGHT_DETECT_KEY context, and the backend's own detector — highlight.js instances carry one built in (highlightAuto over exactly the instance's languages). A ring resolving null is a no-opinion: the chain falls through (the rings complement, they never fight); a reject is terminal — plain text plus a warn naming the ring. Three no-opinion rings = plain text with the ids listed; zero rings at all = a runtime reject whose text carries the install command and both wiring forms below. Detection bytes load only when a card actually enters auto: a bare code-card install carries ZERO detector code, and the DLD ships as its own framework-free item whose four layers are each lazy modules — a layer that answers loads none of the layers beneath it.
two equivalent wirings — pick one
① the wrapper item — children form
npx jixoai-ui add @jixoai/highlight-detect-default pulls the DLD plus this ~10-line provider — import it from the folder barrel or
the direct .svelte path. Svelte context spreads
downward only: wrap the subtree, siblings stay untouched.
<script lang="ts">
import CodeCard from '$lib/ui/code-card';
import HighlightDetectDefault from '$lib/ui/highlight-detect-default';
</script>
<!-- form ①: the wrapper item — a ~10-line children provider whose body
IS the one line of form ②. Svelte context only spreads downward:
cards OUTSIDE this wrapper are untouched; nested wrappers, nearest
wins; the langDetector prop always outranks whatever is wired here -->
<HighlightDetectDefault>
<CodeCard filename="main.ts" lang="auto" code={sample} />
</HighlightDetectDefault>② hand-written — zero components
The lib item is framework-free by law; one setContext line at any subtree root wires the
same { detector } adapter shape the wrapper
stores — the card's reader cannot tell them apart.
<script lang="ts">
// highlight-detect-root.svelte — any subtree root, zero components
import { setContext } from 'svelte';
import { HIGHLIGHT_DETECT_KEY } from '$lib/highlight/context-key';
import { defaultLangDetector } from '$lib/highlight/default-detector';
setContext(HIGHLIGHT_DETECT_KEY, { detector: defaultLangDetector() });
</script>
<slot /> <!-- your subtree: every lang="auto" card below eats the DLD --># detection is an OPTIONAL capability — a bare code-card install carries
# ZERO detector bytes; lang="auto" without a ring rejects at runtime
npx jixoai-ui add @jixoai/highlight-lang-detector # the DLD lib: four-layer waterfall, framework-free
npx jixoai-ui add @jixoai/highlight-detect-default # + the children wrapper (form ① convenience)the DLD waterfall — cheap layers first, every layer its own lazy module
| layer | reads | answers from | cost |
|---|---|---|---|
L1 filename | filename | the extension table + the exact-basename table (Dockerfile, Makefile, tsconfig.json…): 'main.ts' → typescript, hit and done | pure table parse |
L2 shebang | first line | the interpreter table + first-line modeline: '#!/usr/bin/env python3' → python | pure table parse |
L3 structure | body shape | Markdown-guarded probes — whole-body JSON.parse, SVG/XML/HTML roots, YAML front-matter, TOML-before-INI sections; never a programming-language fingerprint | pure TS probes |
L4 statistical | body statistics | betlang wasm — 48 calibrated labels mapped through the canonical authority table; confident answers carry a 0..1 probability | ~98 KiB wasm, fetched on the first L1-L3 miss only |
'Four layers' counts the waterfall only — the card's detection rings stay three plus
the reject tail. The on-demand law cuts both ways: a
filename hit loads L2-L4 zero bytes (pick auto in the
playground — its filename answers at L1), and a bare
install loads the whole capability zero bytes — the DLD is never a hidden dependency of
code-card. A detection whose language the active engine's curated set rejects is
terminal by the same matrix law as any lang: the reject names the engines that cover it.
live — the filename-less lanes (structure and statistics)
code-card · auto lanes
no filename, no shebang — the body's shape answers at L3
{
"lane": "L3",
"why": "no filename, no shebang — but the whole body parses as JSON",
"result": { "lang": "json", "source": "structure" }
}no filename, no shebang, no shape — the wasm answers at L4
set -euo pipefail
# no filename, no shebang, no structural shape — the wasm answers:
# betlang's calibrated top label is Shell, the authority table maps it
# to bash, and the card paints it with whatever engine is selected
npx jixoai-ui add @jixoai/highlight-lang-detector<script lang="ts">
import CodeCard from '@ui/code-card.svelte';
import HighlightDetectDefault from '@ui/highlight-detect-default';
import { AUTO_LANG } from '@lib/highlight/lang-detector';
</script>
<!-- no filename, no shebang — the body's shape answers at L3 -->
<HighlightDetectDefault>
<CodeCard lang={AUTO_LANG} code={structureSample} copyable={false} />
</HighlightDetectDefault>
<!-- no shape either — the wasm answers at L4 -->
<HighlightDetectDefault>
<CodeCard lang={AUTO_LANG} code={statisticalSample} copyable={false} />
</HighlightDetectDefault>Both cards hydrate plain and upgrade after the detector resolves — prerendered output
is always plain text (detection never runs server-side). The wasm rides the @jixoai/ui-betlang-wasm npm package (betlang
= 0.1.1 pinned at the crate, checksum-gated build): vite emits it as a real asset
through the ?url channel, fetched the first time a
sample falls through L1-L3.
the detection lab — type anything, watch the waterfall answer
The traced DLD reports one row per executed layer — what it consulted, what it
answered, and how long it took; the layers below a hit render as skipped (the
short-circuit law made visible). The card at the bottom paints through the real
consumer path: lang="auto" inside the wrapper,
its own waterfall run, never a pre-computed verdict.
Detection is debounced 250 ms and re-runs the whole waterfall — no cache (design D6); the card below rides the REAL consumer path (lang="auto" inside the wrapper), not a pre-computed verdict.
no opinion anywhere — the card stays plain text (the [detect:all] law)
- L1 filename — no filename given — the table never consulted
set -euo pipefail
# no filename, no shebang, no structural shape — the wasm answers:
# betlang's calibrated top label is Shell, the authority table maps it
# to bash, and the card paints it with whatever engine is selected
npx jixoai-ui add @jixoai/highlight-lang-detectortypes
Code card variants
Head and foot are compositional; fill turns the card into a pinned-chrome panel.
code-card · variants
bare pre
const bare = true;filename tab + copy
export const hello = "world";header/footer snippets
npx jixoai-ui add code-card<script lang="ts">
import CodeCard from '@ui/code-card.svelte';
</script>
<CodeCard lang="ts" code={'const bare = true;'} copyable={false} />
<CodeCard filename="hello.ts" lang="ts" code={'export const hello = "world";'} />
<CodeCard filename="install.sh" lang="bash" code={'npx jixoai-ui add code-card'}>
{#snippet header()}
<span class="pill">registry</span>
{/snippet}
{#snippet footer()}
<span class="text-[11px] text-muted-foreground">terminal install</span>
{/snippet}
</CodeCard>usage
Usage
Code is always a runtime prop — Shiki escapes it, so samples containing literal closing tags are inert data.
<script lang="ts">
import CodeCard from '@ui/code-card.svelte';
</script>
<!-- the sample is a runtime prop: Shiki escapes it, so a literal </script>
inside it is inert data — nothing to escape at the template level -->
<CodeCard filename="spawn.ts" lang="ts" theme="jixoai" code={source}>
{#snippet header()}
<span class="pill">node-pty route</span>
{/snippet}
{#snippet footer()}
<span>powered by Shiki</span>
{/snippet}
</CodeCard>
<!-- fill mode: the parent owes the height; the pre is the only scroller -->
<CodeCard lang="svelte" code={sample} fill minHeight="12rem" />a11y
Accessibility
The pre is a labelled, keyboard-focusable scrollport; the copy control is a real button with state feedback.
Keyboard
| Key | Action |
|---|---|
| Tab | Reaches the scrollport (pre) and the copy control |
| ← / → / ↑ / ↓ | Scroll the focused pre — long lines horizontal, capped bodies vertical |
| Enter / Space | Activate the copy button |
ARIA
| Attribute | Value | Description |
|---|---|---|
| aria-label | "{filename|lang} code sample" | On the pre — the scrollport is named whether or not a filename tab exists. |
| aria-label | copy {filename|lang} sample | On the copy button; flips to "copied" for the 1.6s feedback window. |
theming
Density and tokens
Token paint end to end — the jixoai theme resolves to the --tok-* palette at paint time; the shell rides the --readonly-code-* tints.
export const density = "fixed rhythm";export const density = "fixed rhythm";export const density = "fixed rhythm";export const density = "fixed rhythm";| Token | Default | Source |
|---|---|---|
| --tok-token-keyword | var(--primary) | color |
| --tok-token-string | var(--accent) | color |
| --readonly-code-bg | muted 42% / background | color |
| --readonly-code-meta-bg / -fg | accent mixes | color |
| body rhythm | 13px mono, fixed padding | structural |
| --jx-text | 11 / 12 / 13 / 15px | density |
axes
Universal props
The eight-axis surface (explicit-props): size · shape · radius · density · color · theme · elevation · motion — each axis takes named steps, auto (inherit the ambient context; stamps nothing), an exact number (px · coefficient · dp · hue per axis), or query() for responsive/container-conditional values. SEVEN lanes — the shiki theme literal owns the theme name (the terminal bezel twins' precedent, §13 rules no rename; W6-dossier-flagged). The flat readonly surface carries no rung of its own.
| Property | Type | Default | Description |
|---|---|---|---|
| density | 'small' | 'medium' | 'large' | 'auto' | number (+ legacy spellings) | 'auto' | MEASURED INERT — the rung stamps data-density and the carriers broadcast, but this surface hosts no components and its own voices are fixed-scale: the 12.5px pre, the px paddings, the typed --jx-text-label head. A DensityDemo scope moves nothing (probe receipt). No tenant, no consumption — the quietest lane on the page. |
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | THE §11 ECHO, NOTHING FOLLOWS — the stamp lands verbatim (--jx-size-effective: 18px; font-size: var(--jx-size-effective, 1rem) measured on the root) and the card's paint is px-anchored: the pre stays 12.5px, the head stays the typed label step. The heading-contrast case: a stamp with no em to scale. Number unit: px. |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' | SUPPLY-ONLY — stamps --jx-shape-effective/--jx-radius-factor-effective; the bezel corners are the family radius law, not the axis (grep receipt: zero shape reads). Number unit: none. |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | SUPPLY-ONLY — stamps --jx-radius-effective; the shell corner is the family radius law (grep receipt: zero carrier reads). Number unit: px. |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' | SUPPLY-ONLY — stamps --jx-color-effective; no family css reads it (grep receipt). Token hue rides the theme sheet (--primary/--accent/--success…); the copied state retunes through the jx-hue-success injection. Number unit: hue degrees. |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' | DROPPED AT THE SOURCE (the §13 keep): stampCarriersForLanes receives theme: undefined and the lane is never broadcast — the shiki `theme` literal owns the theme name. What answers instead is ANCESTRY: the family declares its own .dark / .jx-light token re-declarations (the readonly tints + the tok palette dark formulas), so a dark TREE re-inks the card while the axis prop sits out (measured: a .dark wrapper flips the ground to the dark tint and re-derives the tok dark formulas; the card itself carries no .dark class). light/system/auto ride tree inheritance. No number lane. |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' | SUPPLY-ONLY — the card floats on the readonly tints; no shadow in the family css (grep receipt: zero box-shadow, zero carrier reads). Number unit: dp. |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' | SUPPLY-ONLY ON THE AXIS — the carrier is unread (grep receipt). The family reads the motion KERNEL directly: the scroll-edge veils fade on var(--motion-150)/var(--motion-ease-out) and prefers-reduced-motion kills both the veils and the copy transition. Animation law: none (the card is static; the veils are scroll-state, not choreography). Number unit: coefficient. |
Deviations, cited: the adoption is the census batch D row (explicit-props W3-D1 — openspec/changes/explicit-props/research/migration-census.md); the theme drop is the §13 keep (the shiki theme literal), W6-dossier-flagged. No query() seat: this surface hosts no components, so a responsive lane would re-base nothing — the density row's probe receipt is the demonstration.
CodeCard · universal props
const size = 18// named steps resolve via the alias ladder<CodeCard size={18} density="small">…</CodeCard>api
API
Thirteen props; code is the only required one — everything else is composition.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| code * | string | — | The sample (runtime prop; the backend escapes it into inert spans). |
| backend | HighlightBackend | context → shiki() | Highlight backend instance — shiki() | prismjs() | highlightJs() | sugarHigh() | treeSitter() | microLighter(); see the engine matrix. |
| lang | string | 'ts' | Language id; aliases (ts/sh/md/…) resolve in the active backend's table — or 'auto' (the AUTO_LANG sentinel, strict equality) to run the three-ring detection chain first; see lang="auto". |
| langDetector | LanguageDetector | — | Detection ring ①: an explicit detector outranking context and backend (defaultLangDetector() / betlangDetector() from the highlight-lang-detector item, or any { id, detect } implementation). |
| filename | string | '' | Filename tab on the head's left; head renders when it or header exists. With lang='auto' it feeds the DLD's L1 tables verbatim (paths included — the detector takes the last segment). |
| header | Snippet | — | Head-right area; replaces the default lang label. |
| footer | Snippet | — | Footer-left content. |
| copyable | boolean | true | Copy control on the footer bar's right. |
| maxHeight | string | '' | CSS length capping the body; turns on vertical scrolling. |
| fill | boolean | false | Stretch to the container height; the pre becomes the only scroll area. |
| minHeight | string | '' | Floors the card height; pairs with fill so short samples open readable. |
| class | string | '' | Forwarded to the figure. |
Universal props
| Property | Type | Default | Description |
|---|---|---|---|
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | the base scale — root font-size; parts size in em |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' · ambient scope | corner geometry (CSS corner-shape; §14 degrade table) |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | corner size; auto = the concentric broadcast (§3) |
| density | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | spacing/leading scale over the kernel channels (§4) |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' · ambient scope | the hue axis of the fixed oklch primary system (§5) |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' · ambient scope | light/dark profile; system = the JS-mutable global (§6) |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' · ambient scope | official M3 levels over the surface ladder (§7) |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' · ambient scope | intensity across the motion kernels (§8) |