@exor404/mdslides
v0.1.3
Published
Markdown in, presentation out.
Maintainers
Readme
mdslides
Markdown in, presentation out.
The slides sibling of mdstack. Point it
at a folder — mdslides scaffolds a starter slides.md, then presents it in a
dev server or builds a self-contained dist/ you can host anywhere static.
Status: pre-1.0, provisional. CLI flags and markdown conventions may change before
0.x → 1.0.
Quick start
npx @exor404/mdslides@latestNo install step. It asks for a name, scaffolds <name>/<name>.md (three
sample slides — a title page, a list, and a help slide) plus a
mdslides.config.js, and opens the dev server. The name defaults to the current
folder, so pressing Enter at Name your presentation (chemistry): gives you
chemistry/chemistry.md. That file is your deck — edit it and the slides
live-reload.
Point it at a folder for the other commands:
npx @exor404/mdslides ./chemistry # present (dev server, live-reload)
npx @exor404/mdslides build ./chemistry # static build → ./chemistry/dist/
npx @exor404/mdslides preview ./chemistry # serve the built dist/
npx @exor404/mdslides export ./chemistry # → chemistry.pdf (one page per slide)Already have a deck? Point straight at the file: npx @exor404/mdslides ./chemistry.md.
Heads-up:
npm install @exor404/mdslidesonly installs the package — running the CLI (vianpx, as above) is what scaffolds and presents. Usingnpxalso keeps the engine's dependencies in npx's cache instead of bloating a project'snode_modules.
Prefer a global command? npm i -g @exor404/mdslides makes mdslides available
directly (mdslides ./chemistry).
Try the bundled example from a clone:
npm install
npm run dev # opens ./exampleInstall
The package is published on the public npm registry as
@exor404/mdslides. Pick the
option that suits you:
No install (recommended). npx fetches and caches the latest version, then
runs it — nothing is added to your project:
npx @exor404/mdslides@latest # prompts for a name, scaffolds + presentsGlobal install. Puts an mdslides command on your PATH:
npm install -g @exor404/mdslides
mdslides ./chemistry # then use it anywhere
mdslides --help # (or run with no folder to start fresh)Update later with npm update -g @exor404/mdslides; remove with
npm uninstall -g @exor404/mdslides.
As a project dependency. Add it to a repo so collaborators get the same
version via npm install:
npm install -D @exor404/mdslides
npx mdslides ./chemistry # resolves from local node_modulesRequires Node 18+. Installing the package pulls in its rendering engine (Astro, Shiki, KaTeX) — that's why
npxis the lightest way to try it: those dependencies stay in npx's cache instead of your project'snode_modules.
The deck model
One markdown file is the whole deck. Every # (H1) heading starts a new
slide — write plain markdown in between. A standalone --- line is an
explicit break (for a slide with no heading, or to split one section).
# Presentation title
## A subtitle
**Your Name** · {{date}}
# Second slide
- a point
- another (bullets reveal one at a time)Title slide
The first slide is the title slide automatically: the # renders large,
## is the subtitle, and the last line becomes a byline pinned to the bottom —
write your presenter name(s) in markdown there. The {{date}} token resolves to
today's date. Opt a slide out with <!-- slide: plain -->, or mark another
slide as a title with <!-- slide: title -->.
Supported markdown (the lean subset)
Only what slides actually need:
- Headings, paragraphs, bold, italic, ~~strike~~, ==highlight==
- Lists (reveal one item at a time — see Reveal & motion), tables, blockquotes
inline codeand fenced code blocks (syntax-highlighted via Shiki)- Math —
$inline$and$$block$$via KaTeX - Images (relative paths resolve from the deck folder)
Fence a block with a language for syntax highlighting:
```js
export default { theme: 'angular' };
```Presenting
| Key | Action |
| --- | --- |
| → space PageDn | next slide / reveal next fragment |
| ← PageUp | previous |
| Home / End | first / last slide |
| O | overview grid (click a slide to jump) |
| F | fullscreen |
| . | black screen |
| ? | help |
The URL hash tracks the current slide (#/3) so deep links work.
Reveal & motion
Slides build themselves as you talk:
- Content fades in when a slide opens — headings, paragraphs, images, code, tables and quotes rise into place with a light stagger.
- Lists reveal step by step. Every list item is hidden when the slide opens
and appears one at a time on
→/space;←steps back. Once all items are shown, the next press advances to the following slide. This is automatic — you don't need to mark anything. - Manual fragments. Append
{.fragment}to any other line (a paragraph, a heading) to hold it back and reveal it on a later press, in document order alongside the list items.
In the overview grid and PDF export, everything is shown at once. Motion respects the OS "reduce motion" setting.
Per-slide directives
A leading comment configures one slide:
# A slide
<!-- slide: center bg=#1a0b2e -->center— vertically + horizontally centeredbg=<color|url(...)>— slide background
Configuration
First run drops a mdslides.config.js next to your deck:
export default {
brand: { text: 'mdslides' }, // bottom-left wordmark; '' to hide
theme: 'angular', // built-in: 'angular'
transition: 'fade', // 'fade' | 'slide' | 'none'
};PDF export
mdslides export renders the /print route (one slide per page, all
fragments shown). It uses Playwright if installed
(npm i -D playwright && npx playwright install chromium); otherwise open
/print in a browser and use Save as PDF.
Releasing
Releases go to the public npm registry (npmjs.com), so anyone can
npx @exor404/mdslides. A tag-triggered GitHub Actions pipeline
(.github/workflows/package-publish.yml)
does the publishing for you: it runs only when you push a v* tag, verifies
the tag matches package.json, and runs npm publish against npmjs.com using an
automation token stored as the NPM_TOKEN repository secret.
How the pipeline works
push tag v0.1.3 ──► GitHub Actions ──► checkout ──► setup Node 22
│
├─ guard: tag (v0.1.3) must equal
│ package.json "version" (0.1.3),
│ else the run fails
│
└─ npm publish (auth: NPM_TOKEN)
└──► npmjs.comThe version guard means the git tag and the published version can never drift — if they disagree, the run stops before publishing.
One-time setup
You only do this once. It wires an npm token into the repo so Actions can publish on your behalf.
- Create an npm automation token. On npmjs.com: avatar → Access Tokens →
Generate New Token → Automation (or a Granular token scoped to the
@exor404/mdslidespackage with Read and write). Copy the value — npm shows it only once. - Add it to GitHub as a secret. Repo → Settings → Secrets and
variables → Actions → New repository secret. Name it exactly
NPM_TOKEN, paste the token, Add secret. (It's a secret, not a variable — secrets are encrypted and hidden from logs.)
The very first version must exist on npm before automation can update it. If the package isn't published yet, do one manual publish first:
npm login && npm publish --access public.
Cutting a release
Once the secret is in place, every release is three commands:
npm version patch # bumps package.json (0.1.2 → 0.1.3) and commits + tags v0.1.3
git push origin main # push the version-bump commit
git push origin v0.1.3 # push the tag → the pipeline publishesnpm version patch (or minor / major) bumps package.json, makes the commit,
and creates the matching vX.Y.Z tag in one step. Pushing that tag is what
triggers the workflow. Watch it run under the repo's Actions tab; when it goes
green the new version is live on npm.
Prefer to bump by hand? Edit "version" in package.json, then:
git commit -am "🔖 Release 0.1.3"
git tag v0.1.3 # must match package.json exactly, or the guard fails
git push && git push --tagsRequirements
Node 18+.
License
MIT
