Using @page Rules in Practice: Page Breaks, Margins and Print Formats
AI generated
{ }
@
CSS · @page · Pagination · PDF Layout
Using @page Rules in Practice
Page sizes, margin boxes and numbering without JavaScript

With @page rules, CSS directly controls how large a printed page is, what margins it has, and what appears in the header and footer. For invoices, contracts and multi page reports, @page rules replace a significant part of what would otherwise require an external PDF library.

16 min read @page · size · margin boxes · counter(page) Chrome · Firefox · Safari

1. What @page rules solve that media print cannot

An @media print block controls how individual elements look, but not how the page itself is defined as a physical paper format. This is exactly where @page rules come in: they determine how large a printed page is, how wide its margins are, and what appears in the margin areas, independent of the document's content. Without @page rules, page size and margins are left entirely to the printer driver's default settings, which leads to inconsistent results for formal documents like contracts.

For invoices, quotes and multi page technical reports, @page rules are therefore not a nice extra but often the only way to achieve a consistent layout without a server side PDF library. This article goes deep into the syntax of @page rules, from the base structure through named pages and margin boxes to automatic page numbering with CSS counters.

2. Base syntax: @page, size and margin

The simplest form of an @page rule defines a fixed page size and uniform margins for the entire document. The size property accepts either named formats such as A4 and letter, explicit dimensions such as 210mm 297mm, or an orientation keyword such as landscape combined with the format. The margin property works analogous to box model margin, but applies to the entire printable area of the page rather than a single HTML element.

An important detail to understand: @page rules sit outside the normal DOM tree, they do not reference a single HTML element but the page boxes generated by the user agent itself. That is why normal CSS selectors such as classes or IDs do not work inside @page, instead there are dedicated pseudo classes and the special margin box syntax explained later in this article.


/* Base @page rule: A4 with generous margins for print binding */
@page {
  size: A4;
  margin: 25mm 20mm 25mm 20mm; /* top right bottom left */
}

/* Landscape variant for wide data tables */
@page wide-report {
  size: A4 landscape;
  margin: 15mm;
}

/* Apply the named page to a specific section via HTML */
.data-table-section {
  page: wide-report;
}

3. Named pages for different document sections

A single document rarely needs the same page format everywhere. A cover sheet often has no margins at all, the main body has standard margins, and an appendix with wide tables benefits from landscape orientation. This is exactly what @page rules provide named pages for: with @page reportcover { ... } a dedicated page definition is created, which gets assigned to any HTML element through the CSS property page: reportcover;. As soon as the renderer reaches that element, it automatically breaks to a new page using the named page's properties.

This feature of @page rules is especially valuable for automatically generated reports, where cover sheet, table of contents and appendix each need a different format but are rendered from the same HTML source. Without named pages, you would either need to generate multiple separate documents or fall back to a dedicated PDF library with its own page management.


/* Named page definitions for different document sections */
@page {
  size: A4;
  margin: 20mm;
}

@page cover {
  size: A4;
  margin: 0; /* full bleed cover, no margin */
}

@page appendix {
  size: A4 landscape;
  margin: 15mm 10mm;
}

/* Assign named pages to structural elements */
.cover-page   { page: cover; break-after: page; }
.report-body  { /* uses the default @page */ }
.appendix     { page: appendix; break-before: page; }

4. Pseudo classes: first, left and right

@page rules support three specific pseudo classes tied to a page's position within the document. @page :first applies exclusively to the first page and allows, for example, a larger logo in the header area or completely omitting the page number on the cover sheet. @page :left and @page :right distinguish between left and right pages in a double sided printed document, which matters for book layouts, where the inner binding margin needs to be wider than the outer margin.

Combining named pages with these pseudo classes allows fine grained @page rules such as @page report:first, which only apply to the first page of a specific section. Browser support for :left and :right is solid in Chromium based browsers through the print to PDF renderer, but patchier in regular Firefox and Safari print dialogs, which makes testing in the target environment mandatory before production use.


/* First page: larger top margin for a title block, no page number */
@page :first {
  margin-top: 60mm;
}

/* Left/right pages for double-sided book-style printing */
@page :left {
  margin-left: 30mm;  /* wider inner margin for binding */
  margin-right: 15mm;
}
@page :right {
  margin-left: 15mm;
  margin-right: 30mm; /* wider inner margin for binding */
}

5. Margin boxes: headers and footers with @top-center

The most powerful part of the @page rules specification is the set of so called margin boxes. Inside an @page declaration, up to 16 named boxes can be defined, including @top-left, @top-center, @top-right, @bottom-left, @bottom-center and @bottom-right. Each of these boxes can be filled with content, either static text or CSS counters for dynamic page numbers, entirely without JavaScript.

In practice, margin boxes are used for company logos in the header, for document titles, and for page numbering in the footer. Browser support for margin boxes is currently inconsistent: Chrome supports them through its native print to PDF renderer in a production ready way by now, while Firefox only renders some of the boxes correctly in newer versions. A robust use of @page rules with margin boxes therefore always tests explicitly in the actual target environment before the feature gets shipped to customers.


@page {
  size: A4;
  margin: 25mm 20mm 30mm 20mm;

  /* Header: company name left, document title centered */
  @top-left {
    content: "Mironsoft GmbH";
    font-size: 9pt;
    color: #555;
  }
  @top-center {
    content: "Invoice No. 2026-0417";
    font-size: 9pt;
    font-weight: 600;
  }

  /* Footer: page numbering via counters, no JavaScript involved */
  @bottom-right {
    content: "Page " counter(page) " of " counter(pages);
    font-size: 8pt;
    color: #777;
  }
}

6. Page sizes and orientation in detail

The size property inside @page rules supports a whole range of standardized format names: A3, A4, A5, B4, B5, letter, legal and ledger are the most common. Additionally, any format can be defined explicitly with two length values, for example size: 148mm 210mm; for a custom format that does not match any standard. The order of values follows width before height, regardless of orientation.

For orientation, the keyword landscape exists, which swaps the height and width values when combined with a format name, without having to manually recalculate the dimensions. An important practical note for @page rules: the user can manually override the paper format in the browser's print dialog, which means size is a recommendation, not an absolute guarantee. For legally binding layouts where exact page size is critical, a server side rendered PDF export remains the more reliable alternative.

7. Page numbering with counter(page) and counter(pages)

CSS ships with two special, implicitly defined counters that only work inside @page rules and their margin boxes: counter(page) outputs the current page number, counter(pages) the total number of pages in the document. Both counters are managed automatically by the renderer, no manual incrementing like with custom CSS counters is needed. The combination content: "Page " counter(page) " of " counter(pages); is the standard pattern for a complete page indicator in the footer.

A subtle point regarding @page rules with named page sections: when a document uses multiple @page definitions with different page-name values, the function target-counter() combined with page anchor references can even refer to the page number of another element, for example for an automatically generated table of contents with correct page numbers. This advanced technique is currently primarily fully implemented in specialized rendering engines such as Prince XML and Weasyprint, only partially in regular browsers.

8. Browser support and fallback strategies

Browser support for @page rules has grown historically fragmented. Basic properties such as size and margin work reliably in all current browsers. Margin boxes and the pseudo classes :left and :right, on the other hand, are supported less consistently, with the best support currently found in Chromium based browsers. A sensible fallback is to place critical information such as page numbers additionally in the regular document flow, so it remains visible even when a margin box is not rendered in a particular browser.

For use cases where exact, browser independent output is critical, for example legally binding invoices, using a dedicated rendering engine such as Prince XML, Weasyprint or Paged.js is recommended, since these implement @page rules fully and consistently, independent of the end user's browser. These tools render server side and deliver a PDF, instead of relying on the client side print dialog.

9. @page rules compared to other pagination approaches

Depending on the requirements for consistency and control, there are different ways to implement page sizes and numbering, with @page rules as the CSS native baseline option.

Approach Control Browser Consistency Infrastructure
@page rules in the browser Medium to high Inconsistent for margin boxes None, pure CSS
Prince XML / Weasyprint Very high Consistent, own rendering engine Requires a server side renderer
Paged.js High Simulates @page in the browser via JS Additional JS library
PDF library (e.g. TCPDF, DomPDF) Very high Fully controlled server side Own template language instead of CSS

Pure @page rules in the browser are best suited for cases where the user actively triggers "Print" or "Save as PDF" and small differences between browsers are acceptable. As soon as a system needs to generate PDFs automatically for many users at once, for example monthly invoice runs, the choice shifts clearly toward server side rendering engines, which can use the same @page rules as input but guarantee consistent output.

Mironsoft

CSS pagination, PDF layout and document automation

Invoices and reports with exact @page rules?

We implement @page rules with margin boxes, page numbering and named pages production ready, including a fallback strategy for browsers with patchy support.

Layout Concept

Planning page sizes, margins and margin boxes for invoices and reports

Implementation

Implementing named pages, numbering and headers and footers

Rendering Strategy

Deciding between browser printing and server side PDF rendering

10. Summary

@page rules extend print CSS with the missing layer of the page itself: fixed formats via size, custom margins via margin, different layouts via named pages and the pseudo classes :first, :left and :right. Margin boxes such as @top-center and @bottom-right enable headers and footers including automatic page numbering via counter(page), entirely without JavaScript.

The inconsistent browser support for margin boxes remains the biggest practical stumbling block for @page rules. For simple documents printed by the user themselves, native browser support is usually sufficient, while for automated, legally binding PDF generation at scale a dedicated rendering engine is the more robust choice.

Using @page Rules in Practice — The Essentials at a Glance

Base Syntax

@page { size: A4; margin: 25mm; } defines format and margins for the entire document.

Named Pages

@page cover { ... } plus page: cover; on an element switches to its own format.

Margin Boxes

Up to 16 boxes such as @top-center for headers and footers with static or generated content.

Numbering

counter(page) and counter(pages) provide automatic page numbers without JavaScript.

11. FAQ: Using @page Rules in Practice

1What are @page rules?
They define format and margins of the printed page itself, not individual HTML elements.
2How do I set the page size?
size: A4; for standard formats, size: 148mm 210mm; for custom dimensions, landscape swaps orientation.
3How do named pages work?
@page name plus page: name on an element switches to the new definition when that element is reached.
4What does @page :first do?
Applies only to the first page, useful for cover sheets with a larger margin or without a page number.
5What are margin boxes?
Up to 16 named areas such as @top-center inside @page, filled with text or counters.
6Page numbers without JavaScript?
counter(page) and counter(pages) inside a margin box, managed automatically by the renderer.
7Do margin boxes work everywhere the same?
No, inconsistent. Chromium offers the best support, Firefox only partially in newer versions.
8When :left and :right instead of :first?
For double sided printing with different binding margins, :first affects only the very first page.
9Can size be overridden?
Yes, users can choose a different paper format in the print dialog, size is a recommendation.
10When a PDF library instead of @page?
For automated, browser independent generation at scale, for example invoice runs, a server side engine is more robust.