CSS Print Page Counters: counter(page) Explained

CSS print page counters, specifically counter(page) and counter(pages), are specialized CSS features used in paged media to dynamically generate page numbers for printed documents and exported PDFs. This article explains what these counters are, the difference between the current page and total page counters, how to implement them using the @page at-rule and margin boxes, and their practical browser support considerations.

Understanding counter(page) and counter(pages)

In traditional CSS for web layouts, elements flow continuously down a screen. However, when printing or exporting web pages to PDF, content is divided into discrete pages. To handle pagination, the CSS Paged Media Module introduces two reserved, automatically managed counters:

  • counter(page): Represents the current page number. The rendering engine automatically increments this counter as it breaks content across physical pages.
  • counter(pages): Represents the total page count of the rendered document. The engine computes this value after completing the document layout.

Unlike custom CSS counters created with counter-reset and counter-increment, these page counters are built directly into the pagination algorithm.

How to Implement Page Counters

Page counters are typically applied within margin boxes defined inside an @page rule. CSS margin boxes represent the headers, footers, and margins around the printable area of a page.

Here is a standard implementation displaying a "Page X of Y" format in the bottom-right corner of each page:

@page {
  /* Define margin box area */
  @bottom-right {
    content: "Page " counter(page) " of " counter(pages);
    font-family: sans-serif;
    font-size: 9pt;
    color: #555;
  }
}

Margin Box Locations

The CSS Paged Media specification provides 16 margin boxes where you can insert counters, including:

  • @top-left, @top-center, @top-right
  • @bottom-left, @bottom-center, @bottom-right
  • @left-top, @left-middle, @left-bottom
  • @right-top, @right-middle, @right-bottom

Changing Numbering Formats

The counter() function accepts an optional second argument that controls the numbering style. By default, decimal numbers are used, but you can specify formats such as Roman numerals or alphabetical lists:

@page {
  @bottom-center {
    /* Outputs lower-case Roman numerals (e.g., "i", "ii", "iii") */
    content: counter(page, lower-roman);
  }
}

Commonly used counter styles include decimal, lower-roman, upper-roman, lower-alpha, and upper-alpha.

Resetting and Customizing Page Numbers

You can restart page numbering for specific document sections using named pages and the standard counter-reset property. For example, to exclude a cover page and restart numbering on the main content:

@page cover {
  /* Remove footers from the cover page */
  @bottom-right {
    content: none;
  }
}

.cover-page {
  page: cover;
}

.main-content {
  break-before: page;
  counter-reset: page 1;
}

Browser Support and PDF Engines

Support for CSS margin boxes and counter(pages) varies depending on the rendering tool:

  • Print-to-PDF Tools: Dedicated HTML-to-PDF processors (such as PrinceXML, WeasyPrint, and Paged.js) offer full, native support for all margin boxes and both counter(page) and counter(pages).
  • Web Browsers: Native browser support for @page margin boxes has historically been limited. While modern Chromium-based browsers (Chrome, Edge) support margin boxes and page counters during print, Firefox and Safari have partial or varying support. Polyfills like Paged.js are frequently used to ensure cross-browser consistency when generating printable documents directly from client-side web pages.