Skip to content

Print CSS for PDF: a practical guide

Print CSS is the set of style rules that decides how a web page looks on paper or in a PDF. This guide shows you how to write a print stylesheet, set the page size and margins, control page breaks, and check the result with our free HTML to PDF tool.

In one sentence

A practical guide to print CSS for PDF output, covering @media print, @page size and margins, page numbers, page breaks and backgrounds, tested with the headless Chromium that makes the PDFs here.

Key facts

  • Print styles are the default (media print); prefer_css_page_size true makes the @page size and margin win over the format option.
  • Checked on 2026-10-11 with the engine's headless Chromium (Chrome for Testing 156): page margin boxes with counter(page), break-inside, break-after, repeated table headers and fixed elements behave as described in the guide.
  • Includes a runnable two-page example and the PDF it produced.

What print CSS is

Print CSS is the part of a stylesheet that applies when a page is printed or saved as a PDF. The same HTML can look one way on a screen and another way on paper. Print rules let you change the layout for paper without changing the page on screen.

Our tools use this idea. The HTML to PDF tool and the API render your HTML in headless Chromium, a browser with no window, and then print the page to a PDF. By default they use print styles, so any print CSS you write is used when the file is made.

This guide covers what to write, what the tools do with it, and how to check the result. It is written for people who build invoices, reports, forms and documents from HTML, and for anyone who wants a web page to print cleanly. It does not teach HTML from the beginning, and it gives no legal or tax advice about what a document must contain.

There are two common ways to write a print stylesheet. The first is a css media print block inside your main stylesheet. Rules inside @media print apply only when the page is printed or rendered with the print media type.

The second is a separate file linked with media="print" on the link element. The browser loads that file only for printing. Both approaches give the same result. Use the block form when the document is one self-contained HTML file, which is the case for most of the tools on this site. Use a separate file when a large site already has one stylesheet for screens.

The tools let you choose the media type with the media option, which takes print or screen. The default is print, so a PDF uses your print styles. Choose screen when you want the PDF to look like the page on your monitor, for example when you print a chart that you styled for the screen.

A good starting point is to remove navigation, buttons and decoration, use a plain background, and set a readable text size in points or millimetres. Change one rule at a time and render the file after each change.

  • Hide navigation, search boxes and buttons that do nothing on paper.
  • Use dark text on a white or plain background.
  • Set widths in percentages or millimetres so that content fits the page width.
  • Use one of the server fonts listed later in this guide, or load a web font yourself.

Here is the smallest useful form of a print block. Add your own rules inside it.

CSS and HTML
/* Normal (screen) styles */
body { font-family: system-ui, sans-serif; background: #f5f5f5; }
.site-nav, .cookie-banner { display: flex; }

/* Applies only when the page is printed or saved as PDF */
@media print {
  .site-nav, .cookie-banner { display: none; }
  body { background: #fff; color: #000; font-size: 11pt; }
}

<!-- or, as a separate file in your HTML head: -->
<link rel="stylesheet" href="site.css">
<link rel="stylesheet" href="print.css" media="print">

Set the page size and margins with @page

The @page rule sets the size and the margin of each printed page. A css page margin is written inside this rule, next to the page size. The margin is the blank space between the edge of the paper and your content.

The tools have two ways to decide the page size. By default the format option applies. The default format is A4, and the default margin is 10 mm on all four sides. You can choose A3, A5, Letter or Legal, and you can change the margin in millimetres.

The second way uses the size in your @page rule. In the HTML to PDF web tool, tick the checkbox labelled to use the page size and margins from your CSS @page rule. In the API, set prefer_css_page_size to true. When this setting is on, the size and margin in your @page rule are used and the format menu is ignored. When it is off, the format option sets the page size and the @page size is ignored.

We checked this with an @page rule that sets a page size of 100 by 150 mm and a margin of 30 mm. The output was a 100 by 150 mm page with a 30 mm margin. You can run the same check on your own file if the size of the page matters for your output.

Choose one place for the size and margin and keep it there. If you set the margin both in @page and in the format options, make sure you know which setting is in use, as described above.

CSS
@page {
  size: A4;
  margin: 20mm 15mm;
}

/* The first page gets more room at the top for the title block */
@page :first {
  margin-top: 35mm;
}

Page numbers and running headers with page margin boxes

Page margin boxes are areas in the margin of each page, such as the top-left corner or the bottom centre. You put text in them with the content property. Two counters are useful here. The counter page gives the current page number, and the counter pages gives the total number of pages.

In our checks, a bottom-centre box with the text Page, then the page counter, then of, then the pages counter printed Page 1 of 5 on the first page of a five-page document.

You can change one page without changing the others. The :first selector applies a rule to the first page only. For example, a running title in the top-left box can be removed on the first page with @page :first. This is useful when a cover page should not repeat the title.

In the web tool, turn on the checkbox that uses your CSS @page rule, so that the page size and margins match what you wrote, and leave enough margin for the boxes. The API also has header and footer templates with page number spans. That option is covered in the API section below.

CSS
@page {
  size: A4;
  margin: 20mm 15mm;

  @top-left {
    content: "Quarterly report";
    font-size: 9pt;
    color: #666;
  }
  @bottom-center {
    content: "Page " counter(page) " of " counter(pages);
    font-size: 9pt;
    color: #666;
  }
}

/* No running title on the first page */
@page :first {
  @top-left { content: ""; }
}

Control page breaks with break-before, break-after and break-inside

A css page break decides where one printed page ends and the next one begins. Without any rules, the browser breaks the content where the page is full. That can split a table row, a card or a heading away from the text that follows it.

Three properties control this. break-before: page starts a new page before the element. break-after: page ends the page after the element. break-inside: avoid keeps the element on one page when it fits.

In our checks, break-inside: avoid kept cards whole, break-after: avoid on a heading moved the heading to the next page together with the text after it, and break-before: page started a new page at the element.

Apply break rules to the smallest block that should stay together, such as a card, a figure or a row group. Avoid putting break rules on the whole page, because that gives the browser fewer choices.

Tables repeat their header row by default. In our check with a 90-row table, the thead row was printed at the top of both pages. If you change the display property of thead, keep it as table-header-group, or the header may not repeat.

Older stylesheets use the page-break-before, page-break-after and page-break-inside names. The break-before, break-after and break-inside names are the newer ones. Our checks used the newer names. If your stylesheet uses the older names, test each page break before you rely on it.

A break rule is a request, not a guarantee. If a block is taller than a page, the browser still has to split it.

CSS
h2, h3 { break-after: avoid; }          /* a heading is never left alone at the bottom of a page */
.card, figure, tr { break-inside: avoid; } /* keep a block or a table row in one piece */
.chapter { break-before: page; }           /* always start this on a new page */

thead { display: table-header-group; }     /* repeat the table header on every page */

/* Older names, still understood by browsers */
.chapter { page-break-before: always; }
.card    { page-break-inside: avoid; }

Backgrounds and colours in print

Background colours and images are often left out when a page is printed. The tools print backgrounds by default. In the API, print_background is true unless you change it. The web tools have a backgrounds switch that you can turn off.

If you turn backgrounds off and one element still needs its colour, set print-color-adjust: exact on that element. Set both print-color-adjust: exact and -webkit-print-color-adjust: exact on the element. With print_background set to false, the element's background then prints, while other backgrounds stay out.

Use this only where it helps the reader, such as a coloured heading band or a table with shaded rows. Large coloured areas use more ink if someone prints the file.

Good css print styles keep the page plain. Use light tints for fills, keep text dark, and make sure text still reads clearly when there is no colour at all.

CSS
/* Print this element's colours even when the printer or the API has backgrounds switched off */
.badge, .table-head {
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

Hide, show and rewrite things for print

Print CSS often hides things that only make sense on a screen. Use display: none on navigation bars, cookie banners, share buttons and forms that do not work on paper.

The opposite also works. Some content is only useful on paper, such as a note that explains the source of a chart. You can show that note in print and hide it on the screen.

CSS
.print-only { display: none; }          /* hidden on screen */

@media print {
  .no-print, .share-buttons { display: none; }
  .print-only { display: block; }       /* shown on paper */
}

Long web addresses can be shown in print. A rule on the a[href] selector can add the address in brackets after each link, so a reader of a paper copy can see where the link goes. This is a plain text rule with no extra browser features.

CSS
a[href^="http"]::after {
  content: " (" attr(href) ")";
  font-size: 9pt;
  color: #555;
}

.running-note {
  position: fixed;   /* repeats on every printed page */
  bottom: 0;
  right: 0;
  font-size: 9pt;
}

Fixed elements repeat. An element with position: fixed is printed on every page. Use this for a running logo or a footer line, and avoid it for content that should appear only once.

Try it: a full example, rendered to PDF

The example below is a short report. It has a title, two sections, a table with a repeating header row, a card that should not split across pages, and a page number in the bottom margin. All names and figures are fictional.

Paste the example into the HTML to PDF tool. Tick the checkbox that uses the page size and margins from your CSS @page rule, then convert the file and download the PDF.

Check the result for four things: the page number in the bottom margin, the table header on each page, the card kept whole, and the margins on the first page. If a card splits across pages, check its break-inside rule.

HTML
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>Quarterly report</title>
<style>
  @page {
    size: A4;
    margin: 20mm 15mm;
    @top-left { content: "Quarterly report"; font-size: 9pt; color: #666; }
    @bottom-center { content: "Page " counter(page) " of " counter(pages); font-size: 9pt; color: #666; }
  }
  @page :first { margin-top: 30mm; @top-left { content: ""; } }
  body { font-family: "Liberation Sans", Helvetica, Arial, sans-serif; font-size: 11pt; line-height: 1.45; color: #1a1a1a; margin: 0; }
  h1 { font-size: 24pt; margin: 0 0 4px; }
  h2 { font-size: 14pt; margin: 22px 0 8px; break-after: avoid; }
  .lead { color: #444; margin: 0 0 14px; }
  .badge { display: inline-block; background: #2f4bff; color: #fff; padding: 2px 10px; border-radius: 99px; font-size: 9pt; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .card { border: 1px solid #c9ceef; background: #f0f2ff; padding: 10px 14px; margin: 10px 0; break-inside: avoid; height: 38mm; }
  .card h3 { margin: 0 0 4px; font-size: 12pt; }
  table { width: 100%; border-collapse: collapse; }
  thead { display: table-header-group; }
  th { background: #1e2a78; color: #fff; text-align: left; padding: 6px 8px; font-size: 10pt; }
  td { padding: 6px 8px; border-bottom: 1px solid #ddd; }
  td:last-child, th:last-child { text-align: right; }
  tr { break-inside: avoid; }
  .chapter { break-before: page; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 8.5pt; color: #555; }
  .note { position: fixed; bottom: 0; right: 0; font-size: 8pt; color: #888; }
</style></head><body>
<div class="note">Generated with aiipworld.com</div>
<h1>Quarterly report</h1>
<p class="lead">Q3 summary for the fictional company Sample Studio. <span class="badge">Draft</span></p>
<h2>Highlights</h2>
<div class="card"><h3>Renewals</h3><p>Most accounts renewed without contacting support. The next review is on 12 November.</p></div>
<div class="card"><h3>Support</h3><p>Open tickets fell during the quarter. The knowledge base now covers billing questions.</p></div>
<div class="card"><h3>Product</h3><p>The export feature shipped in October. Feedback is collected through the <a href="https://aiipworld.com/contact/">contact page</a>.</p></div>
<div class="card"><h3>Plans</h3><p>The <a href="https://aiipworld.com/pricing/">plan list</a> is the source for this report.</p></div>
<div class="chapter"><h2>Revenue by region</h2>
<p>All figures in US dollars. The table header repeats if the table runs onto another page.</p>
<table><thead><tr><th>Region</th><th>Line</th><th>Revenue</th></tr></thead><tbody>
<tr><td>North</td><td>Subscriptions</td><td>41,200</td></tr>
<tr><td>North</td><td>Services</td><td>12,850</td></tr>
<tr><td>South</td><td>Subscriptions</td><td>37,900</td></tr>
<tr><td>South</td><td>Services</td><td>9,400</td></tr>
<tr><td>East</td><td>Subscriptions</td><td>28,300</td></tr>
<tr><td>East</td><td>Services</td><td>7,150</td></tr>
<tr><td>West</td><td>Subscriptions</td><td>33,600</td></tr>
<tr><td>West</td><td>Services</td><td>10,020</td></tr>
<tr><td>Online</td><td>Subscriptions</td><td>52,480</td></tr>
<tr><td>Online</td><td>Services</td><td>4,310</td></tr>
<tr><td>Partners</td><td>Subscriptions</td><td>19,750</td></tr>
<tr><td>Partners</td><td>Services</td><td>6,880</td></tr>
<tr><td>North 2</td><td>Subscriptions</td><td>41,200</td></tr>
<tr><td>North 2</td><td>Services</td><td>12,850</td></tr>
<tr><td>South 2</td><td>Subscriptions</td><td>37,900</td></tr>
<tr><td>South 2</td><td>Services</td><td>9,400</td></tr>
<tr><td>East 2</td><td>Subscriptions</td><td>28,300</td></tr>
<tr><td>East 2</td><td>Services</td><td>7,150</td></tr>
<tr><td>West 2</td><td>Subscriptions</td><td>33,600</td></tr>
<tr><td>West 2</td><td>Services</td><td>10,020</td></tr>
<tr><td>Online 2</td><td>Subscriptions</td><td>52,480</td></tr>
<tr><td>Online 2</td><td>Services</td><td>4,310</td></tr>
<tr><td>Partners 2</td><td>Subscriptions</td><td>19,750</td></tr>
<tr><td>Partners 2</td><td>Services</td><td>6,880</td></tr>
<tr><td>North 3</td><td>Subscriptions</td><td>41,200</td></tr>
<tr><td>North 3</td><td>Services</td><td>12,850</td></tr>
<tr><td>South 3</td><td>Subscriptions</td><td>37,900</td></tr>
<tr><td>South 3</td><td>Services</td><td>9,400</td></tr>
<tr><td>East 3</td><td>Subscriptions</td><td>28,300</td></tr>
<tr><td>East 3</td><td>Services</td><td>7,150</td></tr>
<tr><td>West 3</td><td>Subscriptions</td><td>33,600</td></tr>
<tr><td>West 3</td><td>Services</td><td>10,020</td></tr>
<tr><td>Online 3</td><td>Subscriptions</td><td>52,480</td></tr>
<tr><td>Online 3</td><td>Services</td><td>4,310</td></tr>
<tr><td>Partners 3</td><td>Subscriptions</td><td>19,750</td></tr>
<tr><td>Partners 3</td><td>Services</td><td>6,880</td></tr>
</tbody></table></div>
</body></html>

The example sets Liberation Sans, one of the fonts installed on the render server. Arial and Helvetica are mapped to it, and Times New Roman maps to Liberation Serif. Keep the example as your starting point and change one rule at a time.

Open the HTML to PDF tool

Example output

Two-page report printed with the CSS from this guide (page 1)
The example from this guide, printed with prefer_css_page_size on. Generated by this site's own engine. Page 1 shown. Open the PDF (or select the picture) for the full document.

If you make PDFs from your own software, call the API. Send a request to the render endpoint with your HTML and the options you want. The API answers with status 202 and a job id. Poll the job endpoint with that id until the status is succeeded, failed or timeout.

Authenticate with your API key from the dashboard, sent as Authorization: Bearer followed by the key, or in the x-api-key header. A succeeded job gives a download link that is short-lived, so download the file as soon as the job finishes.

curl -X POST https://api.aiipworld.com/v1/render \
  -H "Authorization: Bearer $AIIPWORLD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html":"<!doctype html><html>…your document, with its @page and @media print rules…</html>","output":"pdf","options":{"format":"A4","print_background":true,"prefer_css_page_size":true}}'

# returns {"job_id": "...", "status": "queued"}; then poll:
curl https://api.aiipworld.com/v1/jobs/JOB_ID -H "Authorization: Bearer $AIIPWORLD_API_KEY"

The print options below control the PDF. Each one is optional, and the defaults apply when you leave one out.

  • media: print (the default) or screen, to choose which styles apply.
  • format, width and height set the page size. The format default is A4.
  • landscape turns the page sideways.
  • margin takes top, right, bottom and left values in millimetres. The default is 10 mm on each side.
  • print_background is true by default. Set it to false only when the page should print without backgrounds.
  • scale changes how large the printed content is.
  • prefer_css_page_size set to true uses the size and margin from your @page rule.
  • page_ranges chooses which pages to print, for example 1-3.
  • display_header_footer, with header_template and footer_template, adds a header and a footer. Use spans with the classes pageNumber and totalPages for page numbers. Leave room in the margin for them, and keep each template short.
  • tagged and outline are options you turn on when your file needs them.
  • wait_until, wait_for_selector, delay_ms and javascript control when the page counts as ready to print. JavaScript runs by default.

Each request makes one document. There is no batch endpoint, so send one request for each file. Template ids are not available yet, and the API returns an error if you send one. Webhooks are not available yet either.

Common problems

Tables run past the edge of the page. A table that is wider than the page can be cut at the margin. Set the table width to 100 percent, or make the text smaller, and check the page size in your @page rule or the format option.

Blank pages appear. A blank page can come from an element that is taller than the page, or from a break rule placed after the last item. Remove one rule at a time until the blank page goes.

Backgrounds are missing. Check the print_background option first, then the print-color-adjust rule on the element, as described above.

Fonts look different from the screen. The render server has a fixed set of fonts. Fonts installed on your own computer are not there. To use another font, load it with @font-face from a public https address or from a data URL.

A page behind a login shows the login screen. No cookies are sent with the request, so the PDF shows what an anonymous visitor sees.

A fixed header covers content. Fixed elements repeat on every page, so leave space for them at the top and bottom of the page.

Testing your print stylesheet

Start with your browser's print preview. It shows roughly how the page will break. Then render the file with the HTML to PDF tool or the API, because the rendering engine is what makes your PDF.

The PDF can differ a little from your browser's print preview, especially for fonts and page breaks. Check the PDF itself, not only the preview.

Check at least three kinds of page: one with a table, one with a long paragraph, and one with a card or an image. Look at the top and bottom margins, the page numbers and the last page.

Keep a small test file with your rules and render it after each change. This makes it easy to see which change caused a problem.

The web tool keeps results for 10 minutes and then deletes them, and it keeps no history, so download the PDF you need straight away.

Questions

How do I write a print stylesheet?

Put your print rules inside a block that starts with @media print, or link a separate stylesheet with the media attribute set to print. Start by hiding navigation and buttons, then set plain colours and a readable text size. Render the file to check the result.

What is the difference between css media print and media="print"?

A css media print block is a set of rules inside your stylesheet that applies only to printing. The media="print" attribute on a link element loads a whole file only for printing. Both give the same result. In our tools, the media option chooses print or screen for the whole conversion.

How do I force a page break?

Add break-before: page to the element that should start a new page, or break-after: page to the element before it. Put the rule on a block, not on a line of text. Then render the file, because the browser still splits content that is taller than a page.

How do I avoid a css page break inside a table or a card?

Use break-inside: avoid on the card, or on a row group of the table. The browser keeps that element whole when it fits on one page. For a table, the header row in thead repeats on each page by default.

How do I set margins with css page margin rules?

Set the size and the margin inside an @page rule, then turn on the checkbox that uses your CSS @page rule in the HTML to PDF tool, or set prefer_css_page_size to true in the API. Without that setting, the format and margin options apply. The default margin is 10 mm on each side.

Why are backgrounds missing from my PDF?

Backgrounds print by default in our tools, so first check that the backgrounds option is on, or that print_background is not set to false in the API. If you turn it off, set print-color-adjust: exact on the element that needs its background printed.

How do I add page numbers to a PDF?

In the web tools, add an @page rule with a bottom margin box that uses counter(page) and counter(pages), and turn on the checkbox that uses your @page rule. In the API, you can also use header and footer templates with spans for pageNumber and totalPages.

What is the difference between page-break-* and break-*?

The break-before, break-after and break-inside properties are the newer names for the same jobs as the older page-break-before, page-break-after and page-break-inside. Our checks used the newer names. If an older stylesheet uses the page-break names, test it before you rely on it.

Why does my PDF look different from my browser's print preview?

The PDF is made by a headless Chromium engine with its own set of fonts, so a font from your computer may not be there. Load web fonts from a public https address, and check the PDF itself, because fonts and page breaks can differ from a preview.

Can I use the @page size with your tool?

Yes. Tick the checkbox that uses the page size and margins from your CSS @page rule in the HTML to PDF tool, or set prefer_css_page_size to true in the API. When that setting is off, the format option decides the page size and the @page size is ignored.

Does the tool check my CSS or tell me what my document must contain?

No. The tool renders the HTML and CSS you give it and gives no legal or tax advice. Whether a document meets the rules in your country is for you, or your adviser, to decide.

How long are my results kept?

On the web tools, results are deleted after 10 minutes, and no history is kept, so download the PDF when it is ready. Through the API, results are kept for 1 day on the free plan, 7 days on Starter and Pro, and 30 days on Business.

Related tools