Yet another markdown + React deck builder.
Why?
- My taste
- Good support for HTML presentation - keyboard control, slide persistence in URL, jump to slide, title
- Good support for PDF generation - configure
pagecss property properly
Skills for authoring decks with an AI agent are available in skills/deckx/SKILL.md, and can be installed into Claude Code, Codex, Cursor, etc. via skills.sh.
Then bootstrap a deck:
mkdir my-deck && cd my-deck
bunx skills add samuelcolvin/deckx
bun init -y
bun add @samuelcolvin/deckx
# author deckx.toml, deck.mdx, styles.css, components/
bunx deckx html # → dist/index.html
bunx deckx dev # live dev server at http://localhost:5173/npx / pnpm dlx work in place of bunx. The package is published as @samuelcolvin/deckx; the CLI binary is deckx, so bunx deckx ... works once the package is installed.
my-deck/
├── deckx.toml # title, tabs, paths (all optional)
├── deck.mdx # the slides
├── styles.css # theme tokens
└── components/ # optional custom React components
└── Hello.tsx
deckx.toml:
title = "My Deck"
theme = "light" # light | dark | markdown-light | markdown-dark (default: light)
tabs = [
{ id = "intro", label = "Intro" },
]The four built-in themes split on two axes: light vs dark backgrounds, and whether markdown-source decorations (# heading prefixes, ** strong markers, traffic-light dots, mono slide counter, diamond bullets) render on top. Pick light or dark for a clean baseline; pick a markdown-* variant for the opinionated annotated look.
deck.mdx:
import { Slide } from "deckx";
import Hello from "./components/Hello.tsx";
<Slide theme="title">
# Hello, world
</Slide>
<Slide tab="intro">
# Slide two
<Hello />
</Slide>styles.css overrides any of the CSS variables in deckx's base stylesheet:
:root {
--bg-slide: #092224;
--color-heading: #fbffea;
--accent: #e520e9;
}bunx deckx html [output]- build to<output>(default:./dist/index.html).bunx deckx pdf [output]- build HTML, then convert to<output>via Chrome (default:./dist/deck.pdf).bunx deckx html-to-pdf <input.html> <output.pdf>- convert an existing HTML file to PDF via Chrome, no rebuild.bunx deckx dev [dir]- Vite dev server with HMR. Positional is the build directory (default: cwd).bunx deckx skill- print the authoring guide (SKILL.md) to stdout.bunx deckx --help- CLI help.bunx deckx --version- version.
All commands accept --dir <dir> to point at a build directory other than the current one. Examples:
bunx deckx pdf my-deck.pdf # build current dir to my-deck.pdf
bunx deckx html out/index.html # custom HTML output path
bunx deckx pdf --dir ./decks/foo # build ./decks/foo to its default ./decks/foo/dist/deck.pdfA subcommand is required - running bunx deckx with no arguments prints help and exits with status 1.
bunx deckx pdfThis builds the HTML, prints the exact Chrome command it's about to run, then runs it. The output lands at ./dist/deck.pdf.
If Chrome isn't found, or the conversion fails, copy the printed command, fix the Chrome path or flags, and run it yourself. The default command looks like:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--headless=new --disable-gpu \
--no-margins --print-to-pdf-no-header \
--paper-width=11 --paper-height=6.1875 \
--print-to-pdf=./dist/deck.pdf "file://$PWD/dist/index.html"(Use google-chrome or chromium on Linux - deckx looks for them automatically.)
The full authoring guide - including the <Slide> prop reference, the CSS variable contract, and tips on translating a brand palette into a styles.css - lives at skills/deckx/SKILL.md.
bun install
bun run build # bundle CLI + library to dist/
bun run typecheck
bun run lintTo smoke-test against the bundled starter example:
cd examples/starter
bun install
bunx deckx html # writes dist/index.html