Print Stylesheets for Data Tables

Permalink to "Print Stylesheets for Data Tables"

Printing is unglamorous but real: auditors print reconciliations, managers print weekly reports, and many users with low vision or cognitive disabilities find paper easier to read than a dense screen. Data tables print badly by default. Wide grids are clipped at the right margin, sticky headers print once on page one, row striping and status colours vanish because browsers drop background colours, and scroll containers print only their visible part.

This guide belongs to data export & print.

Spec reference

Permalink to "Spec reference"

There is no WCAG criterion about printed output, but printing is how many users consume the same content, and the principles carry over:

  • SC 1.4.1 Use of Color (A) — browsers remove background colours by default when printing, so colour-only status disappears entirely on paper.
  • SC 1.3.1 Info and Relationships (A) — headers must be identifiable on every page; CSS display: table-header-group repeats thead across pages in all major browsers.
  • SC 1.4.10 Reflow (AA) and SC 1.4.4 Resize Text (AA) — on screen, the same reflow techniques that help zoom users help printing: content that fits a narrow width prints without clipping.
  • CSS Paged Media — @page size and margins, break-inside: avoid on rows.
How a table prints with and without a print stylesheet Mock showing a printed page: header row repeated, status as text, no clipped columns, and a caption line stating filters and date. How a table prints with and without a print stylesheetInvoiceCustomerStatusAmount (EUR)INV-1042Lund & CoOverdue1,240.00INV-1043Mesa FoodsPaid310.50INV-1044Arden LtdOverdue88.00Page 2 of 5 — header row repeated; "Overdue" printed as text rather than a red fill.
Headers on every page and status in words are the two fixes that matter most.

When to use — and when not to

Permalink to "When to use — and when not to"

Add a print stylesheet to any page where users might reasonably print a table: reports, statements, reconciliations, admin lists. Add a visible “Print” button only if printing is a real task; browsers’ own print command is otherwise enough.

Do not try to print a virtualized grid as-is: only rendered rows exist in the DOM. Either render all rows for printing (if the count is reasonable), or route users to a print-optimised view or a PDF.

The misapplication: @media print { .grid { overflow: visible; } } and nothing else. Wide tables then overflow the page edge, clipped and unreadable.

Default printing versus a print stylesheet Comparison: default printing loses headers after page one, clips wide tables and drops colour status; a print stylesheet repeats headers, fits or rotates and prints status as text. Default printing versus a print stylesheet✗ Default printingHeaders on page one onlyRight-hand columns clippedStatus colours disappearToolbars and buttons printedNo record of filters or date✓ With a print stylesheetHeader row on every pageLandscape page, smaller type, or column subsetStatus words always visibleInteractive chrome hiddenCaption: filters, sort and date

Annotated code example

Permalink to "Annotated code example"
@media print {
  @page { size: A4 landscape; margin: 12mm; }
  /* Remove interactive chrome */
  .toolbar, .pagination, .row-actions, .site-header, .site-footer { display: none !important; }
  /* Undo screen scroll containers so every row prints */
  .table-scroll { overflow: visible !important; max-height: none !important; }
  /* Header row repeats on every page (SC 1.3.1 by analogy) */
  thead { display: table-header-group; }
  tfoot { display: table-footer-group; }
  tr, td, th { break-inside: avoid; }
  table { font-size: 9pt; border-collapse: collapse; width: 100%; }
  th, td { border: 0.5pt solid #000; padding: 2pt 4pt; color: #000 !important; }
  /* Status must not depend on background colour (SC 1.4.1) */
  .status-badge { background: none !important; border: 0.5pt solid #000; }
  .print-caption { display: block !important; font-size: 9pt; margin-bottom: 4pt; }
  a[href]::after { content: none; }                     /* avoid URL clutter in cells */
}
.print-caption { display: none; }
<!-- Print-only context: what this printout is (hidden on screen) -->
<p class="print-caption">Invoices — Status: Overdue — sorted by Due date, oldest first — printed 2 October 2026</p>
<div class="table-scroll" tabindex="0" role="region" aria-label="Invoices">
  <table>…</table>
</div>

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"

Printing is triggered with Ctrl+P (Cmd+P) or a Print button; the print dialog is browser UI and is announced by the screen reader. The considerations are mostly about the page:

Event Expected behaviour Notes
Ctrl+P Browser print dialog Do not intercept unless offering a custom print view
Print button Calls window.print(); name: “Print invoices table” Keep focus on the button after the dialog closes
Screen reader user prints for a sighted colleague Output matches what they filtered The print caption confirms filters
Print preview Header row on every page Verify in Chrome, Firefox and Safari
Browser support for print table features Matrix of Chrome, Firefox and Safari against repeating thead, break-inside on rows, and @page size. Browser support for print table featuresRepeat theadbreak-inside: avoid on tr@page sizeChrome / EdgeYesYesYesFirefoxYesYesYesSafariYesMostlyPartial
All three repeat table headers; row breaking control varies slightly.

Integration context

Permalink to "Integration context"

The same structural decisions that make tables reflow on screen help them print — see meeting reflow 1.4.10 with wide tables and sticky headers and first columns, whose sticky positioning must be disabled in print. For long or formal reports, a tagged PDF may suit better: generating accessible PDF reports from data views.

Gotchas

Permalink to "Gotchas"

Sticky headers print in the wrong place. position: sticky can leave a header stuck mid-page in print. Reset to position: static in the print stylesheet.

Dark mode. Users printing from a dark theme get dark backgrounds that browsers then drop, leaving light text on white. Force color: #000 in print.

Totals rows. If totals live in tfoot, display: table-footer-group repeats them on every page in some browsers — usually not what you want for a grand total. Put grand totals after the table, or accept the repeat with a clear label.

Framework notes

Permalink to "Framework notes"

Grid libraries render their own DOM, which print styles must target. AG Grid offers a print layout (api.setGridOption('domLayout', 'print')) that renders all rows without virtualization — switch to it before window.print() and back afterwards. MUI X Premium has printOptions on its export; it opens a print window with all rows. For Angular Material tables, standard CSS works because the DOM is a real table.

// AG Grid: print every row, then restore
api.setGridOption('domLayout', 'print');
setTimeout(() => { window.print(); api.setGridOption('domLayout', 'normal'); }, 0);

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Ship print styles with the table component and a printCaption prop, so every product’s tables print consistently. Status badges should always include text, which makes them print-safe by design.

FAQ

Permalink to "FAQ"
Should I add a Print button?

Only if printing is a common task for the page. Browser print works everywhere; a button adds discoverability for users who do not know Ctrl+P.

How do I handle tables wider than a landscape page?

Reduce font size moderately, drop non-essential columns in print with a class, or print a narrower “report” view. Never let columns clip silently.

Can screen reader users check what will print?

Print preview is visual. The print caption and a “What will print” summary near the Print button help: “Prints 240 rows, 5 columns, landscape.”

Do printed tables need row numbers?

For long printouts, a row number column helps people refer to rows in conversation. Add it in print only if it would clutter the screen.

Should print output use the user's chosen theme or zoom?

Print should ignore the dark theme (force dark text on white) but respect the browser’s print scaling, which users adjust in the print dialog. Avoid fixed pixel font sizes in print styles; points or relative units scale better.

Permalink to "Related"

← Back to Data Export & Print