How to Create Printer-friendly Pages with CSS

In this article, we review the art of creating printer-friendly web pages with CSS.

“Who prints web pages?” I hear you cry! Relatively few pages will ever be reproduced on paper. But consider:

  • printing travel or concert tickets
  • reproducing route directions or timetables
  • saving a PDF for offline reading
  • accessing information in an area with poor connectivity
  • using data in dangerous or dirty conditions — for example, a kitchen or factory
  • outputting draft content for pen annotations
  • printing web receipts for bookkeeping purposes
  • providing documents to those with disabilities who find it difficult to use a screen
  • printing a page for your colleague who refuses to use this newfangled t’internet nonsense.

The Web and apps can’t cover all situations, but printing pages can be a frustrating experience:

  • text can be too small, too large, or too faint
  • columns can be too narrow or overflow page margins
  • sections are cropped or disappear entirely
  • ink is wasted on unnecessary colored backgrounds and images
  • link URLs can’t be seen
  • advertisements are shown which could never be clicked!

Developers may advocate web accessibility, yet few remember to make the printed web accessible.

Converting responsive, continuous media to paged paper of any size and orientation can be challenging. However, CSS print control has been possible for many years, and a basic stylesheet can be completed within hours. The following sections describe well-supported and practical options for making your pages printer-friendly.

Print Stylesheets

Print CSS can either be:

  1. Applied in addition to screen styling. Taking your screen styles as a base, the printer styles override those defaults as necessary.
  2. Applied as separate styles. The screen and print styles are entirely separate; both start from the browser’s default styles.

The choice will depend on your site/app. Personally, I use screen styles as a print base for most websites. However, I have used separate styles for applications with radically different outputs — for example, a conference session booking system which displayed a timetable grid on-screen but a chronological schedule on paper.

A print stylesheet can be added to the HTML <head> after the standard stylesheet:

<link href="main.css" />
<link media="print" href="print.css" />

The print.css styles will be applied in addition to screen styles when the page is printed.

To separate screen and print media, main.css should target the screen only:

<link media="screen" href="main.css" />
<link media="print" href="print.css" />

Alternatively, print styles can be included within an existing CSS file using @media rules. For example:

/* main.css */
body {
  margin: 2em;
  color: #fff;
  background-color: #000;
}

/* override styles when printing */
@media print {
  body {
    margin: 0;
    color: #000;
    background-color: #fff;
  }
}

Any number of @media print rules can be added, so this may be practical for keeping associated styles together. Screen and print rules can also be separated, but it’s a little more cumbersome:

/* main.css */

/* on-screen styles */
@media screen {
  body {
    margin: 2em;
    color: #fff;
    background-color: #000;
  }
}

/* print styles */
@media print {
  body {
    margin: 0;
    color: #000;
    background-color: #fff;
  }
}

Testing Printer Output

It’s not necessary to kill trees and use horrendously expensive ink every time you want to test your print layout! The following options replicate print styles on-screen.

Print Preview

The most reliable option is the print preview option in your browser. This shows how page breaks will be handled using your default paper size.

Alternatively, you may be able to save or preview the page by exporting to a PDF.

Developer Tools

The DevTools can emulate print styles, although page breaks won’t be shown.

In Chrome, open the Developer Tools and select More Tools, then Rendering from the three-dot icon menu. Change the Emulate CSS Media to print at the bottom of that panel.

In Firefox, open the Developer Toolbar (Shift + F2) and enter media emulate print. Print emulation doesn’t remain active between page refreshes, but press the up cursor key followed by enter to re-execute the command.

Hack Your Media

Presuming you’re using a <link> tag to load printer CSS, you could temporarily change the media attribute to screen:

<link href="main.css" />
<link media="screen" href="print.css" />

Again, this will not reveal page breaks. Don’t forget to restore the attribute to media="print" once you finish testing.

Remove Unnecessary Sections

Before doing anything else, remove and collapse redundant content with display: none;. Typical unnecessary sections on paper could include navigation menus, hero images, headers, footers, forms, sidebars, social media widgets, and advertising blocks (usually anything in an iframe):

/* print.css */
header, footer, aside, nav, form, iframe, .menu, .hero, .adslot {
  display: none;
}

It may be necessary to use display: none !important; if CSS or JavaScript functionality is showing/hiding elements on demand. Using !important isn’t normally recommended but we can justify it here!

Linearize the Layout

It pains me to say this, but neither Flexbox nor Grid play nicely with printer layouts in any browser. That may eventually be addressed but, for the moment, set all layout boxes to display: block;. It may also be necessary to apply width: 100%; on some elements to ensure they span the full page.

Printer Styling

Printer-friendly styling can now be applied. Recommendations:

  • Ensure you use dark text on a white background.
  • Consider using a serif font, which may be easier to read.
  • Adjust the text size to 12pt or higher.
  • Modify paddings and margins where necessary. Standard cm, mm, or in units may be more practical.

Further suggestions include …

Adopt CSS Columns

Standard A4 or US letter paper can result in longer and less readable lines of text. Consider using CSS columns in print layouts. For example:

/* print.css */
article {
  column-width: 17em;
  column-gap: 3em;
}

In this example, columns will be created when there’s at least 37em of horizontal space. There’s no need to set media queries; additional columns will be added on wider paper.

Use Borders Instead of Background Colors

Your template may have sections or call-out boxes that are denoted by darker or inverse color schemes:

A callout box on-screen

Save ink by representing those elements with a border:

A callout box when printed

Remove or Invert Images

Users won’t want to print decorative or other non-essential images. You could consider a default where all images are hidden unless they have a print class:

/* print.css */
img, svg {
  display: none;
}

img.print, svg.print {
  display: block;
  max-width: 100%;
}

Ideally, printed images should use dark colors on a light background. It may be possible to change HTML-embedded SVG colors in CSS but there will be situations where you have darker bitmaps:

A dark chart

A CSS filter can be used to invert and adjust colors in the print stylesheet. For example:

/* print.css */
img.dark {
  filter: invert(100%) hue-rotate(180deg) brightness(120%) contrast(150%);
}

The result:

A light chart

Add Supplementary Content

Printed media often requires additional information which would not be necessary on-screen. The CSS content property can be employed to append text to ::before and ::after pseudo-elements. For example, display the URL in brackets immediately after a standard link:

/* print.css */
a[href^="http"]::after {
  content: " (" attr(href) ")";
}

Append a message:

/* print.css */
main::after {
  content: "Copyright site.com";
  display: block;
  text-align: center;
}

For more complex situations, consider using a class such as print-only on elements which should only be visible when printed. For example:

<p class="print-only">Article updated on 11 July 2018. Please see https://site.com/page for the latest version.</p>

The CSS:

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

@media print {

  /* visible when printed */
  .print-only {
    display: block;
  }
}

Note: most browsers display the URL and current date/time on the printed page’s header and/or footer.

Page Breaks

The CSS3 properties break-before and break-after allow you to control how page, column, or region breaks behave before and after an element. Support is good, although Firefox only permits the deprecated — but very similar — page-break-before and page-break-after properties.

The following before and after values have good cross-browser support:

  • auto: the default; a break is permitted but not forced
  • avoid: avoid a break on the page, column or region
  • avoid-page (break-* only): avoid a page break
  • page (break-* only): force a page break
  • always: an alias of page supported in page-break-*
  • left: force page breaks so the next is a left page
  • right: force page breaks so the next is a right page.

For example, here’s how to force a page break immediately prior to any <h1> heading:

/* print.css */
h1 {
  page-break-before: always;
  break-before: always;
}

The break-inside and older page-break-inside properties specify whether a page break is permitted inside an element. The commonly-supported values:

  • auto: the default; a break is permitted but not forced
  • avoid: avoid an inner break where possible
  • avoid-page (break-inside only): avoid an inner page break where possible

To prevent page breaks occurring within a table of data:

/* print.css */
table {
  page-break-inside: avoid;
  break-inside: avoid;
}

The widows property specifies the minimum number of lines in a block which must be shown at the top of a page. Imagine a block with five lines of text. The browser wants to make a page break after line four so the last line appears at the top of the next page. Setting widows: 3; breaks on or before line two so at least three lines carry over to the next page.

The orphans property is similar to widows, except it controls the minimum number of lines to show at the bottom of a page.

The box-decoration-break property controls element borders across pages. When an element with a border has an inner page break:

  • slice: the default, splits the layout. The top border is shown on the first page and the bottom border is shown on the second page.
  • clone: replicates the margin, padding, and border. All four borders are shown on both pages.

Finally, CSS Paged Media (@page) has limited browser support but provides a way to target specific pages so alternative margins or breaks can be defined:

/* print.css */

/* target all pages */
@page {
  margin: 2cm;
}

/* target the first page only */
@page :first {
  margin-top: 6cm;
}

/* target left (even-numbered) pages only */
@page :left {
  margin-right: 4cm;
}

/* target right (odd-numbered) pages only */
@page :right {
  margin-left: 4cm;
}

The CSS page break properties can be placed within your screen or print styles because they only affect printing.

Unfortunately, page break control is little more than a suggestion to the browser. There is no guarantee a break will be forced or avoided, because layout is limited to the confines of the paper.

Printing Pains

Control over printing web media will always be limited and results can vary across browsers. That said:

  • printer-friendly stylesheets can be retro-fitted to any site
  • it’s unlikely to affect or break existing functionality
  • the development costs are minimal.

Adding a few page breaks and removing unnecessary sections will delight users and raise your site above competitors. Add it to your to-do list!

Sponsors