Page breaks
sghtmltopdf controls page breaks through the CSS Fragmentation properties.
Explicit page breaks
.chapter { break-before: page; } /* break before this element */
.summary { break-after: page; } /* break after this element */
.card { break-inside: avoid; } /* do not split this element across a page boundary */
| Property | Accepted values |
|---|---|
break-before, break-after | auto, avoid (avoid-page and avoid-column mean the same), always (page means the same) |
break-inside | auto, avoid (as above) |
The older page-break-before, page-break-after, and page-break-inside are accepted as aliases, so stylesheets written for wkhtmltopdf or wicked_pdf carry over unchanged.
The spread-related values left, right, recto, and verso, and the multi-column values, are not supported.
Without writing CSS
The same thing can be written as an HTML attribute.
<div data-page-break="before">…</div>
<div data-page-break="after">…</div>
<div data-page-break="avoid">…</div>
These count as low-priority hints, so a rule in a stylesheet can override any of them individually.
Keeping paragraphs from being split badly
p {
orphans: 3; /* leave at least three lines at the bottom of the page */
widows: 3; /* carry at least three lines to the top of the next page */
}
Both take an integer of 1 or more and default to 2. When the requirement cannot be met, the whole paragraph moves to the next page.
@page: paper and page margins
@page {
size: A4;
margin: 20mm;
@top-center { content: "Invoice"; }
@bottom-center { content: counter(page) " / " counter(pages); }
}
@page :first {
@bottom-center { content: "Cover"; }
}
sizeaccepts a page size keyword such asA4orLetter, one or two<length>values, andlandscapeorportrait@pagewins over the CLI page setup options, which act as initial values- Text can be placed in the margin boxes, the sixteen from
@top-left-cornerto@bottom-right-corner, throughcontent. Decoration such as background colours and borders is not supported there counter(page)is the current page number andcounter(pages)is the total page count
Limits of @page
sizeandmargincannot vary from page to page. Such declarations qualified with:first,:left, or:rightare parsed but not applied; those pseudo-classes only serve to vary the contents of the margin boxes- Named pages,
@page introtogether withpage: intro, are not supported - The margin box geometry is simplified. The four corners are fixed to where the vertical and horizontal margins meet, and the other twelve divide each edge into three equal parts; a
widthis ignored counter(pages)is unavailable in streaming mode, because the total page count is not known in a single pass
CLI options such as --header-center are mapped internally onto these @page margin boxes. If you write both, the CSS wins.
Breaking tables across pages
A table that does not fit on one page is split row by row and flows across pages.
- The rows of
<thead>are repeated at the top of every page after the first. Multi-row headers work too, and nothing is repeated for a table that fits on one page <tfoot>moves to the end of the table regardless of where it appears in the source, but it is not repeated at the bottom of every page; it appears once, on the last page- The
captiongoes with the first fragment forcaption-side: topand with the last forbottom - Every fragment carries the table’s own background and borders, and
border-collapse: collapsestill merges borders within each page
As a known limitation, a cell whose rowspan crosses a break belongs to the fragment of its starting row and its lower part runs off the page without being clipped. Row-level break-inside: avoid and the equivalent of orphans and widows are not supported either.
How Flexbox and Grid are handled
| Layout | Page breaks |
|---|---|
display: flex | Atomic. It is never split; if it does not fit, the whole thing moves to the next page |
display: grid | Split row band by row band, except at a boundary crossed by an item that spans rows |
display: table | Split row by row, as above |
When laying out large cards, a flex container can jump to the next page in one piece. Use Grid or a table where you want it split.
Common patterns
Keep a heading from being stranded at the bottom of a page:
h2, h3 {
break-after: avoid; /* do not break right after a heading */
break-inside: avoid;
}
Keep a single line item from splitting across two pages:
.line-item { break-inside: avoid; }
Always start a new page for each chapter:
section.chapter + section.chapter { break-before: page; }
Note
In streaming mode, selectors that look backwards, such as
:last-child, never match. The adjacent sibling combinator+used above does work.