htmlasitis
v0.1.0
Published
A4 print-safe CSS library for HTML books. One .hai-page section becomes exactly one printed A4 page.
Maintainers
Readme
htmlasitis
A4 print-safe CSS library for HTML books.
Guarantee: one .hai-page section becomes exactly one printed A4 page.
No overflow. No empty pages. No widow page numbers.
Tested against the Chromium print engine (Chrome, Edge, Brave, Opera).
Why this library exists
Browsers were not built for books. They were built for documents that scroll forever. When you try to print HTML to A4 sheets, the browser makes its own decisions about how to break content across pages, and those decisions are usually wrong:
- Headings get orphaned at the bottom of pages
- Tables split across page boundaries
- Page numbers jump to the next page alone
- Content overflows silently and creates extra pages
This library enforces a simple rule that solves all of these problems:
each .hai-page section is exactly one printed page. If your
content does not fit, it gets clipped, and you find out immediately
when you run the validation script.
This trades flexibility for predictability. That is the right trade for books.
Quick start
# 1. Set up the environment (run once)
./run/init.sh
# 2. Edit example.html or copy template.html to start a new book
# 3. Preview in a browser
./run/dev.sh
# Open http://localhost:8000/example.html
# 4. Validate that page counts match
./run/test.sh example.htmlIf the test passes, your book prints exactly. If it fails, the script tells you how many extra pages were created and where to look.
How to use the library
Minimum HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="htmlasitis.css">
</head>
<body>
<section class="hai-page">
<h1>Your Chapter Title</h1>
<p>Your content...</p>
<div class="hai-page-number">— 1 —</div>
</section>
</body>
</html>That is it. Wrap each printed page in <section class="hai-page">.
The library handles the rest.
Components
All components are documented with a content budget — roughly how much text fits when you use that component on a page.
.hai-page (required)
The fundamental unit. Each one is exactly 210mm × 297mm with 18mm horizontal and 20mm vertical padding. Content area: 174mm × 257mm.
Content budget: about 1800 characters of body text alone, less when you add other components.
.hai-cover
Combine with .hai-page to make a book cover. Centers content
vertically with flexbox.
<section class="hai-page hai-cover">
<div class="hai-cover-volume">Volume 1</div>
<h1>Book Title</h1>
<div class="hai-cover-subtitle">Subtitle here</div>
<div class="hai-cover-author">Author · Year</div>
</section>.hai-divider
A part divider page. Like cover, but for sections within a book.
<section class="hai-page hai-divider">
<h1>Part Two</h1>
<div class="hai-divider-subtitle">The subtitle of this part</div>
</section>.hai-page-number
A page number pinned to the bottom of the page. Uses absolute positioning, so it always sits 12mm from the bottom regardless of content above it.
<div class="hai-page-number">— 42 —</div>.hai-callout
A bordered box for emphasizing one key insight per page.
<div class="hai-callout">
The most useful constraint is the one that tells you when you
have written too much.
</div>Content budget cost: about 200 characters less of body text.
.hai-example, .hai-example-good, .hai-example-bad
Boxes for showing examples, optionally framed as good or bad.
<div class="hai-example hai-example-bad">
<div class="hai-example-label">BEFORE</div>
An example of what not to do.
</div>
<div class="hai-example hai-example-good">
<div class="hai-example-label">AFTER</div>
An example of what to do instead.
</div>Content budget cost: about 150 characters per example.
.hai-annotation
A side note. Like a callout but with a left border instead of a full border. Less prominent.
<div class="hai-annotation">
<div class="hai-annotation-label">NOTE</div>
An aside that supplements the main text.
</div>Content guidelines
The library enforces page boundaries. It cannot enforce content density. That is your job. Here are the budgets:
| Page composition | Body text budget | | --- | --- | | H1 + body text only | ~1500 characters | | H1 + H2 (×2) + body text | ~1300 characters | | H1 + 1 callout + body text | ~1300 characters | | H1 + 1 small table + body text | ~1000 characters | | H1 + 1 code block + body text | ~1100 characters | | H1 + 2 example boxes + body text | ~1200 characters | | Pure body text (no H1) | ~1800 characters |
These are at the default font size (11pt) and line height (1.55). If you change those, the budgets change.
How to write to budget:
- Draft your chapter in plain text first
- Count characters:
wc -m chapter.txt(or use word count in your editor) - Divide by your budget to estimate page count
- Split your text into sections of roughly that size
- Each section becomes one
.hai-page
When in doubt, run the test. If the validation passes, you are within budget.
How to write a book that prints cleanly
These are practical rules that emerge from working within the library's constraints:
1. One topic per page
A .hai-page is a unit of attention. Treat it as one idea, one
argument, one example. If you find yourself wanting to put two
unrelated topics on one page, split them.
2. End on a complete thought
Because pages cannot overflow, your last paragraph must finish within the page. Plan your final sentence as you write — do not let it become a half-thought because you ran out of space.
3. Use page numbers consistently
Every content page should have a .hai-page-number. Cover and
divider pages do not. Number from page 2 onward (the page after
the cover).
4. Test early, test often
Run ./run/test.sh whenever you finish a draft of a chapter. The
test runs in seconds and catches overflow problems before they
compound.
5. Treat overflow as a writing prompt
When the validation fails, do not just delete words to fit. Ask what the overflow is telling you about the chapter. Often it means you have two ideas crammed into one page, and they want to be two pages.
6. Keep typography stable
Avoid changing font sizes mid-book. Each typography change shifts your content budget for that page. The CSS variables let you theme the whole book once at the top — use that, do not override in individual pages.
Theming with CSS variables
All visual properties are CSS variables defined in :root. Override
them at the top of your HTML to theme your whole book at once:
<style>
:root {
--hai-font-body: "Your Font", sans-serif;
--hai-font-mono: "Your Mono Font", monospace;
--hai-font-size-body: 12pt;
--hai-line-height-body: 1.6;
}
</style>See htmlasitis.css for the full list of variables.
Note that changing font size or line height changes your content budget per page. Re-run validation after any theming change.
How the library works under the hood
A short tour of the engineering:
@page { margin: 0 }
Browsers add their own margins to printed pages by default
(about 9.5mm in Chromium). We disable that and use .hai-page
padding to create margins. This gives us per-page control and
defeats browser defaults that would shift our layout.
Fixed .hai-page dimensions
Each page is exactly 210mm × 297mm with padding: 20mm 18mm.
This is not responsive — it is intentionally rigid. The
content area is precisely 174mm × 257mm, every time.
overflow: hidden on .hai-page
This is the core trick. It clips content that exceeds the page. Without this, the browser would helpfully flow content to a new page and create an empty page after each overflow. With this, overflow becomes visible (clipped content) and detectable (failed test).
break-after: page for page boundaries
Each .hai-page ends with a forced page break. The legacy
page-break-after: always is included alongside for compatibility.
print-color-adjust: exact
By default, browsers do not print background colors to save ink. We force exact color rendering for callouts, code blocks, and table headers, so the printed book looks like the screen book. Users no longer need to enable "Background graphics" in the print dialog.
Heading cohesion via break-after: avoid
Headings cannot end up alone at the bottom of a page. The
browser will pull them onto the next page with their following
content if needed. (This rarely matters within a .hai-page
because it cannot break, but it matters if you nest content
in unusual ways.)
Validation script details
./run/test.sh does three things:
- Renders the HTML file to PDF using headless Chromium
- Counts
<section class="hai-page">in the source - Compares to the page count of the generated PDF
If the counts match, your book prints predictably. If they do not, some content overflowed and created extra pages.
The script accepts any HTML file as an argument:
./run/test.sh my-book.html
./run/test.sh chapters/chapter-3.htmlBrowser support
| Browser | Engine | Status |
| --- | --- | --- |
| Chrome | Chromium | Tested, works |
| Edge | Chromium | Tested, works |
| Brave | Chromium | Tested, works |
| Opera | Chromium | Should work |
| Firefox | Gecko | Most features work, some edge cases differ |
| Safari | WebKit | print-color-adjust works, otherwise less tested |
The library is primarily targeted at Chromium because of its strong print CSS support and predictable behavior. Validation requires Playwright with Chromium.
Browser print dialog settings
The library is designed to print correctly with default print dialog settings:
- Paper size: A4 (matches CSS
@page size: A4) - Margins: any setting works, because we use
@page margin: 0and create margins via.hai-pagepadding - Background graphics: any setting works, because we force
background printing with
print-color-adjust: exact - Headers and footers: should be off — turn this off in the print dialog so the browser does not add its own page numbers on top of yours
The only setting that matters: turn Headers and footers off.
Files in this library
htmlasitis/
├── README.md This file
├── htmlasitis.css The CSS library
├── template.html Minimal starting point for new books
├── example.html Demonstrates all components
├── overflow-test.html Verifies clipping behavior
└── run/
├── init.sh Install Playwright + Chromium
├── test.sh Validate page counts
└── dev.sh Run local server for previewLicense
Use it however you want. Ship books.
