CSS for print: page breaks, repeating table headers and @page margins
, 5 min read
Browsers print HTML with the same engine that draws it on screen, plus a handful of print-only CSS features. Knowing those few features is most of the difference between a PDF that looks designed and one that breaks a table in half. Everything here works in Chromium, which is what pagenull and Puppeteer use.
Page size and margins with @page
@page {
size: A4; /* or Letter, or 210mm 297mm, or A4 landscape */
margin: 20mm 16mm;
}
@page :first {
margin-top: 30mm; /* more room on the first page only */
}With pagenull, preferCssPageSize is on by default, so a size in @page wins over the format option. Set margins in one place, either @page or the API’s margin option, so you never have to work out which one applies.
Page breaks
.chapter { break-before: page; } /* always start on a new page */
.card, figure { break-inside: avoid; } /* never split across pages */
h2, h3 { break-after: avoid; } /* keep a heading with what follows */
p { orphans: 2; widows: 2; }The older names (page-break-before, page-break-inside) still work. break-inside: avoid is a request, not a guarantee: an element taller than a page has to break somewhere.
Tables that span pages
Put the header row in <thead> and it repeats at the top of every page; <tfoot> repeats at the bottom. Keep rows whole with break-inside: avoid on tr.
<table>
<thead><tr><th>Description</th><th>Qty</th><th>Amount</th></tr></thead>
<tbody>
<tr><td>API integration, October</td><td>12 h</td><td>€1,020.00</td></tr>
<!-- … hundreds of rows … -->
</tbody>
</table>pagenull injects a small print reset before your own CSS, so these are already on unless you set printReset: false: repeating thead/tfoot, no split rows, images and code blocks, headings kept with the next element, orphans and widows of 2, exact colours, images no wider than the page, and two helper classes, .page-break and .no-break. Your own rules override all of it.
Backgrounds and colours
Browsers drop backgrounds when printing unless told otherwise. print-color-adjust: exact on the element (part of the reset) and printBackground (on by default) keep table stripes, badges and brand colours.
Page numbers, headers and footers
For “Page 3 of 12”, set pageNumbers: true or a template such as "{page} / {pages}", and pageNumbersPosition. For a full header or footer, pass headerHtml and footerHtml; the details and the classes Chromium fills in are in headers, footers and page numbers.
Print styles vs screen styles
The renderer uses print media by default, so @media print rules apply. If your page only has screen styles and looks wrong in print, try emulateMedia: "screen" first.
Fonts
Load web fonts with <link> or @font-face; with JavaScript on, the renderer waits for fonts to load before printing. If a font fails, the response’s pagenull-warnings header names the problem, such as missing glyphs.
Checking a layout quickly
Paste HTML into the playground, or send "test": true renders while you work: they are watermarked, free and not counted.
curl https://api.pagenull.dev/v1/pdf -H "Authorization: Bearer $PAGENULL_API_KEY" -H "Content-Type: application/json" \
-o test.pdf -d '{"html":"<style>@page{size:A5;margin:12mm}</style><h1>Hello</h1>","test":true}'