Bring your own design. This makes it editable.
A drop-in runtime that turns a static HTML slide deck into an editable one — and
a presentable one — in the browser, with nothing installed. Drag, resize and
rotate objects, group them, align and distribute them, type markdown and get
typography (**bold**, - for a list, `code`), edit text, link it,
manage pages, insert shapes, tables, charts and ten kinds of diagram, add
motion, build a slide that computes (drag a slider the deck declares, and the
margin table, the breakeven line and the chart all answer),
press F5 to present and S for a speaker view with your notes and a timer, and
export HTML, PDF or real PowerPoint. All of it from a file on a disk, with no
server: the deck stays a single self-contained file, so you send it and the
recipient gets the whole thing.
Embedded in someone else's viewer it stands down — chrome hidden, keys released — so it never fights its host. One attribute hands it back.
The runtime has no opinion about how a deck looks. It is the same code whether
the design is brutalist, pastel or terminal-green — what it needs is a
structure, not a style. That structure is written down in
CONTRACT.md, and it is short: slides are section.slide
directly under .slides-offset, and every editable thing is a .slide-object
with a unique data-oid and a data-object-type.
So a deck built to your company's design system, to a design.md, to a theme
from GitHub, or generated by an agent, is editable without borrowing anything
from here. Verified rather than claimed: a deck with zero CSS custom
properties, in a design unlike anything this repo ever shipped, passes the full
test suite unchanged — inserted tables, charts and shapes fall back to
currentColor and land looking native.
The fastest way to understand this is to read the deck that explains it, in the editor it is explaining. It is not shipped built — a deck carries its runtime inlined, so a committed one would go stale in place — so build it:
cd examples/why-and-how
python3 ../../scripts/make_deck.py --content content.md --design design.css --output why-and-how.html
python3 dress.py why-and-how.html # one of every object type: shapes, a table, a counting number
python3 shots.py why-and-how.html # screenshots the editor with the editor, embedded as data URIs
python3 diagrams.py why-and-how.html # a page per diagram archetype, laid out by the deck itself
open why-and-how.htmlTwenty-six pages: why the editor lives in the file, then how to drive it, then a specimen page for each of the ten diagrams. Press E to edit, drag something, F5 to present, Export ▸ PowerPoint to see what leaves.
Run the first command alone and you get the words without the pictures — which
reads like a manual that describes a toolbar without showing you one. shots.py
is what makes it legible, and it re-runs: change the chrome, re-run it, and the
manual catches up on its own.
Build a deck from your design and your content:
python3 scripts/make_deck.py --content outline.md --design brand.md --output deck.html--design takes a tokens .json, a .css with a :root block, or a
design.md that just names its colours and fonts in prose. It reads the names
real design systems use — --bg, --fg, --accent, --as, --t1, --line
— so a token file can be copied out of a codebase and used as it is, with no
renaming. Leave --design out and the deck still works: every token the
runtime reads has a fallback.
The design owns the proportions; --density is one multiplier.
python3 scripts/make_deck.py … --density balanced # or consultingpresentation (the default) is a deck read from the back of a room.
balanced and consulting scale the whole type system by 0.82 and 0.68 and add
a footer band — every design responds to the flag, and no design is overruled by
it. A design that states --title-size: 58px keeps the ratio it chose at every
density. Weights, leadings and tracking are tokens too (--title-weight,
--title-leading, --kicker-track), so a design states them rather than
inheriting a guess.
Port an existing deck instead:
python3 scripts/port_to_editable.py --source your-deck.html --output editable.htmlThen open editable.html. It opens as the editor — that is what the file is.
Changes save themselves as you make them; F5 presents and Esc puts you
back where you were.
A deck has two states, editing and presenting, and no third one to get lost in. What it does have is four ways out, all of them from the file itself:
| Export ▸ | |
|---|---|
| Copy of this deck | a working copy that opens as the editor |
| Reading copy | one that opens as a deck to read. A posture, not a lock: E opens the editor and Done returns |
| every slide at full size | |
| PowerPoint | a real .pptx — text becomes a text box, a shape becomes PowerPoint's own geometry, a table a table, a chart a chart with its data and your palette, an image a picture part. Offline, no library. Its 16:9 page is 13.333 × 7.5in, which at CSS's 96px/in is exactly this runtime's 1280 × 720 canvas, so nothing is estimated |
Check that what you built is a deck someone could be sent:
python3 scripts/validate_deck.py deck.htmlTwelve checks over the file rather than the behaviour — self-contained, contract shaped, no editor artefacts left inside a slide, a real stylesheet, svg references that resolve locally — plus two about composition, as warnings: every size comes from a token (a literal font size in the markup is a second type scale hiding in the deck) and every slide uses the space it has (a claim with four bullets under it leaves half a slide empty, and that emptiness is what generated decks look like). Covers and dividers are exempt and say so themselves.
Pick up a newer runtime in a deck you already have:
python3 scripts/refresh_runtime.py --file editable.htmlruntime/ |
The whole thing — runtime.js, chrome.html, chrome.css, viewport-base.css |
runtime/vendor/ |
What a deck carries besides the runtime: Moveable for drag/resize/rotate, deflated, and tiny-inflate to unpack it — about 99 KB in total, both MIT |
CONTRACT.md |
What a deck must provide. Read this before generating one |
examples/why-and-how/ |
The manual, as sources. Build it with the four commands above |
scripts/make_deck.py |
Build a conforming deck from a design source and a markdown outline |
scripts/port_to_editable.py |
Wrap an existing HTML deck to the contract |
scripts/refresh_runtime.py |
Re-inject the current runtime into a deck |
scripts/validate_deck.py |
Read a deck the way its recipient would |
scripts/extract_style_from_url.py |
Derive a palette, fonts and tokens from a live page |
tests/ |
44 suites against four unrelated designs. python3 tests/run.py |
Python 3 and Chrome. No npm, no build step, no dependency to install.
python3 tests/run.py drives real decks in a real browser: 44 suites × 4
designs, 3,608 assertions, ~5 minutes. Every fixture is also validated as it is
built, before a single suite runs.
The suites answer does the runtime behave. They cannot answer is the file it
just wrote a deck someone could be sent, and that gap is where the worst bug
this project has had lived for weeks: the PDF export deleted the deck's own
stylesheet, so every PDF went out black on black, with every assertion green. So
there are two kinds of check, and validate_deck.py reads the artefact.
The habit that came out of it, written here because it is the useful part: an
assertion that cannot fail is worse than no assertion. A control was in the DOM
and hidden by CSS — .click() fires on a display:none element just as happily,
so the suite passed while nobody could reach the button. Diagram nodes were
positioned, stamped, on the grid and invisible. Assertions here are checked in
both directions: red with the fix reverted, green with it in.
This repo used to vendor 43 preset designs. It no longer does — designs belong to their authors, and the runtime never needed them.
Most were ports of
zarazhangrui/beautiful-html-templates
(MIT). Go there for the originals: they are better maintained at the source,
and port_to_editable.py will wrap any of them.
ATTRIBUTIONS.md keeps the full record of what was here and where each design came from.
MIT — see LICENSE.