CSS Print Headers and Footers with Page Margin Boxes
CSS handles print headers and footers through the CSS Paged Media
Module, specifically using the @page rule combined with
predefined page-margin boxes. By dividing the margin area surrounding a
printed page into sixteen distinct regions, CSS allows developers to
position static text, dynamic page counts, and document titles directly
into margins without altering the underlying HTML structure.
The @page Rule
and Margin Box Layout
When formatting documents for print, the browser or rendering engine
treats each sheet of paper as a paged context. The @page
at-rule defines the dimensions, orientation, and margins of these
pages.
The perimeter inside the page margins is divided into 16 margin boxes:
- Top Margins:
@top-left-corner,@top-left,@top-center,@top-right,@top-right-corner - Bottom Margins:
@bottom-left-corner,@bottom-left,@bottom-center,@bottom-right,@bottom-right-corner - Left Margins:
@left-top,@left-middle,@left-bottom - Right Margins:
@right-top,@right-middle,@right-bottom
Headers are typically placed within @top-left,
@top-center, or @top-right, while footers
reside in @bottom-left, @bottom-center, or
@bottom-right.
Basic Syntax and Implementation
Margin boxes function similarly to standard CSS pseudo-elements. You
declare them inside an @page block and assign content using
the content property.
@page {
size: A4;
margin: 20mm;
@top-center {
content: "Quarterly Performance Report";
font-size: 9pt;
color: #666;
}
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
font-size: 9pt;
}
}Dynamic Page Numbering
CSS Paged Media provides built-in counters specifically for print contexts:
counter(page)represents the current page number.counter(pages)represents the total number of pages in the printed document.
These counters update automatically per page and only function within the context of page-margin boxes or page-break events.
Extracting Document Content with Named Strings
To display dynamic document data in headers—such as the current
chapter title—CSS provides the string-set property and the
string() function.
- Identify the element containing the title in your document and copy
its text into a named string using
string-set. - Retrieve that value inside the
@pagemargin box usingcontent: string(variable-name).
h1 {
string-set: chapter-title content();
}
@page {
@top-left {
content: string(chapter-title);
font-style: italic;
}
}When a new h1 appears in the document flow, the
chapter-title value automatically updates for subsequent
pages.
First, Left, and Right Page Variations
Headers and footers often need to vary depending on the page position, such as omitting headers on a cover page or alternating margins for double-sided printing. CSS accommodates this through pseudo-class page selectors:
@page :firsttargets the opening page, commonly used to clear headers (content: none).@page :lefttargets even-numbered pages in double-sided printing.@page :righttargets odd-numbered pages in double-sided printing.
@page :first {
@top-center { content: none; }
@bottom-right { content: none; }
}
@page :left {
@top-left { content: counter(page); }
@top-right { content: "Document Title"; }
}
@page :right {
@top-left { content: "Document Title"; }
@top-right { content: counter(page); }
}Browser Support and Print Engines
Standard web browsers have partial support for CSS Paged Media Level
3 margin boxes. While features like basic @page margins are
universally supported, full margin-box placement and
string-set are predominantly supported by dedicated
print-to-PDF engines such as WeasyPrint, PrinceXML, and Paged.js, or
Chromium-based browsers via specific print flags and headless printing
pipelines.