v0.9.0 · pre-1.0 feedback phase
TypeScript everywhere, including your HTML.
The same modifier-chain DSL writes your landing pages, your docs, and your apps. No bundler. No JSX. No template language.
Three zeros, by design.
DraftOle outputs static HTML. Nothing leaks into the visitor browser, nothing balloons your node_modules, nothing forces a bundler step into your toolchain.
0
JavaScript in output
page() emits plain HTML and scoped CSS. Your LP ships zero framework runtime to visitors.
0
production deps
One entry in node_modules. No transitive web of packages to audit.
0
bundler config
Runs on tsx, ts-node, or node --experimental-strip-types. Build is one command.
What it is, in one paragraph.
A function-as-page DSL. You compose views (Section, VStack, HStack, Text, Heading) and apply modifiers (.padding(), .background(), .frame()) the way SwiftUI taught us to. Call doc.export() and you get index.html + style.css. That is the whole library.
— Type-safe end-to-end. HTML attribute typos are compile errors, not silent runtime bugs.
— SwiftUI-style modifier chains. Layout intent reads top-to-bottom.
— Scoped CSS per node, generated automatically. No global selector wars.
— ESM + CJS + .d.ts. Works with tsx, ts-node, and node --experimental-strip-types.
— Progressive: start static with page(), add interactive surfaces later with app().
Philosophy
Page first. App later.
Static pages are the cleanest place to enforce type safety end-to-end, so DraftOle starts there. When you need an interactive island — a counter, a form, a small client store — the same DSL extends through app(). You never have to leave TypeScript or change frameworks.
Try it. One file, sixty seconds.
Install with your favorite package manager. Then write a page() and run it.
$ pnpm add draft-ole
Read the Quick Start →