← Back to DevBytes

Mastering CSS Print Styles: Tips and Best Practices

What Are CSS Print Styles?

CSS print styles are a set of CSS rules specifically designed to control how a webpage appears when printed on paper or saved as a PDF. They are defined using the @media print media query, which tells the browser to apply these styles only when the user prints the page (or uses print preview). This allows developers to create an entirely different visual experience optimized for the printed medium — stripping away unnecessary navigation, adjusting typography for readability on paper, and ensuring content flows correctly across physical pages.

When you visit a webpage and hit Ctrl+P (or Cmd+P on macOS), the browser doesn't simply take a screenshot of the screen. Instead, it consults the print stylesheet and renders the page according to those rules. If no print styles are defined, the browser applies its own default transformations, which often lead to poor results — tiny text, broken layouts, wasted ink on irrelevant UI elements, and content cut off awkwardly at page boundaries.

Why Print Styles Matter

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Despite living in a digital-first world, printed web content remains surprisingly common. Consider these scenarios:

In each case, a well-crafted print stylesheet dramatically improves the user experience. It ensures the printed output is legible, well-organized, and free of clutter. Without print styles, users might waste pages printing navigation menus, sidebar widgets, ads, and social media buttons — all completely useless on paper. Worse, critical content might be missing, truncated, or rendered in an unreadable font size.

Print styles also demonstrate attention to detail and accessibility. They show you've considered the full lifecycle of your content, including its offline, physical manifestation. For documentation-heavy sites, blogs, and news outlets, good print styles can be a significant competitive advantage.

How to Implement Print Styles

There are three main approaches to adding print styles to your project:

Approach 1: An External Print Stylesheet

Create a dedicated CSS file and link it with the media="print" attribute:

<!-- In your HTML <head> -->
<link rel="stylesheet" href="print.css" media="print">

This keeps print styles completely separate from screen styles. The browser only downloads and applies print.css when printing, which can be beneficial for performance on initial page load (though modern browsers typically download all stylesheets regardless of media type).

Approach 2: An Embedded @media Print Block

Place a @media print block directly inside your main stylesheet or within a <style> tag:

/* Inside your main styles.css */
@media print {
  /* All print-specific rules go here */
  body {
    font-size: 12pt;
    color: #000;
    background: #fff;
  }
  
  nav, .sidebar, .advertisement {
    display: none;
  }
}

This is the most common approach — it keeps everything in one file and makes it easy to see print overrides right next to their screen counterparts.

Approach 3: Combining Screen and Print in a Single Media Query

You can also target both screen and print in one rule using a comma-separated media query list, though this is rarely used for print-specific styling and more common for shared styles:

@media screen, print {
  /* Styles applied to both screen and print */
  .article-body {
    line-height: 1.6;
    font-family: Georgia, serif;
  }
}

@media screen {
  /* Screen-only additions */
  .article-body {
    max-width: 700px;
    margin: 0 auto;
  }
}

@media print {
  /* Print-only overrides */
  .article-body {
    font-size: 11pt;
  }
}

Best Practices for Print Stylesheets

1. Reset Backgrounds and Colors for Ink-Friendly Output

Printed pages are typically white. Remove dark backgrounds, reset text to black, and eliminate unnecessary color usage to save ink and improve readability:

@media print {
  *,
  *::before,
  *::after {
    background: transparent !important;
    color: #000 !important;
    box-shadow: none !important;
    text-shadow: none !important;
  }
  
  /* Keep images visible — they have their own colors */
  img {
    /* Don't force color on images */
    color: auto;
  }
}

The !important flags ensure these resets override any inline styles or highly specific selectors that might otherwise leak through. Note that forcing color: #000 on images can cause problems, so exclude img elements or reset their color property carefully.

2. Hide Irrelevant Elements

Navigation bars, sidebars, social sharing buttons, comment sections, ads, and cookie banners serve no purpose on paper. Hide them:

@media print {
  nav,
  .site-header,
  .sidebar,
  .comments-section,
  .social-share,
  .ad-banner,
  .cookie-notice,
  footer,
  .related-posts,
  #newsletter-signup {
    display: none !important;
    visibility: hidden;
  }
}

Be thoughtful — sometimes a footer contains copyright information or citations that should appear in print. Consider creating a minimal print-only footer with essential metadata instead of hiding the footer entirely.

3. Use Absolute Units for Typography

Screen typography relies on relative units like rem, em, and px. For print, absolute units like pt (points) and physical measurements are more appropriate because they map directly to real-world dimensions:

@media print {
  body {
    font-family: Georgia, "Times New Roman", serif;
    font-size: 12pt;
    line-height: 1.5;
  }
  
  h1 { font-size: 24pt; }
  h2 { font-size: 18pt; }
  h3 { font-size: 14pt; }
  h4 { font-size: 12pt; }
  
  p, li, td {
    font-size: 11pt;
  }
  
  .small-print, figcaption {
    font-size: 9pt;
  }
}

Points (1pt ≈ 0.35mm) are the standard unit for printed typography. Body text at 10–12pt is comfortable to read. Headings should be clearly larger. Serif fonts traditionally work better for printed body text because the serifs guide the eye horizontally across long lines.

4. Control Page Breaks

CSS provides powerful properties to control where pages break. Nothing frustrates a reader more than a heading orphaned at the bottom of a page or a critical paragraph split across two pages:

@media print {
  /* Prevent page breaks inside these elements */
  .avoid-break-inside,
  blockquote,
  table,
  .callout-box,
  .important-note {
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  /* Force a page break before major sections */
  h2, .chapter-heading {
    page-break-before: always;
    break-before: page;
  }
  
  /* Prevent orphaned headings — heading must have at least
     2 lines of content after it before a page break */
  h3, h4 {
    page-break-after: avoid;
    break-after: avoid;
  }
  
  /* Keep headings with their following paragraph */
  h2 + p, h3 + p, h4 + p {
    page-break-before: avoid;
    break-before: avoid;
  }
}

Use the modern break-inside, break-before, and break-after properties for better browser support, but keep the legacy page-break-* equivalents as fallbacks. The avoid value prevents a break inside the element when possible, while always forces a break.

5. Handle Links and URLs

On paper, hyperlinks are useless — you can't click them. However, displaying the URL after the link text lets readers know where the link pointed to. This is especially useful for citations and references:

@media print {
  /* Show the full URL after external links */
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    font-weight: normal;
    color: #555;
    word-break: break-all;
  }
  
  /* Don't show URLs for internal links or anchors */
  a[href^="#"]::after,
  a[href^="/"]::after {
    content: none;
  }
  
  /* Hide the URL for links that already display it visibly */
  a[data-print-url="hide"]::after {
    content: none;
  }
}

Be selective — displaying every single URL can make the printed page look cluttered. Consider showing URLs only for external links and references. You can also use a data attribute like data-print-url="hide" to selectively suppress URL display on specific links.

6. Expand Abbreviations and Acronyms

Similar to showing URLs, you can reveal the expanded text of abbreviations so readers don't have to guess what "API" or "CSS" stands for:

@media print {
  abbr[title]::after {
    content: " (" attr(title) ")";
    font-size: 9pt;
    color: #555;
  }
}

7. Set Proper Page Margins and Size

You can suggest page dimensions and margins using the @page rule. Note that browsers and printer drivers may override these, but providing sensible defaults improves the output in most cases:

/* Set default page margins for all pages */
@page {
  margin: 2cm 2.5cm;
  size: A4; /* or letter, legal, etc. */
}

/* Target the first page differently (e.g., a cover page) */
@page :first {
  margin-top: 4cm;
}

/* Target left-hand pages in a duplex (book-style) print */
@page :left {
  margin-left: 3cm;
  margin-right: 2cm;
}

/* Target right-hand pages */
@page :right {
  margin-left: 2cm;
  margin-right: 3cm;
}

Common paper sizes you can specify: A4, A5, letter, legal, ledger. You can also use custom dimensions like size: 210mm 297mm; for precise control.

8. Optimize Tables for Print

Wide tables that require horizontal scrolling on screen become a nightmare on paper. They get cut off, losing columns entirely. Address this with print-specific table styling:

@media print {
  table {
    width: 100%;
    font-size: 10pt;
    border-collapse: collapse;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  th, td {
    border: 1px solid #999;
    padding: 6px 8px;
    text-align: left;
    vertical-align: top;
  }
  
  th {
    background-color: #f0f0f0 !important;
    font-weight: bold;
  }
  
  /* For very wide tables, consider rotating layout */
  @media print and (max-width: 15cm) {
    table.adaptive-print,
    table.adaptive-print thead,
    table.adaptive-print tbody,
    table.adaptive-print th,
    table.adaptive-print td,
    table.adaptive-print tr {
      display: block;
    }
    
    table.adaptive-print thead tr {
      position: absolute;
      top: -9999px;
      left: -9999px;
    }
    
    table.adaptive-print td {
      position: relative;
      padding-left: 50%;
      border-bottom: 1px solid #ddd;
    }
    
    table.adaptive-print td::before {
      content: attr(data-label);
      position: absolute;
      left: 10px;
      width: 45%;
      font-weight: bold;
      white-space: nowrap;
    }
  }
}

The adaptive table approach converts a horizontal table into a card-like vertical layout when the printable area is narrow. This requires adding data-label attributes to each cell matching its column header.

9. Handle Images Thoughtfully

Images consume significant ink and can break across pages awkwardly. Apply sensible constraints:

@media print {
  img {
    max-width: 100%;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  /* Prevent figures from breaking across pages */
  figure {
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  /* Hide purely decorative images */
  img.decorative,
  img[alt=""],
  .hero-image,
  .banner-image {
    display: none;
  }
  
  /* Ensure SVG icons print cleanly */
  svg {
    fill: currentColor;
  }
}

Be judicious about hiding images. Hero images and decorative backgrounds add no value on paper, but charts, diagrams, photographs in articles, and maps should be preserved. Use the alt attribute or a class to distinguish content images from decorative ones.

10. Add Print-Only Content

Sometimes you want to show additional information only in the printed version — copyright notices, QR codes linking back to the original page, or a condensed reference section:

@media print {
  .print-only {
    display: block !important;
  }
  
  .screen-only {
    display: none !important;
  }
  
  /* Add a header with the page URL and date */
  .print-header::before {
    content: "Printed from: https://example.com/article on " 
             attr(data-print-date);
    display: block;
    font-size: 8pt;
    color: #999;
    border-bottom: 1px solid #ccc;
    padding-bottom: 4px;
    margin-bottom: 12px;
  }
}

@media screen {
  .print-only {
    display: none !important;
  }
}

Use classes like .print-only and .screen-only to control visibility. You can dynamically set a print date using JavaScript before the print dialog opens:

// Set the print date before printing
window.addEventListener('beforeprint', () => {
  const header = document.querySelector('.print-header');
  if (header) {
    header.setAttribute('data-print-date', 
      new Date().toLocaleDateString());
  }
});

11. Manage Widows and Orphans

In typography, a "widow" is a single line of text stranded at the top of a page, and an "orphan" is a single line left at the bottom. CSS lets you control the minimum number of lines that should stay together:

@media print {
  p, li {
    widows: 3; /* At least 3 lines at the top of a page */
    orphans: 3; /* At least 3 lines at the bottom of a page */
  }
  
  /* For headings, be stricter */
  h1, h2, h3, h4, h5, h6 {
    widows: 2;
    orphans: 2;
  }
}

The widows property specifies the minimum number of lines that must appear at the top of a page after a break. The orphans property specifies the minimum number of lines that must appear at the bottom of a page before a break. Setting these to 2 or 3 prevents isolated single lines.

Complete Practical Example

Here is a comprehensive print stylesheet you can use as a starting point for any content-heavy website. It incorporates all the best practices discussed above:

/* ============================================
   Complete Print Stylesheet — Ready to Use
   ============================================ */

@media print {
  
  /* --- Global Reset for Print --- */
  *,
  *::before,
  *::after {
    background: transparent !important;
    color: #000 !important;
    box-shadow: none !important;
    text-shadow: none !important;
    filter: none !important;
  }
  
  /* Restore natural color on images */
  img, picture, svg {
    color: auto;
  }
  
  /* --- Typography --- */
  body {
    font-family: Georgia, "Times New Roman", "Liberation Serif", serif;
    font-size: 12pt;
    line-height: 1.6;
    word-spacing: 0.05em;
    letter-spacing: 0.01em;
  }
  
  h1 {
    font-size: 28pt;
    font-weight: bold;
    margin: 20pt 0 12pt;
    page-break-before: always;
    break-before: page;
    page-break-after: avoid;
    break-after: avoid;
  }
  
  h2 {
    font-size: 20pt;
    font-weight: bold;
    margin: 16pt 0 10pt;
    page-break-after: avoid;
    break-after: avoid;
  }
  
  h3 {
    font-size: 16pt;
    font-weight: bold;
    margin: 14pt 0 8pt;
    page-break-after: avoid;
    break-after: avoid;
  }
  
  h4, h5, h6 {
    font-size: 13pt;
    font-weight: bold;
    margin: 12pt 0 6pt;
    page-break-after: avoid;
    break-after: avoid;
  }
  
  p, li, td, th, dd, dt {
    font-size: 11pt;
    line-height: 1.5;
    widows: 3;
    orphans: 3;
  }
  
  blockquote {
    font-size: 11pt;
    margin: 12pt 24pt;
    padding: 8pt 12pt;
    border-left: 3pt solid #999;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  code, pre {
    font-family: "Courier New", Courier, monospace;
    font-size: 10pt;
    background: #f8f8f8 !important;
    border: 1px solid #ddd;
  }
  
  pre {
    padding: 8pt 12pt;
    white-space: pre-wrap;
    word-break: break-word;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  /* --- Hide Irrelevant Elements --- */
  nav,
  .site-header,
  .site-footer,
  .sidebar,
  .aside,
  .comments,
  .comment-form,
  .social-share,
  .social-links,
  .advertisement,
  .ad-banner,
  .promo-banner,
  .cookie-banner,
  .newsletter-signup,
  .related-posts,
  .search-form,
  .pagination,
  .breadcrumbs,
  button,
  input[type="submit"],
  input[type="button"],
  .screen-only {
    display: none !important;
    visibility: hidden !important;
  }
  
  /* --- Reveal Print-Only Content --- */
  .print-only {
    display: block !important;
    visibility: visible !important;
  }
  
  /* --- Links: Show URLs for External Links --- */
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    font-weight: normal;
    color: #666 !important;
    word-break: break-all;
  }
  
  /* Don't show URL for internal links or anchors */
  a[href^="#"]::after,
  a[href^="/"]::after,
  a[href^="mailto:"]::after,
  a[href^="tel:"]::after,
  a[href^="javascript:"]::after {
    content: none;
  }
  
  /* --- Expand Abbreviations --- */
  abbr[title]::after {
    content: " (" attr(title) ")";
    font-size: 9pt;
    color: #666 !important;
  }
  
  /* --- Images & Figures --- */
  img, svg {
    max-width: 100%;
    height: auto;
  }
  
  figure {
    margin: 12pt 0;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  figcaption {
    font-size: 9pt;
    font-style: italic;
    color: #555 !important;
  }
  
  img.decorative,
  img[alt=""],
  .hero,
  .hero-image {
    display: none;
  }
  
  /* --- Tables --- */
  table {
    width: 100%;
    border-collapse: collapse;
    font-size: 10pt;
    margin: 12pt 0;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  th, td {
    border: 1px solid #999;
    padding: 6pt 10pt;
    text-align: left;
    vertical-align: top;
  }
  
  th {
    background-color: #f0f0f0 !important;
    font-weight: bold;
  }
  
  /* --- Page Setup --- */
  @page {
    margin: 2cm 2.5cm;
    size: A4;
  }
  
  @page :first {
    margin-top: 2.5cm;
  }
  
  /* --- Page Break Controls --- */
  h1, h2 {
    page-break-before: always;
    break-before: page;
  }
  
  h3, h4 {
    page-break-before: avoid;
    break-before: avoid;
    page-break-after: avoid;
    break-after: avoid;
  }
  
  /* Keep heading with the following content */
  h2 + p,
  h2 + ul,
  h2 + ol,
  h3 + p,
  h3 + ul,
  h3 + ol,
  h4 + p,
  h4 + ul,
  h4 + ol {
    page-break-before: avoid;
    break-before: avoid;
  }
  
  /* Avoid breaks inside important elements */
  .avoid-break,
  blockquote,
  .callout,
  .important-note,
  .summary-box,
  ul,
  ol {
    page-break-inside: avoid;
    break-inside: avoid;
  }
  
  /* --- Print Header (URL + Date) --- */
  body::before {
    content: "Printed from: " attr(data-print-url) " on " 
             attr(data-print-date);
    display: block;
    font-size: 8pt;
    color: #999 !important;
    border-bottom: 1px solid #ccc;
    padding-bottom: 6pt;
    margin-bottom: 18pt;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  
  /* --- Ensure content fills the page nicely --- */
  article,
  .main-content,
  .article-body {
    max-width: none;
    margin: 0;
    padding: 0;
    width: 100%;
  }
}

And here's the companion JavaScript to set the print header data attributes before printing:

// Set data attributes for the print header
// Run this on page load or before the print event

function setupPrintHeader() {
  document.body.setAttribute('data-print-url', window.location.href);
  document.body.setAttribute('data-print-date', 
    new Date().toLocaleDateString('en-US', {
      year: 'numeric',
      month: 'long',
      day: 'numeric'
    })
  );
}

// Listen for the beforeprint event (works in most browsers)
window.addEventListener('beforeprint', setupPrintHeader);

// Also set on load as a fallback
document.addEventListener('DOMContentLoaded', setupPrintHeader);

// For on-demand print buttons
document.querySelector('.print-button')?.addEventListener('click', () => {
  setupPrintHeader();
  window.print();
});

Testing and Debugging Print Styles

Testing print styles doesn't require killing trees. Modern browsers offer excellent tools for previewing printed output:

When debugging, remember that !important flags in your print styles can make overriding specific rules tricky. Use the browser's element inspector in print emulation mode to see which rules are actually being applied. Watch out for conflicts between your print reset and any inline styles or JavaScript-applied styling.

Common Pitfalls to Avoid

Conclusion

Mastering CSS print styles is an investment that pays dividends in user experience. It transforms your web content from a screen-bound resource into a versatile, medium-agnostic document that works just as well on paper as it does on a display. The techniques covered here — from basic resets and element hiding to sophisticated page break control and URL expansion — give you complete control over the printed representation of your work.

Start by adding a minimal print stylesheet that hides navigation and adjusts typography. Then progressively enhance it with page break controls, table optimizations, and print-only content. Test frequently in print preview mode. The complete example provided above serves as a solid foundation you can adapt to any project. Remember: the best print stylesheet is one your users never notice — because the printed output simply looks exactly as it should.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles