Math and code
Build-time mathematical typesetting and readable, copyable source listings.
Available. Math, Code, Markdown equations, and styled code fences work in the current site. Math and syntax highlighting render during the build. Readers receive the equation and source immediately; only copying needs a small browser script.
Start with Markdown
Enable alkemist() in astro.config.ts. Ordinary fenced code in both .md and .mdx uses the same Alkemist renderer as Code, including filename, Copy, highlights, focus, changes, and folds. No component import or Alkemist Layout is required: the integration supplies the scoped styles and Copy behavior.
| Authoring syntax | Output |
|---|---|
| Fenced code with a language and metadata | Alkemist Code presentation and behavior |
$...$ and $$...$$ | KaTeX typesetting, shared with Math |
| Headings, paragraphs, emphasis, links, lists, tables, images | Semantic HTML styled by the site’s theme |
Imported Code, Math, Chart, and other components | Explicit props and slots for richer content in Astro or MDX |
The integration owns Markdown rendering; the theme owns ordinary HTML styling. It does not replace every paragraph or heading with a JavaScript component or impose the full site theme on an existing website. Use the Alkemist Layout or theme when you want those typography defaults. See the native HTML and media reference.
Write mathematics naturally
Use single dollar signs for inline notation, such as , and a pair of dollar signs on their own lines for a display equation. Escape a literal dollar sign with \$.
The oscillator satisfies $\ddot{x} + 2\zeta\omega_n\dot{x} + \omega_n^2x = 0$.
$$
x(t) = e^{-\zeta\omega_n t}\cos(\omega_d t)
$$The oscillator satisfies . A useful solution for an underdamped system, with , is:
This particular solution has initial displacement and velocity . Initial conditions belong in the explanation, not just the formula.
Matrices and aligned derivations use ordinary TeX environments:
A fenced math block also produces a display equation. Use a tex or latex fence when you want to show its source instead.
The component form
Math works in Astro and MDX. String.raw keeps TeX backslashes intact when the source is a JavaScript string.
---
import Math from '@alkemdotdev/alkemist-components/math';
---
<Math
tex={String.raw`\int_{\partial\Omega}\omega = \int_\Omega d\omega`}
label="Stokes’ theorem, for a smooth oriented manifold and suitable differential form."
/>Math
Preview loads when it becomes visible.
Component parameters
Source code
---
import Math from "@alkemdotdev/alkemist-components/math";
---
<Math
tex={"E = mc^2"}
display={true}
label={"Mass–energy equivalence"}
/>| Property | Default | Behavior |
|---|---|---|
tex: string | Required | TeX source without dollar delimiters. |
display: boolean | true | A scrollable equation figure; set false for inline notation. |
label: string | Omitted | Optional visible caption and accessible name for the display figure. |
Use \tag{1} inside a display expression for an explicit equation number. Automatic cross-document equation numbering and bibliography management are not implemented yet.
Rendering and correctness
The KaTeX renderer emits visual HTML and accompanying MathML. KaTeX’s own mathematical fonts remain separate from Ubuntu and Ubuntu Mono so fractions, symbols, and stretchy delimiters retain their intended metrics. Display figures scroll horizontally when a long expression needs more room.
Alkemist sets trust: false, rejects invalid or unsupported TeX at build time, and bounds macro expansion and explicit sizing. In Markdown, an additional error gate turns rehype-katex diagnostics into build failures. A broken formula must be corrected before publication. HTML extensions and trusted URL commands are not enabled.
KaTeX is a TeX mathematics renderer, not a complete LaTeX document compiler. Check its supported functions when using specialized notation. Correct rendering does not establish that a mathematical claim is true.
Code fences with context
Code listings share Ubuntu Mono, the same ink palette, and a #111 panel in both board themes. Keeping the panel dark gives the fixed inks sufficient contrast for small source text. The header contains just the filename and Copy. A narrow line-number gutter leaves room for the source. Highlights, focused lines, changes, folds, and notes help a reader follow an explanation.
Add a language, optional title, and one-based highlight ranges to a Markdown fence:
```typescript title="oscillator.ts" {2,4-5}
const damping = 0.25;
const frequency = 2;
const omegaD = frequency * Math.sqrt(1 - damping ** 2);
const displacement = (t: number) =>
Math.exp(-damping * frequency * t) * Math.cos(omegaD * t);
```const damping = 0.25;
const frequency = 2;
const omegaD = frequency * Math.sqrt(1 - damping ** 2);
const displacement = (t: number) =>
Math.exp(-damping * frequency * t) * Math.cos(omegaD * t);Highlight individual lines with {2,4} or inclusive ranges with {4-7}. Add no-line-numbers to the fence metadata to hide the gutter. Titles currently use double quotes: title="filename.ts". Metadata is rendered as text, not evaluated as HTML.
Direct attention without hiding context
Use focus={6-8} to dim surrounding lines, ins={2} and del={1} to mark additions and removals, and collapse={1-4} to put setup behind a disclosure. Line numbers always refer to the original source. Multiple ranges use commas; collapsed ranges must not overlap.
4 lines collapsed
const damping = 0.25;
const frequency = 2;
const duration = 10;
const samples = 300;
const omegaD = frequency * Math.sqrt(1 - damping ** 2);
const displacement = (t: number) =>
Math.exp(-damping * frequency * t) * Math.cos(omegaD * t);const samples = 100;
const samples = 300;
const duration = 10;Add wrap for long prose-like lines. Code normally scrolls horizontally. Use the component form for custom fold labels, inline text highlights, and line annotations.
The code component
Use a child slot for snippets authored directly in Astro or MDX:
<Code lang="javascript" title="answer.js">const answer = 42;</Code>const answer = 42;For source containing braces, tags, or significant line breaks, pass a string as the child. This prevents Astro/MDX from interpreting the snippet as template syntax:
<Code lang="typescript" title="advance.ts">{String.raw`
function advance(position: number, velocity: number, dt: number) {
return position + velocity * dt;
}
`}</Code>function advance(position: number, velocity: number, dt: number) {
return position + velocity * dt;
}Slot input removes surrounding blank lines and shared author indentation. Use code={source} when source is imported with Vite’s ?raw suffix, generated programmatically, or must retain every byte of whitespace. Supply either code or a text child, not both. Default MDX paragraph wrappers are normalized to text. Other nested rendered HTML is rejected; use a string child to display literal markup. As in any JavaScript template string, escape literal backticks and \${...} interpolation when needed.
Astro’s slot API returns rendered HTML, so the component decodes escaped text once before passing it to the shared highlighter. It does not evaluate the source text.
---
import Code from '@alkemdotdev/alkemist-components/code';
const source =
'function advance(position: number, velocity: number, dt: number) {\n return position + velocity * dt;\n}';
---
<Code
code={source}
lang="typescript"
title="integrator.ts"
highlightLines={[2]}
/>Code
Preview loads when it becomes visible.
Component parameters
Source code
---
import Code from "@alkemdotdev/alkemist-components/code";
---
<Code
lang={"typescript"}
title={"oscillator.ts"}
highlightLines={[]}
lineNumbers={true}
focusLines={[]}
addedLines={[]}
removedLines={[]}
collapsedRanges={[]}
annotations={[]}
highlightText={[]}
wrap={false}>{`
const damping = 0.25;
const frequency = 2;
const duration = 10;
const samples = 300;
const omegaD = frequency * Math.sqrt(1 - damping ** 2);
const displacement = (t: number) =>
Math.exp(-damping * frequency * t) * Math.cos(omegaD * t);
const points = Array.from({ length: samples }, (_, i) => {
const time = (i / (samples - 1)) * duration;
return { time, value: displacement(time) };
});
`}</Code>| Property | Default | Behavior |
|---|---|---|
code?: string | Or text child | The source text to display and copy. |
lang | text | A bundled Shiki language or plain text. |
title: string | Source | File name or descriptive caption. |
highlightLines: number[] | [] | One-based positive integer line numbers. |
lineNumbers: boolean | true | Show the numbered gutter. |
focusLines: number[] | [] | Keep these lines prominent and dim surrounding context. |
addedLines: number[] | [] | Mark additions with a plus and green background. |
removedLines: number[] | [] | Mark removals with a minus and red background. |
collapsedRanges | [] | { start, end, label? } ranges behind keyboard-accessible disclosures. |
annotations | [] | { line, text } notes attached to a source line. Text is escaped. |
highlightText: string[] | [] | Highlight exact strings, including across syntax tokens. |
wrap: boolean | false | Wrap long lines instead of horizontal scrolling. |
Choose Explain, Focus, or Changes in the playground to inspect each feature, then combine their parameters. All line references are one-based. Invalid values and overlapping folds produce an error instead of silently altering the listing.
<Code
code={source}
lang="typescript"
title="oscillator.ts"
collapsedRanges={[{ start: 1, end: 4, label: 'Oscillator settings' }]}
highlightLines={[6]}
highlightText={['omegaD']}
annotations={[
{ line: 6, text: 'Damping reduces the natural angular frequency.' },
]}
/>Syntax highlighting uses Shiki during the build. Authored listings need no browser highlighter; interactive playgrounds load it on demand to regenerate source. Components reject unknown languages. Astro’s fenced-code pipeline warns and falls back to plain text for an unknown fence language, so check the build output when adding a new grammar.
Copy uses the browser clipboard when permitted. If copying is blocked, it selects the source and announces how to copy it manually. Copy always includes the complete original source, even when sections are folded. Line numbers, annotations, change markers, and disclosure labels are excluded. Without JavaScript, the code remains readable and selectable and the inactive copy control stays hidden. Long lines scroll inside their panel.
See the test page for equations alongside interactive figures, and its code section for listings in context. The shared integration uses Astro’s Unified Markdown processor for Markdown and MDX.