Skip to content

Components

mdxComponents (from @liebstoeckel/components, wired by the engine’s MDXProvider) maps Markdown to themed elements: h1 (display serif), h2 (text-primary), p (font-body text-muted), ul/li (accent bullets), strong (text-accent), inline code (themed pill), and fenced code (Shiki, brand-bound). Just write Markdown. No imports needed for these.

From @liebstoeckel/engine (see Engine API):

  • <Step>: a reveal.
  • <CodeMagic steps title? lang?>: animated code.
  • <Magic id className?>: Magic Move, morph different stateless elements (same id) between slides.
  • <Slot id className?>: place the same stateful element (defined via persistent) on a slide; it travels/snaps/fades across slides.
  • <Plugin id props? components?>: place a live plugin.

@liebstoeckel/components ships ready-made layouts for the common slide shapes, so a deck (or an agent writing one) can compose a full presentation without hand-building each slide. All of them animate in with the house motion style, scale their type down as content volume grows, and reflow (e.g. into two columns) before content would overflow the 1280x720 stage.

ComponentPropsUse
TitleSlidetitle, kicker?, subtitle?, byline?, date?opening slide
SectionSlidetitle, index?, subtitle?numbered section divider
BulletsSlidetitle, bullets, kicker?staggered list; bullets are strings or { text, detail? }; heavy lists flow into two columns
TwoColumnSlidetitle, left, right, kicker?side-by-side Columns (heading?, text?, bullets?, children?)
QuoteSlidequote, attribution?, role?full-stage pull quote
BigStatSlidestats, title?, kicker?, aside?headline numbers; each Stat (value, label, prefix?, suffix?, decimals?, note?) counts up on entry
ChartSlidetitle, children, kicker?, insight?, aside?frame for a chart (e.g. a registry chart) with an insight line
ClosingSlidetitle, subtitle?, contact?, url?final slide

The building blocks are exported too, for custom slides that should match: Kicker (uppercase eyebrow label), SlideHeading (animated heading; size it with headingSize(text, "hero" | "large" | "medium"), which steps the type down as the text gets longer), Caption, AnimatedNumber (value, prefix?, suffix?, decimals?), and layoutEase (the shared easing curve).

import { BulletsSlide } from "@liebstoeckel/components";
export default function Agenda() {
return (
<BulletsSlide
kicker="Agenda"
title="Where we are going"
bullets={[
{ text: "The problem", detail: "why decks rot in binary formats" },
"One self-contained .html",
"Live sessions and plugins",
]}
/>
);
}

From @liebstoeckel/plugin-ui, used inside plugin client surfaces. They style against var(--brand-*) so they’re automatically on-brand.

ComponentPropsUse
Cardstyle?, className?translucent surface container
ButtononClick?, active?, disabled?, style?full-width option/action button
Barpct, color?, label, value?, highlight?animatable result bar (0 to 100)
Stackgap?, style?vertical flex stack
ScrollAreamaxHeight?, style?, className?bounded, internally-scrolling region. Cap unbounded plugin content so it doesn’t overflow the fixed slide canvas
Eyebrow(none)uppercase mono label

Also exported: useTheme() / readTheme() → ThemeTokens (resolved brand colors/fonts/viz for canvas or SVG use).

import { Card, Eyebrow, Bar, Button, Stack } from "@liebstoeckel/plugin-ui";
<Card style={{ width: "100%", maxWidth: 460 }}>
<Eyebrow>Live poll · 12 votes</Eyebrow>
<Stack>
<Bar pct={75} label="Yes" value="9 · 75%" highlight />
<Bar pct={25} label="No" value="3 · 25%" />
</Stack>
<Button active>Vote</Button>
</Card>

Comments

liebstoeckel

Code-first presentations your agent can author. One file out, no server.

© 2026 Leon Kaucher