Printing the web - in colors

Surely you should be aware of the environment, but some things might still require to be put on a piece of paper. Most browsers do a lot out of the box to conserve the use of ink, which is good when your website is not optimized for printing, but not in this case. This blogpost is based on a recent project. One task was to build an event calendar to represent a lot of different data in colors and be printable as well.

Limitations

Most browsers do some sanitation of the webpage you are about to print. I'm interested in taking a closer look at colors and how they are handled.

You can print colored text, borders and images - but not backgrounds. This makes perfect sense to let the browser handle in the case where you have a dark-colored background with light colored text on and the website author didn't do anything to improve printing. This is at least my interpretation on why this is done in the browser. If we use the previous example and decide that the background color is pitch black and that the text is crisp white and remove the background color, we would be printing with invisible ink. Cool!

Depending on what browser you use this text will be grayed or black.

Workarounds

Chrome among other browsers have a setting to allow you to print the webpage in color. This setting, however, is not something you can rely on the website visitor to know about, let alone actually use.

Billede1

Fortunately this setting can be invoked in CSS.

-webkit-print-color-adjust: exact; 

Any element with this CSS property will be printed as seen on screen. Unfortunately -webkit-print-color-adjust is experimental and the outlooks for general support is unknown. It’s a draft in the CSS Color Module Level 4, but we will have to wait patiently for it to be adapted by various browsers. It’s still very early in its use and it’s not even mentioned by caniuse.com

There's the option to render the webpage as a PDF and by doing so using backgrounds. This, however, is not covered in this blogpost. We thought it would be too extensive a task for our case.

Using images

There’s a few experimental ways of achieving our goal. Another approach could be using images. But we can only print images when they're inlined in the document, not used as image-backgrounds in CSS. We could position the image behind the text making it appear as a background. Let’s first make the grounds for an idea we would like to implement. We want a table where the table header is white on black and all the content black on white. Therefore we would need to include an image in the table header cells.

This, however, is not enough. Since we need to target the image and the text explicitly, we also need a dom element for the text itself. Let’s just go with a span for now. The table would then look like this.

<table>
  <thead>
    <tr>
      <th>
        <img src="black.png"/>
        <span>Header</span>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr><td>Content</td></tr>
  </tbody>
</table>

​Start by resetting the coordinates with a position:relative on th. Then you can position the img absolute. In order to make the image take up all the space of the parent container and act as background, set top, left, bottom and right to zero. The last thing you need to do is z-index the text in the container. In order to do so you need to position it relative.

The CSS should look somewhat like this.

th {
  position: relative;
}
th img {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1;
}
th span {
  position: relative;
  z-index: 2;
}

​The only problem remaining is the white text will not be printed white. In our case we didn't print on pitch black background, so using another color than white wasn't really a problem.
I haven't yet found a solution to printing the color white, but one could be rendering the text as part of the image, this would however raise a new bunch of issues.

Refining the idea

You can benefit from replacing the use of PNG's or GIF's by SVG's.
Rendering background colors with images solves the problem in most browsers. But is it worth the extra HTTP requests? Take it even further. Make the SVG part of your dom - inline the SVG images.

When you inline the SVG it shines. SVG is now a part of the DOM and you can even target its elements with CSS selectors. The properties, however, are slightly different. Instead of background color we would use fill.
There's a lot of other perks using inline SVG's which will be covered in a future blogpost.

Case event calendar

The calendar that we built looks like this.Billede2

 

Each cell in this table consists of one or more inline SVG’s representing weekend, start of event (triangle pointing left), ongoing event, end of event (triangle pointing right) or header. The type of event is defined by colors and because we can alter this with CSS,  we essentially only need three different images.

<svg viewBox="0 0 100 100">
  <polygon points="100,0 100,100 15,100" />
</svg>

<svg viewBox="0 0 100 100">
  <rect width="100" height="100"></rect>
</svg>

<svg viewBox="0 0 100 100">
  <polygon points="85,0 0,100 0,0"></polygon>
</svg>

Then all what is left to do is add some classes to the SVG’s and add the needed styles/colors.

The print dialog now gracefully shows colors without the setting “background graphics”

Billede 3

Few thoughts on performance

In the above mentioned case, each event is represented once per day it occurs. Potentially this means that the amount of images displayed in the table could be way over 365. The initial thought was to have only one representation of each event if the event starts and ends within the same month, that is. It could possibly be achieved computing the viewBox values and building the polygons together and creating a parallelogram instead, but that would probably raise some new challenges. The chosen way is simple, performs okay and is way easier to control going responsive.

Final words

Printing colors using images is a hack. It works regardless of which browser you use, which is why we decided to go this way. The CSS property color-adjust will be the right way to go when it’s adopted by common browsers.

Do think twice before printing this article :)