DEV Community

Daniel Pertu
Daniel Pertu

Posted on

Our stylesheet has no print rules, so the one page type our users actually print comes out with the headings fainter than the body text

Our product is used by people who run pub quizzes. Those people print things. Not the app, but the guides: a running order, a checklist for the hour before, a page to put on a clipboard behind the bar.

Our site is dark. <html class="dark">, background 0 0% 9%, foreground 0 0% 98%, body copy at 0 0% 60%. Nobody on the team had ever pressed Cmd+P on it.

So I did, with the printer being Chrome's own PDF writer driven over the DevTools protocol, which is the same rendering path the print dialog uses.

The stylesheet first

One request, because every page loads the same file:

curl -s https://pub-trivia.app/_next/static/chunks/3lcp5uer5uji0.css | wc -c
86718
Enter fullscreen mode Exit fullscreen mode

In those 86,718 bytes there are 13 media queries. Eleven are min-width, two are hover: hover. The count of @media print rules is zero, and the count of print-color-adjust declarations is also zero. Nothing in the design system has an opinion about paper.

What came out of the printer

Two pages, A4, printed twice each: once with background graphics off, which is Chrome's default, and once with them on.

The how-to-host guide is six A4 pages. The privacy policy is three.

With backgrounds off, the guide is legible, which already surprised me. Our body text is rgb(153, 153, 153) on screen and our headings are rgb(250, 250, 250), so a naive prediction is near-white text on white paper and a blank sheet. That is not what happens, because Chrome does not hand your colours to the paper unchanged when it is not painting your background.

I decoded the PNG of each printed page and sampled the darkest pixel in each band of text:

element colour on screen colour on paper contrast on white
h1 rgb(250, 250, 250) rgb(166, 166, 166) 2.4:1
h2 rgb(250, 250, 250) rgb(166, 166, 166) 2.4:1
body paragraph rgb(153, 153, 153) rgb(69, 69, 69) 9.6:1

Read that column twice. On screen the headings are the brightest thing on the page and the body copy is deliberately dimmed, which is the normal way to build a dark reading page. On paper the relationship is reversed. The body copy lands at 9.6:1 and reads beautifully. The headings land at 2.4:1, which fails WCAG AA even at large-text sizes, and on an actual inkjet they look like someone printed them with a dying cartridge.

Chrome rescued both colours, but it rescued them towards each other and then past each other. The further a colour was from readable, the harder the adjustment pushed, so our two text colours swapped places.

The version with backgrounds on

A host who notices the faint headings has one obvious lever in the print dialog: the "Background graphics" checkbox. Ticking it does fix the contrast, by printing the page exactly as designed. Every sampled pixel in the margins of that PDF is rgb(23, 23, 23).

That is six A4 pages of near-black ink to get a running order onto a clipboard.

The three pages that print perfectly

Our legal pages are a light island inside a dark site. One line in their layout wraps them in className="light", which is the only use of that class in the app, and it flips the palette for those three pages only.

The same measurement on the privacy policy gives headings at rgb(3, 8, 22) and body text at rgb(53, 57, 69). True black on white, 20:1 for the headings and 11.5:1 for the body, no adjustment needed because none of it was light to begin with.

So the three pages nobody prints are the three that print properly, and the guides written for a clipboard are the ones that need the Background graphics checkbox.

Try it

Open pub-trivia.app/guides/how-to-host-a-pub-quiz and press Cmd+P or Ctrl+P. Look at "The week before" against the paragraph under it. Then open pub-trivia.app/legal/privacy and do the same. One dialog, two different products.

If you want the numbers rather than the impression, Chrome's protocol will print a page for you without the dialog, and the output is a PDF you can decode and sample pixel by pixel. That is the only way I would trust a claim about printed contrast, because the colour in your stylesheet is not the colour that reaches the paper.

What this costs to fix

Not much, which is the embarrassing part. A print block that sets the background to white, the foreground to near-black and the muted colour to a mid grey would take about ten lines, and it would apply to all 72 public pages at once because they all render through the same two components.

The reason it was never written is the ordinary one. We have measured this site's response headers, its link graph, its heading structure, its font loading and its JavaScript bundles. Every one of those audits was done in a browser, because that is where the site is, and a browser never shows you the paper.

One caveat on the numbers above: they are Chrome's. Safari and Firefox each make their own decision about light text on an unprinted background, and I have not measured those. The fix removes the question, which is a decent argument for writing it.

The product does have one artifact that was designed to be printed, our scoresheet generator, and it is fine, because it builds a PDF in the browser with its own geometry and never touches the stylesheet at all. The one thing meant for paper is the one thing that escaped the theme.

Top comments (0)