jixoai-ui
the terminal design language
loading the style sheet…
Skip to content
docs · print medium: screen

registry:ui · Data Display

math-block — display math, baked at prerender

The display math surface: a native <figure> (figure semantics kept) framing a wide-equation strip that rides the SHARED scroll-run contract wholesale — createScrollStamp's verdict (data-jx-scroll-state none|start-closed|end-closed|open) gates ScrollChrome's shadow veil and nudge chips, so a formula that fits paints no chrome — with real KaTeX markup rendered SYNCHRONOUSLY during SSR/prerender (the isomorphic-small lane: true zero flash, zero CLS, print-freeze-safe markup — NOT code-card's floor-then-upgrade), a copy control carrying the raw TeX source (press physics, clipboard fallback, labels localization payload), and errors painted IN PLACE (the errorColor token run inside the same box, one console.warn diagnostic; a caller-forced throw degrades to the raw source — errors never escape a component boundary).

install — math-block

$npx jixoai-ui add math-block

https://ui.jixoai.com/r/math-block.json

based on KaTeX sync SSR lane · zero flash scroll-run strip · shared verdict errors paint in place

usage

Usage

tex is a runtime string rendered synchronously in display mode — the markup IS the paint, so prerender bakes it and a prop change re-derives live.

MathBlock usage
<script lang="ts">
  import MathBlock from '@ui/math-block';
</script>

  let tex = $state('\\int_{-\\infty}^{\\infty} \\mathrm{e}^{-x^2} \\, dx = \\sqrt{\\pi}');
</script>

<!-- display math: markup bakes during prerender — zero flash, zero CLS -->
<MathBlock {tex} />

<!-- the wide strip: long formulas scroll, chips and veil ride the
     shared scroll-run verdict; copyable=false drops the footer -->
<MathBlock
  tex="\begin{aligned} \nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0} \\ \nabla \times \mathbf{B} &= \mu_0 \mathbf{J} \end{aligned}"
  copyable={false}
/>

math-block

The complete figure: the strip (the run is the scroller), ScrollChrome's shadow veil + nudge chips gated by the scroll verdict, and the copy control carrying the raw TeX. The playground swaps the formula — a wider one flips the verdict open and the chrome arms.

eiπ+1=0\mathrm{e}^{\mathrm{i}\pi} + 1 = 0
math-block-usage.svelte svelte
<script lang="ts">
  import MathBlock from '@ui/math-block';
</script>

<!-- the tex prop is a runtime string; the markup bakes at prerender -->
<MathBlock tex="\mathrm{e}^{\mathrm{i}\pi} + 1 = 0" />

scroll law

Wide equations — the shared scroll strip

A display formula is not prose: it must not wrap. The strip is a scroll-run (data-jx-scroll-run + data-axis=horizontal) inside a one-cell grid host; the stamp verdict gates the shadow veil and the two nudge chips, and a formula that fits paints no chrome at all.

math-block · wide strip

aligned — Maxwell's equations

∇⋅E=ρε0∇⋅B=0∇×E=−∂B∂t∇×B=μ0J+μ0ε0∂E∂t\begin{aligned} \nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0} & \nabla \cdot \mathbf{B} &= 0 \\ \nabla \times \mathbf{E} &= -\frac{\partial \mathbf{B}}{\partial t} & \nabla \times \mathbf{B} &= \mu_0 \mathbf{J} + \mu_0 \varepsilon_0 \frac{\partial \mathbf{E}}{\partial t} \end{aligned}

matrices — a linear system wider than the column

Ax=b,A=(a11a12⋯a1na21a22⋯a2n⋮⋮⋱⋮am1am2⋯amn),x=(x1x2⋮xn),b=(b1b2⋮bm),∥Ax−b∥2≤ε(∥A∥2 ∥x∥2+∥b∥2)\mathbf{A}\mathbf{x} = \mathbf{b}, \quad \mathbf{A} = \begin{pmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{pmatrix}, \quad \mathbf{x} = \begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix}, \quad \mathbf{b} = \begin{pmatrix} b_1 \\ b_2 \\ \vdots \\ b_m \end{pmatrix}, \quad \lVert \mathbf{A}\mathbf{x} - \mathbf{b} \rVert_2 \le \varepsilon \left( \lVert \mathbf{A} \rVert_2 \, \lVert \mathbf{x} \rVert_2 + \lVert \mathbf{b} \rVert_2 \right)

Both ride the same contract as tabs and button-group strips — the ONE scrollable-region system, never a local copy. The run measures along the inline axis only; the verdict none | start-closed | end-closed | open is the single truth every overlay keys on, and pre-hydration the shared css paints nothing (the prerendered markup stands alone).

math-block-strip-demo.svelte svelte
<script lang="ts">
  import MathBlock from '@ui/math-block';
</script>

<div class="border border-border p-4">
  <p class="font-nav mb-3 text-[11px] uppercase tracking-[0.24em] text-muted-foreground">
    aligned — Maxwell's equations
  </p>
  <MathBlock tex="\begin{aligned}\nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0} & \nabla \cdot \mathbf{B} &= 0 \\\nabla \times \mathbf{E} &= -\frac{\partial \mathbf{B}}{\partial t} & \nabla \times \mathbf{B} &= \mu_0 \mathbf{J} + \mu_0 \varepsilon_0 \frac{\partial \mathbf{E}}{\partial t}\end{aligned}" copyable={false} class="w-full max-w-[42rem]" />
</div>
<div class="border border-border p-4">
  <p class="font-nav mb-3 text-[11px] uppercase tracking-[0.24em] text-muted-foreground">
    matrices — a linear system wider than the column
  </p>
  <MathBlock tex="\mathbf{A}\mathbf{x} = \mathbf{b}, \quad \mathbf{A} = \begin{pmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{pmatrix}" class="w-full max-w-[42rem]" />
</div>

no-scroll variant

Fit — scale instead of scroll

fit=true forces the formula into the container: a font-size scale (KaTeX is em-based throughout, so it re-lays out honestly — no transform residue, no layout compensation), never scaling up. Print engages fit by DEFAULT (beforeprint/afterprint track the medium): a paged sheet never owes a horizontal scrollport — print this page and the linear system below arrives whole.

math-block · fit

the same linear system, fitted — no strip, no chips

Ax=b,A=(a11a12⋯a1na21a22⋯a2n⋮⋮⋱⋮am1am2⋯amn),x=(x1x2⋮xn),b=(b1b2⋮bm),∥Ax−b∥2≤ε(∥A∥2 ∥x∥2+∥b∥2)\mathbf{A}\mathbf{x} = \mathbf{b}, \quad \mathbf{A} = \begin{pmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{pmatrix}, \quad \mathbf{x} = \begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix}, \quad \mathbf{b} = \begin{pmatrix} b_1 \\ b_2 \\ \vdots \\ b_m \end{pmatrix}, \quad \lVert \mathbf{A}\mathbf{x} - \mathbf{b} \rVert_2 \le \varepsilon \left( \lVert \mathbf{A} \rVert_2 \, \lVert \mathbf{x} \rVert_2 + \lVert \mathbf{b} \rVert_2 \right)

The fitter measures the formula's natural width and clamps the wrapper's font-size to the run's client box (re-measured on resize); when fit is off the inline scale clears and the scroll law owns the strip again — the stamp verdict follows the restored width naturally.

math-block-fit-demo.svelte svelte
<script lang="ts">
  import MathBlock from '@ui/math-block';
</script>

<!-- fit: the no-scroll variant — a font-size scale, never scaling up -->
<div class="border border-border p-4">
  <p class="font-nav mb-3 text-[11px] uppercase tracking-[0.24em] text-muted-foreground">
    the same linear system, fitted — no strip, no chips
  </p>
  <MathBlock tex="\mathbf{A}\mathbf{x} = \mathbf{b}, \quad \lVert \mathbf{A}\mathbf{x} - \mathbf{b} \rVert_2 \le \varepsilon" fit copyable={false} class="w-full max-w-[42rem]" />
</div>

errors

Invalid TeX paints in place

throwOnError:false is the facade default: the bad source run renders error-tinted inside the same box — no error chrome, no thrown exception — and one console.warn carries the KaTeX diagnostic.

math-block · error paint

an unclosed group — \frac{ left open

\frac{\pi}{2} + \frac{

The error run paints in var(--error) — katex's own output under the facade's errorColor token binding, so light/dark follows the sheet with zero re-render. A caller forcing throwOnError: true (strict options) still gets a safe surface: the component catches, paints the escaped raw source, and warns — errors never escape a component boundary.

math-block-error-demo.svelte svelte
<script lang="ts">
  import MathBlock from '@ui/math-block';
</script>

<!-- throwOnError:false is the facade default: the bad source run
     renders error-tinted inside the same box -->
<div class="border border-border p-4">
  <p class="font-nav mb-3 text-[11px] uppercase tracking-[0.24em] text-muted-foreground">
    an unclosed group — \frac{ left open
  </p>
  <MathBlock tex="\frac{\pi}{2} + \frac{" class="w-full max-w-[40rem]" />
</div>

a11y

Accessibility

The figure keeps figure semantics; role=math lives on the inner content-only wrapper, so the copy button stays a discoverable interactive node. KaTeX's hidden MathML is the screen-reader path — no aria-label shadows it.

Keyboard

KeyAction
TabReaches the strip (focusable scroll run) and the copy control
← / →Scroll the focused strip when the verdict is open
Enter / SpaceActivate the copy button (raw TeX payload)

ARIA

AttributeValueDescription
role="math"inner wrapperOnly the katex output carries it — the figure and the button keep their own semantics.
hidden MathMLkatex outputThe screen-reader path ships in the markup itself (output: htmlAndMathml default); surfaces mount no aria-label.
aria-labelcopy / copiedOn the copy button; flips for the 1.6s feedback window.

theming

Tokens

The surface owns almost no paint: KaTeX paints in currentColor, the error run rides the error token, and the strip's chrome rides the shared scroll-run knobs on the host.

TokenDefaultSource
--errorjixoai-theme error tokencolor
--ringjixoai-theme ring tokencolor
--jx-scroll-veilinset · 1.5color
--jx-scroll-chevron-*shared defaultscolor

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. FIRST-TIME contract, all no-own: the KaTeX engine's internals (the em-based formula scale, the fit-mode compensation) are OUTSIDE the supply set — the surface rides the family's OWN figure root (the size lane scales the shell chrome).

MathBlock · universal props

The engine's em scale is outside the supply set; the shell's chrome scales.

math-block-universal.svelte svelte
<MathBlock size="medium">…</MathBlock>

api

API

Six props plus the HTML rest; tex is the only required one. Everything else is composition over the facade.

Properties

PropertyTypeDefaultDescription
tex * string—TeX source (runtime string — rendered synchronously in display mode).
copyable booleantrueCopy control on the footer bar (press physics, clipboard fallback, 1.6s copied feedback; the payload is the raw TeX).
labels { copy?: string; copied?: string }—Localization payload for the control vocabulary; absent = the English literals shipped, rendered byte-identically.
macros KatexOptions['macros']—KaTeX macros — merged per key OVER the site-level registerMacros table ($lib/katex).
strict KatexOptions['strict']—KaTeX strict mode passthrough (boolean | ignore | warn | error | handler).
trust KatexOptions['trust']—KaTeX trust passthrough (boolean | handler).
fit booleanfalse (print: true)The no-scroll variant: force-scale the formula into the container instead of scrolling — a font-size fit (KaTeX is em-based, so it is a true re-layout), never scaling up. PRINT engages fit by default regardless of the prop: a paged sheet never owes a horizontal scrollport.
class string''Merged onto the figure through cn().
…rest HTMLAttributes<HTMLElement>—Spread onto the figure BEFORE the component’s own data-kind/data-jx-math-block stamps — consumer attributes land, component semantics stay un-overridable.

Universal props

PropertyTypeDefaultDescription
size'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopethe base scale — root font-size; parts size in em
shape'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto''auto' · ambient scopecorner geometry (CSS corner-shape; §14 degrade table)
radius'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopecorner size; auto = the concentric broadcast (§3)
density'small' | 'medium' | 'large' | 'auto' | number'auto' · ambient scopespacing/leading scale over the kernel channels (§4)
color'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string'auto' · ambient scopethe hue axis of the fixed oklch primary system (§5)
theme'light' | 'dark' | 'system' | 'auto''auto' · ambient scopelight/dark profile; system = the JS-mutable global (§6)
elevation'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number'auto' · ambient scopeofficial M3 levels over the surface ladder (§7)
motion'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number'auto' · ambient scopeintensity across the motion kernels (§8)
jixoai-ui ui.jixoai.com the jixoai design language

Theme

Brand hue

0°

Auto cycle · 24h