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 printso 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: avoidon every heading level, andbreak-inside: avoidon paragraphs, list items, figures and stats. -
Table headers repeat.
theadbecomestable-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.
| 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-urlis 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-breakon 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.