Make a page print properly

Somebody needs a paper copy of an invoice, and the browser's print preview shows a dark background, a navigation bar, a cookie banner and a table cut in half across two pages. Deck ships 64 rules for exactly this, so most of that is already handled. This page is what it does for you and the five classes for the decisions it cannot make.

Try printing first

Press Ctrl+P on a Deck page before changing anything. Everything in this list already happened:

  • The palette is forced to light. Dark mode is a screen idea. Paper is white, ink is black, and the whole token set is redeclared inside @media print so a dark page does not print as a black rectangle.
  • Shadows, blurs, animations and transitions are gone. A shadow on paper is a grey smudge that costs toner.
  • Chrome is removed. Nav bars, tab bars, drawers, mega menus, speed dials, banners and the back-to-top button are all hidden.
  • Type is resized in points. Body text becomes 10.5pt and headings scale down with it, because a 16px heading is not a paper heading.
  • Headings do not end a page. break-after: avoid on every heading level, and break-inside: avoid on paragraphs, list items, figures and stats.
  • Table headers repeat. thead becomes table-header-group, so a long table carries its column names onto every page. Rows are kept whole.
  • Link destinations are printed. An external link gets its URL in small grey text after it, because a printed link is otherwise a dead end.
  • Deliberate fills survive. Badges, chips, avatars, icon tiles and chart series keep their colour through print-color-adjust: exact — a status badge that prints white is a status badge that means nothing.
  • Mobile card fallbacks are switched off. A narrow print width would otherwise trigger the phone layout that restacks a table into cards, which is a screen behaviour and wrong on A4.

The five classes

These are the choices Deck cannot guess: what is chrome on your page, and where a page break belongs in your document.

Classes, generated from the stylesheet source
Class Layer What it does Source
.no-print deck.utilities Sets display. 09-utilities.css:240
.print-only deck.print Hidden on screen, shown on paper 99-print.css:197
.page-break deck.print Sets break-before, page-break-before. 99-print.css:186
.keep-together deck.print Sets break-inside, page-break-inside. 99-print.css:188
.no-print-url deck.print No declarations. 99-print.css:125
.no-print
Anything on screen that is not on paper. Your own filter bar, a "back to list" link, a live chat widget, the action buttons above a report.
.print-only
The reverse, and the one people forget. A letterhead, a signature line, a page of terms, the "this is a copy" watermark — things that belong on the paper and would be clutter on the screen.
.page-break
Force a new sheet before this element. One per major section of a long report; not between every card.
.keep-together
Never split this across two pages. A signature block, an address, a total, a small table. Use it on things that are meaningless in halves.
.no-print-url
Suppress the printed URL after a link. For a link whose text already is the address, or a link into your own app that a reader cannot use from paper. Buttons and in-page anchors are already exempt.
<div class="print-header print-only">
  <img src="/letterhead.svg" alt="Ledgerly">
</div>

<div class="bar no-print">
  <button class="btn">Export</button>
  <button class="btn btn-primary" onclick="print()">Print</button>
</div>

<table class="table">…</table>

<div class="keep-together stack stack-2">
  <p class="text-sm">Total due</p>
  <p class="h2">£4,281.16</p>
</div>

<section class="page-break">
  <h2>Terms</h2>
</section>

.print-header and .print-footer exist too, for a block that should sit at the top or bottom of the printed document. Pair either with .print-only — on their own they are visible on screen as well.

Page size and margins

Deck does not set @page, because the right margin depends on whether you are printing onto letterhead. It is one rule in your own layer:

@layer app.base {
  @page {
    size: A4;
    margin: 18mm 16mm;
  }
  /* wider top margin on the first page, for a pre-printed letterhead */
  @page :first {
    margin-block-start: 45mm;
  }
}

@page takes size, margin and the :first, :left and :right selectors. It does not take arbitrary CSS, so a header on every page is not something you can do here — browsers put their own header and footer there, and the reader controls it in the print dialogue.

Use physical units for print. mm, cm and pt mean something on paper; rem and vw do not, and vh is meaningless when the viewport is a sheet.

Testing it

The print preview dialogue is the real test, but it is slow to iterate in. In Chrome dev tools you can hold the page in print mode while you edit: three-dot menu, More tools, Rendering, then Emulate CSS media type: print. Firefox has a printer icon in the Inspector's toolbar that does the same.

  • Check page two. Most print bugs are on the second sheet: a repeated header that is not repeating, a table row split in half, a heading stranded at the bottom of page one.
  • Print to PDF rather than paper while iterating, then check one real print before you ship. Printers disagree with PDF renderers about margins.
  • Look at the printed URLs. If a table of internal links has turned into a wall of grey text, that is what .no-print-url is for.
  • Try it in dark mode. The palette is forced light, and this is the quickest way to confirm nothing in your own CSS is fighting that.
  • Count the pages. A report that was three sheets and is now nine usually has a .page-break on a repeated element.

Next

The printing component page has the full class table and the complete list of what is hidden. If you are building a document that is mostly meant for paper, table and data grid both document their own print behaviour, which is where the fiddly parts are.