Print Styles
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:
@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:
<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:
@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:
@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:
| Property | Use |
|---|---|
break-before: page | Start this element on a new page, for example each chapter or each student's report card |
break-inside: avoid | Do not split this element if possible: figures, cards, table rows, address blocks |
break-after: avoid | Try not to break right after this element, useful on headings |
@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:
@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.
<!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: 1200pxlayout 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 printor a stylesheet withmedia="print"; usept,mmorcmon 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-insideandbreak-aftercontrol page breaks; the oldpage-break-*names are aliases.- Backgrounds are not printed by default; use
print-color-adjust: exactonly 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
@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?
attr(href) inserts the link's address as generated text.
2. Why might a red "Overdue" label made only with background-color disappear on paper?
Unless the user enables background graphics, backgrounds are skipped to save ink.
3. Which property starts each student's report card on a new page?
break-before: page forces a page break before the element.
4. What does @page { size: A4; } do?
size is a suggestion for the printed page box.
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
