Finally, a way to hold it all together.
The zero-dependency web component library for people who just need their HTML to act like a simple piece of paper.
<script src="https://cdn.jsdelivr.net/gh/rlnorthcutt/stapler@main/dist/stapler.min.js"></script>
The problem
Browsers weren't built for staples.
Web browsers were built for infinite scrolling. Business reports were built for staples. Between the two lies a graveyard of broken @media print rules and overlapping footers.
Every developer who has shipped a PDF on a deadline
The mechanism
Three reasons it actually works.
Hard-clipped precision
We don't guess. If it doesn't fit on the page, it's gone — cleanly, intentionally, with overflow: hidden instead of a prayer. Build documents like an engineer, not a typesetter.
AI-native markup
LLMs are exceptional at writing structured content. They are terrible at calculating millimeter margins. Stapler is designed so a model can emit valid, predictable markup without understanding CSS layout.
Zero drama
No NPM. No build step. No 2 GB node_modules folder having an existential crisis in your project root. One <script> tag. Six custom elements. Done.
The whole API
Six elements. That's the library.
No configuration object, no plugin registry. If you can read HTML, you already know how to use it.
<stapled-doc>
The root. Sets page dimensions, measures, builds. The stapler itself.
<s-page>
One page — a fixed-size, hard-clipped container.
<s-page-body>
The content area inside a page. Your padding goes here.
<page-header>
Declared once, stamped into every page. Skippable per page.
<page-footer>
Same deal, at the bottom of the sheet.
<page-number>
Resolves to 3, 3 / 8, or 3 of 8 when stamped.
5-second setup
From zero to page one.
Drop in the script tag and start writing pages. Each <s-page> is a hard-clipped container — the library handles headers, footers, and page numbers automatically.
<stapled-doc page-width="8.5in" page-height="11in" page-gap="2rem">
<page-header height="36px" skip-first>
My Report · <page-number format="n of total"></page-number>
</page-header>
<page-footer height="24px">
Confidential
</page-footer>
<s-page skip-header skip-footer>
<!-- Cover page — no header or footer -->
<s-page-body style="padding: 3rem;">
<h1>My Report</h1>
</s-page-body>
</s-page>
<s-page>
<!-- Page 2 — header and footer stamped automatically -->
<s-page-body style="padding: 2rem;">
<p>Content here.</p>
</s-page-body>
</s-page>
</stapled-doc>
Where it sits
Compared with the usual options.
Stapler isn't trying to replace a PDF pipeline. It removes the need for one when the document is already HTML.
| Approach | Runs where | Setup | What you give up |
|---|---|---|---|
| Stapler | Browser | One script tag | You decide what fits on each page |
| Server-side PDF (wkhtmltopdf, Puppeteer) | Node or a binary | Runtime, deploy, security review | Preview parity, iteration speed |
| JS layout engines (paged.js and friends) | Browser | Polyfilled layout, page-break CSS | Predictability on complex content |
Raw @media print |
Browser | Nothing to install | Consistency across browsers and OSes |
Who it's for
People with a deadline and a printer.
App developers shipping reports
Invoices, statements, briefs — generated from data your app already has, printed straight from the page the user is looking at.
Anyone piping an LLM into a document
The model writes the words, the library handles the pages. Paste the prompt, get valid markup back.
Teams tired of a PDF service
One file, no runtime, nothing to keep patched. Delete the microservice.
Ready to stop fighting the browser?
Drop it in. Write documents that respect the concept of a page.