Print CSS cheat sheet: control page breaks, margins and colours

Updated: · Dawwerni team

Title card for a print CSS cheat sheet showing a web page split into printed A4 pages with page break markers

To control print CSS page breaks, use break-before: page or break-after: page to force a new page, and break-inside: avoid on cards, figures and table rows to keep them whole. Set the paper with @page { size: A4; margin: 12mm; }, hide navigation inside @media print, and add print-color-adjust: exact to keep background colours.

Where print CSS goes and how to test it

Print rules live in a media query, either inside your main stylesheet or in a separate file loaded with media="print". Everything in this cheat sheet applies the moment someone presses Ctrl+P (Cmd+P on a Mac), and it is also what the browser uses when you choose Save as PDF. That makes print CSS the cheapest way to get a clean, text-selectable PDF out of any HTML page, including the single-file pages AI assistants produce.

You do not need to print paper to test. In Chrome or Edge, open DevTools, press Ctrl+Shift+P (Cmd+Shift+P), type Rendering, and set Emulate CSS media type to print. The page re-renders with your print rules applied. For page breaks themselves you still need the real print preview, because emulation does not paginate. If you just want the PDF without fiddling with the dialog, our free HTML to PDF tool offers the same browser print path with page size and margin presets.

<!-- Option 1: a separate print stylesheet -->
<link rel="stylesheet" href="print.css" media="print">

<!-- Option 2: a block inside your main CSS -->
<style>
  @media print {
    body { font-size: 11pt; }
  }
</style>

@page: paper size, margins and first page

The @page rule describes the sheet, not your content. size accepts keywords (A4, A5, letter, legal) with an optional orientation, or two explicit lengths. margin sets the white border the browser leaves around your content; in Chrome the margins chosen in the print dialog override it only if the user changes them away from Default.

@page {
  size: A4;            /* or: letter, A4 landscape, 13.333in 7.5in */
  margin: 15mm 12mm;   /* top/bottom, left/right */
}

@page :first {
  margin-top: 30mm;    /* extra room on the cover page */
}

@page :left  { margin-left: 20mm; }   /* for double-sided booklets */
@page :right { margin-right: 20mm; }

Named pages: one landscape page in a portrait document

Named pages let a wide table or chart sit on a landscape sheet while the rest stays portrait. You declare a named @page and assign it with the page property. This works in Chromium browsers and in recent Firefox; Safari support is newer, so test there before relying on it.

@page wide {
  size: A4 landscape;
}

.big-table-wrapper {
  page: wide;          /* this element starts on a landscape page */
}

Page numbers in the margin (Chromium only)

Page margin boxes such as @bottom-right with counter(page) are part of the CSS paged media spec. Recent Chrome and Edge versions render them; Firefox and Safari ignore them, so treat page numbers as a bonus rather than a requirement. If you rely on margin boxes, untick Headers and footers in Chrome's print dialog so the browser's own date and URL do not compete with yours.

@page {
  @bottom-right {
    content: "Page " counter(page) " of " counter(pages);
    font-size: 9pt;
    color: #666;
  }
}

Page breaks: break-before, break-after and break-inside

These three properties are the core of any print CSS page break strategy. break-before: page and break-after: page force a new sheet; break-inside: avoid asks the browser not to split an element across two sheets. The older page-break-before, page-break-after and page-break-inside names still work everywhere as aliases, so you can add them for very old engines, but the modern names are supported in all current browsers.

@media print {
  h1, .chapter        { break-before: page; }      /* each chapter on a new page */
  .cover              { break-after: page; }       /* nothing else on the cover */
  h2, h3              { break-after: avoid; }      /* never a heading alone at the bottom */
  figure, img, .card,
  pre, blockquote, tr { break-inside: avoid; }     /* keep these in one piece */
}

Orphans and widows

orphans sets the minimum number of lines of a paragraph left at the bottom of a page; widows sets the minimum carried to the top of the next. The default is 2. Raising both to 3 makes long reports look noticeably more professional. Chrome, Edge and Safari support them; Firefox currently does not, so in Firefox you may still see single lines stranded at a page edge.

@media print {
  p, li { orphans: 3; widows: 3; }
}

Keep background colours and dark sections

By default browsers drop background colours and images when printing to save ink, which is why a dark hero or a coloured table header turns white in the PDF. The user can tick Background graphics in Chrome's print dialog (More settings), but you can also request it from CSS. Use the prefixed version as well: Safari and older Chromium versions only read -webkit-print-color-adjust.

@media print {
  * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

Hide navigation, show link URLs

Menus, cookie banners, chat widgets, buttons and video players are noise on paper. Hide them in print and widen the main column. On paper a link cannot be clicked, so printing the destination after external links keeps the information. Limiting the rule to links that start with http avoids printing internal anchors like #section-2.

@media print {
  nav, header .menu, footer, .cookie-banner,
  .chat-widget, button, video { display: none !important; }

  main { width: auto; max-width: none; margin: 0; }

  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.85em;
    color: #555;
    word-break: break-all;
  }
}

Tables: repeat the header row on every page

When a long table spans several pages, readers lose track of the columns. Browsers repeat a table's thead on each printed page when it is displayed as a table header group, which is the default for a real thead element. Problems appear when a CSS framework or a responsive pattern changes the table to display: block or flex. Restore the table display values for print and keep rows whole.

Use a real thead element: a first row of th cells inside tbody will not be repeated. This works in Chrome, Edge, Firefox and Safari.

@media print {
  table  { display: table; width: 100%; border-collapse: collapse; }
  thead  { display: table-header-group; }   /* repeats on each page */
  tfoot  { display: table-footer-group; }
  tr     { break-inside: avoid; }
}

Fixed and sticky elements, 100vh and other screen-only patterns

An element with position: fixed is printed on every page in Chromium and Firefox, so a fixed site header can end up stamped over the top of each sheet and hide your text. Sections sized with height: 100vh can also behave strangely in print, because the viewport is replaced by the page box. Reset both for print, and let animated content appear in its final state.

@media print {
  .site-header, .sticky { position: static !important; }
  .hero, section        { height: auto !important; min-height: 0 !important; }
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
  .reveal-on-scroll     { opacity: 1 !important; transform: none !important; }
}

Browser support at a glance

FeatureChrome / EdgeFirefoxSafari
@page size and marginYesYesRecent versions; test
@page :firstYesYesYes
Named pages (page property)YesRecent versionsRecent versions; test
Margin boxes (@bottom-right, counters)Recent versionsNoNo
break-before / after / insideYesYes (avoid partly)Yes
orphans / widowsYesNoYes
print-color-adjustYes (add -webkit- too)Yes-webkit- prefix
Repeating theadYesYesYes

Because Chromium supports the widest set of paged media features, Chrome or Edge is usually the best engine for Save as PDF. If your page cannot be edited, for example an export from an AI tool, the HTML to PDF troubleshooting guide maps each symptom to a fix, and our converter can apply several of them (frozen animations, fixed headers shown once, dark backgrounds kept) without touching the code. For choosing the sheet itself, see the PDF page size guide, and for the bigger picture the three free ways to convert HTML to PDF.

FAQ

How do I force a page break in CSS?

Add break-before: page to the element that should start a new page, or break-after: page to the element that should end one, inside an @media print block. The older page-break-before: always also works in every current browser.

Why does break-inside: avoid not work?

The element is probably taller than one page, or it sits inside a flex or grid container where some engines ignore the rule. Make the container display: block for print and keep the protected blocks shorter than a page.

How do I keep background colours when printing to PDF?

Add print-color-adjust: exact and -webkit-print-color-adjust: exact to the elements (or to all elements) in your print CSS, or tick Background graphics in the browser's print dialog.

Can I add page numbers with CSS?

Yes, with @page margin boxes such as @bottom-right and counter(page). Recent Chrome and Edge render them; Firefox and Safari currently ignore them, so do not rely on them for every reader.

Why does my header appear on every printed page?

If it uses position: fixed, that is expected: fixed elements are repeated on each page by Chromium and Firefox. Set position: static on the header inside @media print, or hide it with display: none.

Try it: free HTML to PDF converter

Runs in your browser: no upload, no sign-up, no watermark.

A free tool by Dawwerni, makers of gamified loyalty for local businesses.