Printing HTML & CSS

To internet people, it makes no sense to print websites. Not all people are internet people though and some time ago a requirement to print a report page landed on our road map. Here are the obstacles we encountered.

Print stylesheets are typically a giant list of .menu, .footer, ... { display: none } hiding all navigation and interactive elements. This gets applied on top of the default stylesheet. When I learned CSS, the common practice was to link two CSS files in the correct order:

<html>
    <head>
    ...
    <link href="/assets/normal-screen-stylesheet.css" rel="stylesheet" type="text/css" />
    <link href="/assets/print-stylesheet.css" rel="stylesheet" type="text/css" media="print" />
    </head>
    <body>
    ...
    </body>
</html>

The link tag has a media attribute which defines when the stylesheet should be applied. (The default is under any conditions.) Over the years, the attribute has expanded to accept media queries specifying (typically) the minimal screen size to load an additional stylesheet. Another valid value is print which does pretty much what you expect, applies the style when printing.

We prepared a print stylesheet, let Webpacker compile it, linked it from the template and we ended up with a mess. Webpacker with hot reloading does not respect the media attribute. We solved it by including the print rules in the main stylesheet under a media query declaration. While print is rarely used and it would be preferable to load it only when needed, it’s just a few lines of code in our case, so we can swallow it.

Preview print stylesheet

Now, with a rough print stylesheet loading, we needed to tune it. The normal process would be:

  1. Load the page
  2. Ctrl + P
  3. Check the preview
  4. Close the preview
  5. Change the code
  6. Wait for hot reloading / reload manually
  7. Go to step 2

Being lazy we looked for ways to skip some steps. It turns out to be possible but finding the option is not intuitive. In case you need to preview print stylesheet in Chrome:

  1. Open developer tools
  2. Click on Customize and control DevTools - three vertical dots next to the close button
  3. Open More tools > Rendering
  4. Scroll to the bottom and select print from Emulate CSS media drop down

From our experience, turning it on/off is a bit glitchy but nothing that a page reload could not solve.

Page border

OK, we’re making progress but the whitespace around the content is way too generous. Luckily, it’s one of the things you can control from CSS when printing via @page at-rule. This is the only time I’ve used centimeter as a unit in CSS.

@page {
	margin: 1cm;
}

Line breaks

With the whitespace around reduced we ended up with very unfortunate line breaks - section headings at the end of the page while content is on the next page. If it was a text document intended for print, you’d configure the word processor to not split headings or possibly sprinkled in some page breaks manually. (I’m so glad I haven’t had to do anything similar for years!) In this situation, CSS is willing to take suggestions but gives no promises. We ended up using this rule:

section {
	page-break-inside: avoid;
}

which instructs the browser to avoid placing a section across a page boundary.

We have momentum so let’s figure out why the icons are not being printed. This one was rather tricky. We wasted some time googling issues with printing SVG because the icons which disappeared (e.g. up/down arrows) were SVG icons inserted as background images. Since we don’t have any bitmaps as backgrounds on the page in question, we didn’t realize that the problem is not SVG but being a background. Browsers make a reasonable assumption that background images are not to be printed. In our case, the images indicated a trend and we didn’t want to lose them.

There’s a variety of ways browsers solve this problem. Chrome allows you to set a vendor-prefixed CSS property -webkit-print-color-adjust to include the background image. Unfortunately, Chrome is alone in this. Other browsers including Firefox, Internet Explorer and Opera leave the decision up to the user via a rather complicated sequence of steps and Safari does not allow it in any way.

Since the icons have meaning, we decided to use inline SVG which gets printed across the board.

Tailwind

The final step on our printing journey is an inversion. We’ve started using Tailwind CSS for styling and it’s a wonderful tool. It doesn’t let you do everything you need but gets you 95+% there. One of its features are utilities for CSS pseudo-classes like :hover. For example class hover:text-black makes the text black when the element is hovered. Who knew that a colon is a valid character in a CSS class name?

After having the print stylesheet pretty much done, we were looking at the list of elements being hidden for print an bright idea appeared. Why not define a Tailwind-style CSS class which would invert the behavior. This way, we ended up with:

.print\:hidden {
	display: none;
}

This made our print stylesheet miniscule, 26 lines of generously spaced SASS, at the expense of declaring which elements are not to be printed right in the HTML.

If you happen to come across a legitimate use case for printing a web page, I hope this summary helps.

We're looking for developers to help us save energy

If you're interested in what we do and you would like to help us save energy, drop us a line at jobs@enectiva.cz.

comments powered by Disqus