Articles

Every page has its place

How to decide on a page size, margins, running headers and page numbers for a printed template, and how to check them with real content.

By Palatine · Published · Updated

Start with the paper

Say you generate invoices from an HTML template. On screen the layout is fluid, but an invoice has to fit A4 or US Letter, and a table that runs off the edge looks broken once it is printed. So decide the page size first and state it in your CSS, instead of accepting whatever default you get.

Margins come next. Physical units such as millimetres or inches make your intended printed dimensions clear. If the document will be stapled or bound, give the inner edge more room than the outer one. Leave space for the header and footer as well, so they do not collide with the body text.

@page {
  size: A4;              /* or letter, or 210mm 297mm */
  margin: 25mm 20mm 30mm;
}

/* A bound report: wider inner margin, mirrored on facing pages */
@page :left  { margin-left: 20mm; margin-right: 28mm; }
@page :right { margin-left: 28mm; margin-right: 20mm; }

In Palatine, paged layout is an explicit option of the PDF export: pass options: { pagination: true } with a width and a height. Your @page size and margins then win over the size you passed. Without the option, the PDF is one continuous canvas, like the SVG and image exports, and everything in this article is ignored.

Running headers and footers

A running header repeats on every page: the company name, the report title, the current chapter. For a monthly report it tells someone holding a loose page where it came from. Keep it short. A title that fits on one line in your test data may wrap on the next customer's data, and a wrapped header pushes into the content.

CSS places headers and footers in sixteen margin boxes around the page area: @top-left, @top-center, @bottom-right and so on. Fixed text goes straight into content. Text that changes with the page, such as the current chapter title, is copied from the document with a named string:

h1 { string-set: chapter content(text); }

@page {
  @top-left    { content: "Annual report 2026"; }
  @top-right   { content: string(chapter); }
  @bottom-center {
    content: "Page " counter(page) " of " counter(pages);
    font-size: 9pt;
  }
}

string(chapter) shows the first chapter title set on the page, or the one carried over from the previous page; the keywords start, last and first-except change which value wins. When a header needs more than text, a logo next to the title for instance, mark an element with position: running(header) and place it with content: element(header): it keeps its own styles and links.

Decide what changes between pages. A fixed footer is easy. A header that shows the current chapter takes more care, so test it on a chapter that starts mid-page and on one that spans several pages. Also check the first page, since many documents want no header there at all.

First pages, blank pages and named pages

Page selectors let you style pages by position. :first is the first page of the document, :left and :right alternate for facing pages, and :blank matches the empty pages that Palatine inserts when a chapter must start on a right-hand page.

@page :first {
  @top-left { content: none; }
  @top-right { content: none; }
}

/* Every chapter opens on a right-hand page */
h1 { break-before: right; }
@page :blank {
  @top-right { content: none; }
  @bottom-center { content: none; }
}

/* A wide appendix table on landscape pages */
@page wide { size: A4 landscape; }
.appendix { page: wide; }

Named pages are the clean way to mix orientations: the element with page: wide starts on a new page of that type, and the next content returns to the default page. Use them for a landscape table, a full-bleed cover or an appendix with different margins.

Page numbers

Page numbers look trivial until the document gets a cover. Decide whether the cover counts, and whether the front matter should use a different numbering style from the body. The page counter is set and incremented in the page context, so a cover that should not count looks like this:

@page :first {
  counter-set: page 0;               /* cover is page 0, next page is 1 */
  @bottom-center { content: none; }
}

@page front {
  @bottom-center { content: counter(page, lower-roman); }
}
.preface { page: front; }

One limit to plan around: counter-reset: page on an element is not supported, so you cannot restart the arabic numbering where the first chapter begins. If your house style needs that, render front matter and body as two PDFs, or let the roman pages share the sequence. counter(pages) counts every physical page, blank ones included.

Breaks that read well

Most bad pages come from a break in the wrong place: a heading alone at the bottom, a table header without rows, a figure cut from its caption. A few rules prevent most of them.

h2, h3 { break-after: avoid; }
figure, .totals { break-inside: avoid; }
p { widows: 2; orphans: 2; }

Long tables break between rows, and the first thead and tfoot repeat on every page the table continues on, as long as each fits in a quarter of the page. A row taller than a page is split inside its cells rather than lost. avoid is a request, not a guarantee: a block taller than the page still has to be split somewhere.

What to test

Before you rely on a template, render a document that is actually long, not the three-page sample you built the template with. Run it with real content:

  • the longest customer name and the longest chapter title you can find, to see the header wrap;
  • a table that crosses a page break, to see the repeated header;
  • a heading that lands at the bottom of a page, and a chapter that ends on a right-hand page;
  • the first page, a blank page and the last page, which each have their own rules.

Read the warnings too. When Palatine cannot paginate something as asked, it says so with a code such as pagination.monolithic-overflow rather than dropping content silently. Then open the PDF and look at it. Paged output and screen output are different jobs, and the problems hide in the details.

Explore the rendering documentation


All articles