EthicsComputerCourses
Join training +91 90532 10052

Print Styles

CSS · Lesson 67 of 68 Advanced 8 min read Updated

People still print web pages: bills and receipts, train tickets, admit cards, recipes, school notices, assignment instructions, and pages saved as PDF with "Print to PDF". Without print styles, they get the navigation bar, a cookie banner, a dark background that wastes ink, links that only say "click here", and a table cut in half across two pages.

In this lesson you will write a print stylesheet with @media print, decide what to hide and what to show, set the paper size and margins with @page, control where pages break, keep colours that carry meaning, and test it all without wasting paper.

Adding print styles

Print styles are ordinary CSS inside a print media query. You can put them at the end of your main stylesheet:

CSS
@media print {
  .site-header nav,
  .site-footer,
  .cookie-banner,
  .chat-button { display: none; }

  body {
    font: 11pt/1.45 Georgia, "Times New Roman", serif;
    color: black;
    background: white;
  }
}

Or load a separate file that only applies when printing:

HTML
<link rel="stylesheet" href="/css/print.css" media="print">

Print units such as pt (points), mm and cm make sense on paper, because the printer knows their real size. 10 to 12 points is a normal size for printed body text. Serif fonts are a common choice on paper, but your brand font is fine too.

What to hide and what to show

A printed page cannot be clicked, scrolled or hovered. Remove everything that only makes sense on a screen:

  • Navigation menus, search boxes, "back to top" buttons and share buttons.
  • Advertisements, chat widgets, cookie banners and video players.
  • Decorative background images and large hero banners.

Then add what paper needs. The most useful trick is printing the address of each link after its text, so a reader can still find the page:

CSS
@media print {
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.85em;
    word-break: break-all;
  }
}

The selector [href^="http"] picks only full web addresses, so links to sections of the same page (#fees) do not print a confusing #fees. attr(href) inserts the value of the attribute as text.

The page box with @page

The @page rule styles the printed page itself rather than an element. Its most common jobs are paper size, orientation and margins:

CSS
@page {
  size: A4;          /* or A4 landscape, letter, 210mm 297mm */
  margin: 15mm 12mm;
}

India uses A4 paper almost everywhere, so A4 is a sensible choice for receipts and forms. The size property is a request: the user can still choose another size or orientation in the print dialog. @page with size and margin became Baseline Newly available in late 2024, when Safari added support. Older Safari versions ignore it and use the printer defaults, which is fine.

Controlling page breaks

Browsers break pages wherever the paper runs out, which can split a table row, separate a heading from its paragraph, or leave one lonely line at the top of a page. Three properties give you control:

PropertyUse
break-before: pageStart this element on a new page, for example each chapter or each student's report card
break-inside: avoidDo not split this element if possible: figures, cards, table rows, address blocks
break-after: avoidTry not to break right after this element, useful on headings
CSS
@media print {
  h2, h3 { break-after: avoid; }
  figure, tr, .card, .address { break-inside: avoid; }
  .report-card + .report-card { break-before: page; }
}

These are hints. If an element is taller than a whole page, it must break somewhere. Not every browser honours every value, especially break-after: avoid, so test in more than one browser. In older code you will see page-break-before, page-break-after and page-break-inside; they still work as aliases, but the break-* names are the current standard.

The orphans and widows properties set the minimum number of lines of a paragraph left at the bottom or carried to the top of a page. They work in Chrome, Edge and Safari but not in Firefox.

Colours on paper

To save ink, browsers usually do not print background colours and background images unless the user switches on "Background graphics" in the print dialog. Text colours do print. So never rely on a background colour to carry meaning, such as a red "Unpaid" stamp drawn only as a background.

When a colour really matters, for example a coloured status badge or a brand bar on a certificate, print-color-adjust: exact asks the browser to keep it:

CSS
@media print {
  .status--unpaid {
    print-color-adjust: exact;
    background: #fee2e2;
    color: #991b1b;
    border: 1px solid #991b1b;  /* still visible if the background is dropped */
  }
}

Use it sparingly, since printing coloured areas costs the user ink. print-color-adjust is Baseline Newly available since 2025; older code uses the prefixed -webkit-print-color-adjust, which many sites still include as well.

A complete printable bill

Here is a shop bill with screen and print styles. Click "Print bill" to open the print preview, or emulate print in DevTools as described below.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Bill 2026-0457</title>
  <style>
    body { margin: 0; font-family: system-ui, sans-serif; background: #f1f5f9; color: #0f172a; }
    .topbar { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; background: #0f766e; color: white; }
    .topbar button { padding: 6px 14px; border: 0; border-radius: 6px; font: inherit; cursor: pointer; }
    .bill { max-width: 40rem; margin: 16px auto; padding: 24px; background: white; border-radius: 12px; }
    .bill header { display: flex; justify-content: space-between; gap: 16px; }
    .bill h1 { margin: 0; font-size: 1.4rem; }
    table { width: 100%; border-collapse: collapse; margin-block: 16px; }
    th, td { padding: 6px 8px; border-bottom: 1px solid #e2e8f0; text-align: left; }
    td:last-child, th:last-child { text-align: right; }
    .total td { font-weight: 700; border-bottom: 2px solid #0f172a; }
    .status { display: inline-block; padding: 2px 10px; border-radius: 999px; background: #fee2e2; color: #991b1b; border: 1px solid #991b1b; }
    .help { font-size: 0.9rem; }

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

    @media print {
      body { background: white; font: 11pt/1.4 Georgia, serif; }
      .topbar, .help button { display: none; }
      .bill { max-width: none; margin: 0; padding: 0; border-radius: 0; }
      tr { break-inside: avoid; }
      .status { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
      a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.85em; }
    }
  </style>
</head>
<body>
  <div class="topbar">
    <strong>Isha's Stationery, Lucknow</strong>
    <button type="button" onclick="window.print()">Print bill</button>
  </div>
  <main class="bill">
    <header>
      <div>
        <h1>Bill 2026-0457</h1>
        <p>Customer: Aarav Sharma<br>Date: 2 November 2026</p>
      </div>
      <p><span class="status">Unpaid</span></p>
    </header>
    <table>
      <thead><tr><th>Item</th><th>Qty</th><th>Amount</th></tr></thead>
      <tbody>
        <tr><td>A4 notebooks, 200 pages</td><td>6</td><td>₹540</td></tr>
        <tr><td>Geometry box</td><td>1</td><td>₹185</td></tr>
        <tr><td>Gel pens, pack of 10</td><td>2</td><td>₹220</td></tr>
        <tr class="total"><td>Total</td><td></td><td>₹945</td></tr>
      </tbody>
    </table>
    <p class="help">Pay online at <a href="https://example.com/pay/2026-0457">our payment page</a>. <button type="button">Chat with us</button></p>
  </main>
</body>
</html>

In print, the green top bar and the chat button disappear, the grey page background and the card's rounded corners go, the text switches to a print-friendly size, the payment link shows its address, and the "Unpaid" badge keeps its colour. Long bills would never split a row across two pages. Most browsers also repeat a table's <thead> row at the top of each printed page, which is one more reason to mark up tables properly.

Testing print styles

You do not need paper to test:

  • Press Ctrl+P to open the print preview. Choose "Save as PDF" as the destination to check several pages at once.
  • In Chrome or Edge DevTools, open the Rendering tool (Ctrl+Shift+P, then type "Rendering") and set Emulate CSS media type to print. The page stays in the window, and you can inspect print styles with the normal Styles pane. See Debugging CSS with DevTools.
  • Check with "Background graphics" both on and off.
  • Try a long page to see where pages break, and look at the last page for a lonely heading or line.

Common mistakes

  • No print styles at all. The navigation, banners and widgets fill the first page.
  • Meaning carried only by background colour. It disappears when backgrounds are not printed. Add a border, an icon or text.
  • Printing every link address. Internal anchors and long tracking URLs clutter the page. Select only full external addresses.
  • Fixed pixel widths. A width: 1200px layout overflows A4 paper. Let content use the page width in print.
  • Hiding content people need on paper, such as the order number, date or terms. Print what the reader will need offline.

Key points

  • Put print rules in @media print or a stylesheet with media="print"; use pt, mm or cm on paper.
  • Hide navigation, ads, widgets and buttons; print external link addresses with attr(href).
  • @page { size: A4; margin: 15mm; } sets the paper and margins, as a request the user can change.
  • break-before, break-inside and break-after control page breaks; the old page-break-* names are aliases.
  • Backgrounds are not printed by default; use print-color-adjust: exact only for colours that matter.
  • Test with print preview, Save as PDF and the DevTools print emulation.

Exercise

A school in Jaipur publishes exam admit cards as web pages. Each card has a header with the school logo and navigation, a photo, the student's name, roll number and exam centre, a list of exam dates in a table, and a "Download app" banner. Write print styles that hide the navigation and banner, print one admit card per A4 page with 12mm margins, keep the table rows and the student details block from splitting, and make sure the roll number stays bold and readable even without background graphics.

Show solution
CSS
@page {
  size: A4;
  margin: 12mm;
}

@media print {
  .site-nav,
  .app-banner { display: none; }

  body { font: 11pt/1.4 Georgia, serif; color: black; background: white; }

  .admit-card + .admit-card { break-before: page; }

  .admit-card__details,
  .admit-card tr { break-inside: avoid; }

  .admit-card__roll {
    font-weight: 700;
    font-size: 14pt;
    border: 1.5pt solid black;   /* visible without background graphics */
    padding: 2mm 4mm;
  }
}

Every card after the first starts a new page, so each student gets exactly one sheet. The roll number uses a border and bold text instead of a background colour, so it prints clearly with any print settings.

Quick Quiz

1. Which rule prints the web address after external links?

2. Why might a red "Overdue" label made only with background-color disappear on paper?

3. Which property starts each student's report card on a new page?

4. What does @page { size: A4; } do?

Learn CSS with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call