Printing with CSS and Media Queries

Approaches to Printing Webpages

By William Hertling

Web strategist, software developer, and science fiction author. Follow him at @hertling.

Developers and designers often tailor web experiences for mobile screens by adapting how we render navigation, content, and images. We also want to do the same for the printed page. As I've discussed in other articles: navigation bars, links, backgrounds, and some images just don't make sense in the context of a printed page. Font choices, line spacing, and other design elements also change for the printed page.

There's a spectrum of ways to handle printing:

  • Do nothing: If nothing at all is done to support printing, the user will often get pretty bad results when printing. One news article should have been about 2 pages but took up 18 printed pages because the webpage's navigational elements were rendered and over many pages. (They should not have been rendered at all in the print file.)
  • Print-specific CSS Browser rendering: To perform the basic job of customizing what gets printed, choosing appropriate print fonts, margin, and layout, most browsers will do fine if provided with appropriate CSS. This is the approach to web page printing that most developers should use. Results from one browser to the next might vary, and not all margin, hyphenation, and other print-relevant rendering will be present, but it will be good enough for many cases.
  • HTML-to-PDF: PDF generation is an approach to web printing I use in my day job at HP because the HTML-to-PDF generation tools (wkhtmltopdf and PrinceXML) do a better job at print-specific rendering than web browsers. And by generating PDFs on the server, you can deliver consistent print results across all desktop and mobile browsers. Usually what happens is that the developer hooks into the File->Print event with Javascript and/or places a prominent Print button on the webpage. The PDF is either generated ahead of time or on the fly. (In most cases a PDF can be rendered in less than a second.)

We have two other articles about the HTML-to-PDF approach: 

Today I'll discuss how to improve the print experience with browser rendering. Browser rendering requires:

  1. Media queries to specify which CSS to use for paged media (aka printing).
  2. CSS to turn off the display of content that is not appropriate for the printed page, such as as navigation bar or page header.
  3. CSS to properly format the remaining content for print (appropriate font, colors, sizes, etc.)

Step 0: Baseline

In a very meta example, I’ll be working with the printing experience of a site I once created for improved printing. We were running on WordPress using the Minn Lite theme with some customization. Some WordPress themes support printing out of the box, but Minn Lite does not. When I printed an article, these were the results:

Baseline Page 1
Page 1: Note the wasted space due to the top banner and fixed column width.
The background and banner both waste ink when printing.
Baseline Page 2
Page 12: There’s no need for a comment entry form on a printed page. Notice also how the info box that floats to the right doesn’t quite fit in the available space.
Baseline Page 3
Page 13: Whether comments should be visible or not depends on the specific use case, but if they do appear, they should be formatted reasonably, and definitely not with an all black background.

Here’s the full 13-page PDF showing how bad web printing can be without a print style sheet: Before Print CSS.

Step 1: Media Queries for Paged Media

The first step to create print-specific CSS is use media queries to define the print CSS, just as you would use media queries based on screen size to define a mobile experience.

You could separate the CSS for print and screen into two files and include the print styles using a media selector:

<link rel="stylesheet" href="printstyles.css" type="text/css" media="print" >

Or you can place all of the CSS into one file, and use inline media queries:

@media print {
  .sidebar {
    display: none;
  }
}

In addition to querying for print, we could also query for orientation or page sizes:

@media print and (orientation:landscape) { . . .}
 
@media print and (min-width:4in) and (max-width: 6in) { /* index card styling */ }
@media print and (min-width:7in) and (max-height: 11in) { /* letter-size styling */ }
@media print and (min-width:7in) and (min-height: 13in) { /* legal-size styling */ }

As shown, you may also find it useful to query for page size. How you format, and what content you choose to put on the page might be different if the user has 4×6 photo media or index cards inserted (imagine a photo website or recipe website) vs. a standard letter size or A4 sheet of paper. I’ve found it tricky to determine the exact paper size because of how browsers do margin calculations, but it should be possible to distinguish between smallish things (index cards, photo media) and biggish things (letter size, A4, etc.) You can learn more about media queries.

We’ll use inline media queries for this example.

By the way: One approach is to place all styles inside media queries, so that every style definition is either for screen or print, but not both. The other approach is to define the screen styles first, then let the print styles override them. I tend to use the later approach, but the downside is that you will sometimes run into precedence issues: depending on the specificity used in the screen styles, it can be difficult to override the styles without resorting to the !important flag. (See Understanding Style Precedence in CSS)

Step 2: Turn off unwanted content

We’ll turn off the three main elements we don’t want:

@media print {
  /* turn off big blue header */
  div#header-container { display:none; }
 
  /* turn off comment form */
  div#social { display: none; }
 
  /* turn off recent comments */
  .footer-widget-container { display: none; }
}

If you have any  <video>, <audio>, <object> and <embed>, you’ll definitely want to turn those off as they don’t make sense in the context of print.

  video, audio, object, embed { display: none; }

Step 3: Set up the page and defaults

After getting rid of what we don’t need, it’s good to set up some general defaults. The styles below set up a default margin (to help get some consistency from one browser to the next) and choose typography that’s appropriate for print (serif font, normal size, generous line height to aid with reading across the page).

@page {
  margin: 0.5in;
}
 
body {
  font-size: 11pt;
  font-family: Georgia, "Times New Roman", Times, serif;
  line-height: 1.3;
}

Step 4: Format what’s left

Now we can look at the current state and see what’s needed.

The first page no longer has a blue banner wasting space and ink at the top of the page. The column width still needs to be fixed, as does the background. The column also appears to have a border, and we’ll want to eliminate that.

Almost formatted for print

The last page has the comments and comment entry form removed, which is good. But the About box is still hanging out there.

Format what&#039;s left

The following CSS will take care of the column width, padding, and margins, so that the main content takes up the majority of the page:

  body.two-col-r main.content { 
    width: 100%;
    border: 0px;
    padding: 0px;
  }

  body.two-col-r div#container {
    padding: 0px;
    margin: 0px;
  }

After getting the content to be full-width, the next issue that becomes apparent is that some images are too big for general-purpose printing. Unless a user’s intent is specifically to print photos, most users prefer that images be kept to a modest size to conserve ink/toner. Exceptionally large images also cause more page breaks when an image can’t fit in the remaining space, which uses more paper and makes the printed output less readable.

Limit image size

So we’d like to limit the image size. Unfortunately, it’s hard to do this in one way that’s perfect for all sites and all content. For the example page I’m printing, there is a diagram on page 1 which should ideally be printed full-size, and and many example photos which preferably would be shrunk a little when printed. Using CSS classes to target particular types of content will help, if those classes are used consistently. If not, something like the following CSS will help in many cases:

img {
  max-width:3.5in;
}

The last thing we need to address is the About box, which is originally a right-hand column aside, and which has floated down to the last page. Because it’s wrapped in several layers of divs, the easiest thing to probably do is hide the original About box, and replicate the content using the :before CSS selector. The :before and :after pseudo-selectors are very useful for inserting new content, and you can read more about them on the Mozilla Developer Network.

  aside#primary-sidebar { display: none; }
 
  main.content:before {
    display: block;
    content: "Print Developers Community is a place to find information and discussions on implementing printing in software. Sponsored by HP, but open to all, find us at hp.com/developers";
    margin-bottom: 10px;
    border: 1px solid #bbb;
    padding: 3px 5px;
    font-style: italic;
  }

Formatting Links

I have not done any special formatting for links, although there are several approaches to consider. You can read more about these approaches in the excellent article How to Set Up a Print Style Sheet by Smashing Magazine. Some of the options include:

Eliminate any special visual formatting, because printed links aren’t actionable.
Reformat the links to show the destination URL visibly. (The Print Style Sheet article explains how to do this.)
Leave the links as is.

I personally prefer to leave the links as are, if there’s a modest number of them. It can be nice to know where the links were — because it gives me some tips of where I could get some more info. But I’m not so interested that I want to disrupt the page with long printed links I’m never going to enter. The most likely case is that I’ll want to google some terms if I care to go read more.

An even better solution can be created with HP Link Technology, which would allow you to replace URLs with short, mobile-keyboard friendly versions, and/or watermark the printed page so that users can simply hover over the page with their smartphone to see the links and visit their destinations. That’s out of scope for this tutorial, but you can find out more information here at HP Link Technology API.

Conclusion

Using CSS media queries, developers can easily determine when a webpage is being printed, rather than displayed on a desktop or mobile screen, and customize the design accordingly. As we’ve shown in this and other articles, designing for print means eliminating unneeded navigational elements, cleaning up formatting, and choosing appropriate typography and image sizes.

This is the resulting PDF from less than an hour of CSS work: After Print CSS. Here’s the original PDF, so you can see the difference: Before Print CSS. The printed output shrank from 13 pages to 9, made better use of the full page size, and reduced the size and number of images to conserve ink.

Here’s the final CSS for this site to enable printing:

@media print {
  div#header-container { display:none; }
  div#social { display: none; }
  .footer-widget-container { display: none; }
  div#toc_container { display: none; }
  aside#primary-sidebar { display: none; }
 
  @page {
    margin: 0.5in;
  }
 
  body {
    font-size: 11pt;
    font-family: Georgia, "Times New Roman", Times, serif;
    line-height: 1.3;
  }
 
  body.two-col-r main.content {
    width: 100%;
    border: 0px;
    padding: 0px;
  }
 
  body.two-col-r div#container {
    padding: 0px;
    margin: 0px;
  }
 
  img {
    max-width:3.5in;
  }
 
  main.content:before {
    display: block;
    content: "Print Developers Community is a place for information and discussions about implementing print in software. Sponsored by HP, but open to all, find us at hp.com/developers.";
    margin-bottom: 10px;
    border: 1px solid #bbb;
    padding: 3px 5px;
    font-style: italic;
  }
}

 

More from Will

Code Tutorials

Here are Will's detailed code tutorials that we have migrated to Print Fundamentals for Developers on the HP Developers' Portal

Will's blog and forum posts