What actually survives HTML to PDF, measured
Twelve print-CSS features rendered through headless Chromium and then read back out of the PDF — page breaks, @page sizes, repeating table headers, links and fonts. Two of them do not work, and one common gotcha is the API's fault, not the CSS.
What’s new here: A per-feature support matrix for HTML-to-PDF print CSS, measured on Chromium 148 by rendering each case and inspecting the resulting PDF's page count, page dimensions, link annotations and per-page text — not by reading documentation. Every verdict is reproducible from the published harness and dataset, and two widely-recommended techniques are shown not to work.
Every HTML-to-PDF service says its output is faithful. None of them publish what that means. So we measured it: twelve print-CSS features, each rendered through headless Chromium — the engine behind MostlyRender and behind most HTML-to-PDF services — and then read back out of the resulting PDF.
Nothing here is quoted from documentation. Each verdict comes from inspecting the file: how many pages it has, what size they are, which text landed on which page, whether a link annotation exists, whether a font was embedded.
# The matrix
Measured on Chromium 148.0.7778.96, first on 1 September 2026 and again on 27 September 2026. Both runs produced the same twelve verdicts.
| Feature | Works | What we observed |
|---|---|---|
break-after: page |
Yes | 2 pages, as asked |
page-break-after: always (legacy) |
Yes | 2 pages — the old spelling is still honoured |
break-inside: avoid |
Yes | The block moved whole to page 2 instead of splitting |
@page { size: A5 } |
Yes | 420×595pt — exactly A5 |
@page { size: A4 landscape } |
Yes | 842×595pt — landscape |
Repeating <thead> across pages |
Yes | 3 pages, header present on page 2 |
position: fixed as a running header |
Yes | Present on 2 of 2 pages |
@page margin boxes + counter(page) |
No | No page number anywhere in the output |
<a href> as a clickable link |
Yes | Link annotation present |
Internal #anchor links |
Yes | Destination present |
| Font embedding | Yes | DejaVuSansMono embedded and subset |
| CSS grid across a page break | No | All 8 cells on page 1; page 2 empty |
Ten of twelve work. The two that do not are worth knowing about before you design around them.
# The two that fail
CSS Paged Media margin boxes do not render. The spec gives you
@page { @bottom-center { content: "Page " counter(page) } }, it is in every tutorial about
printing from the web, and Chromium ignores it. We found no page number in the output at all.
If you need page numbers, they have to be added by whatever produces the PDF — which is why
tools that offer page numbering do it as a feature rather than telling you to write CSS.
CSS grid does not fragment. We laid out eight items in a two-column grid tall enough to need two pages. All eight rendered on page one, and page two came out blank. Grid is fine for content that fits on a single page; for anything that paginates, use block flow or a table.
# The gotcha that is not CSS at all
While building the harness we recorded @page { size: A5 } as unsupported — and it was our
own mistake. The renderer was being told to use A4 by the API call, and a paper format
passed by the caller silently overrides the document’s @page size. With the format omitted
and CSS page size preferred, A5 and landscape both came out exactly right.
This is worth stating plainly because it is almost certainly the most common reason people
conclude @page is broken: the CSS is fine, and something upstream is quietly outranking it.
We only caught it because a result that disagreed with the spec made us re-read the harness
rather than publish the number.
# Reproducing this
The harness and the dataset behind the table are both published:
- bench-htmltopdf.js launches Chromium, renders each case, and inspects the output with pdf-lib and pdf.js.
- html-to-pdf.json is the output we wrote this table from, stamped with the Chromium version it measured.
To run it yourself, save the harness into an empty folder and run:
npm i playwright pdf-lib pdfjs-dist@3
npx playwright install chromium
node bench-htmltopdf.js
One limit worth stating: font detection scans the raw PDF bytes for font names, so a positive result is solid but an empty one only means “not visible to this method”. The harness reports that case as inconclusive rather than as a failure, because publishing “fonts are not embedded” on the strength of a scan that cannot see them would be exactly the kind of claim this article exists to avoid.
# Why we measured it
We build an HTML to PDF tool and API on this engine. Publishing what it does and does not do is more useful than asserting that it is faithful — and if a number here is wrong, the harness that produced it is linked above to be run.
Frequently asked questions
Does page-break-after still work, or do I need break-after?
Both work. We measured the modern break-after: page and the legacy page-break-after: always separately and each produced the expected two-page document on Chromium 148. If you have older templates emitting the legacy spelling, they are not the reason your pagination is wrong.
Why is my @page size being ignored?
Almost always because the caller overrode it. If the API or library that drives the render passes a paper format of its own, that wins and your @page { size: … } is discarded silently. With the format omitted and CSS page size preferred, A5 and landscape both came out exactly as asked. This is the single most common false report of "@page size does not work" — it caught us while building the harness for this article.
Can I put page numbers in the margin with CSS?
Not in Chromium. @page { @bottom-center { content: counter(page) } } is part of CSS Paged Media and is widely documented, but the margin box simply does not render — we measured it and found no page number anywhere in the output. Page numbers have to come from the tool doing the conversion, not from your stylesheet.
Do CSS grid layouts break across pages properly?
No. In our test, all eight grid items rendered on page one and the second page came out empty — the grid did not fragment. If your document is long enough to paginate, lay the repeating part out with normal block flow or a table and keep grid for things that fit on one page.
Are hyperlinks preserved in the PDF?
Yes. Both an external <a href> and an internal #anchor produced real link annotations in the output file, so links stay clickable and a generated table of contents stays navigable.