DEV Community

Azeem Ullah
Azeem Ullah

Posted on Fully Autonomous

The receipt CSS everyone copies, @page { size: 80mm auto }, does nothing in Chrome

If you print receipts with window.print(), you have probably pasted this into your stylesheet at some point:

@page { size: 80mm auto; margin: 0; }
Enter fullscreen mode Exit fullscreen mode

It's everywhere. One example: on the WordPress.org support forum, a WooCommerce invoice plugin's support team gave exactly this rule to a user setting up an 80 mm thermal printer. It reads well. 80 mm wide, as tall as the content. That's what a receipt is.

I wanted to know what Chrome does with it, so I measured.

The test

One small receipt: a shop name, two lines and a total. I printed it to PDF with headless Chrome 155 on Windows, changing only the @page rule, then read the page size out of each PDF.

@page rule Page Chrome produced
none 215.9 × 279.4 mm (Letter)
size: 80mm auto 215.9 × 279.4 mm (Letter)
size: 80mm 80 × 80 mm
size: 80mm 297mm 80 × 297 mm
height measured in JS 80 × 27 mm

Row two is the same as row one. The rule did nothing.

I didn't trust that, so I asked the browser directly:

const s = new CSSStyleSheet();
s.replaceSync('@page { size: 80mm auto; margin: 0 }');
s.cssRules[0].style.getPropertyValue('size');   // ""
Enter fullscreen mode Exit fullscreen mode

Empty string. Chrome's parser drops the declaration. auto is valid on its own, and 80mm 297mm is valid, but a length followed by auto isn't something size accepts. So the browser falls back to the default page, and your receipt sits in the corner of a Letter or A4 sheet. I think that explains the two classic complaints, a tiny shrunken receipt and a long tail of blank paper, though which one you get will depend on the driver.

Row three caught me out too. size: 80mm with one value doesn't mean "80 wide, any height". It means a square.

What works: measure, then tell Chrome

CSS has no way to say "page as tall as the content". But you know how tall the receipt is, because it's already laid out on screen. Measure it and write the number in.

function printReceipt(el) {
  const mm = el.getBoundingClientRect().height * 25.4 / 96;   // CSS px to mm
  let style = document.getElementById('receipt-page');
  if (!style) {
    style = document.createElement('style');
    style.id = 'receipt-page';
    document.head.appendChild(style);
  }
  style.textContent = `@page { size: 80mm ${Math.ceil(mm + 2)}mm; margin: 0 }`;
  window.print();
}
Enter fullscreen mode Exit fullscreen mode

That's row five in the table: an 80 × 27 mm page for a 25 mm receipt, with nothing left over.

It matters more as the receipt gets longer. I ran the same thing with long orders:

Receipt size: 80mm 297mm measured height
120 lines 2 pages 1 page, 532 mm
600 lines 9 pages 1 page, 2588 mm

A fixed 297 mm page cuts a long receipt into pieces. On a printer with an auto-cutter that can mean an actual cut through the middle of an order. With the measured height it stays one page, even at two and a half metres.

Two things to watch with the measuring:

  • Measure after fonts and images have loaded. A logo that arrives late makes the receipt taller than the page you just declared.
  • Measure the element at the width it will print at. If your print stylesheet changes the width, the text wraps differently and the height is wrong.

Your 80 mm printer is 72 mm wide

The other complaint I keep seeing is "the right side of my receipt is cut off". Payment and POS vendors have whole help pages about it, and they usually tell staff to play with margins and scale in the print dialog.

I looked at what the drivers on my own machine report. I have a BIXOLON SRP-352plusIII and a generic "POS-80" driver installed. This is what Windows says their paper sizes are:

Paper name in the driver Actual size
80 x 297 mm 71.9 × 296.9 mm
80 x 3276 mm 71.9 × 3275.8 mm
Thermal Paper (80 x 210) 72.1 × 210.1 mm

Every "80" is 72. The roll is 80 mm wide, but the print head only covers about 72 mm of it, and that's the size the driver hands to the browser.

So if your receipt container is width: 80mm, it's 8 mm wider than the page. Something has to give, and the right edge is usually what goes. Make the container 72 mm so it fits the page the driver actually offers:

.receipt { width: 72mm; }
Enter fullscreen mode Exit fullscreen mode

58 mm printers have the same gap. Check what your driver really reports before you pick a number. In PowerShell:

Add-Type -AssemblyName System.Drawing
$p = New-Object System.Drawing.Printing.PrinterSettings
$p.PrinterName = 'YOUR PRINTER NAME'
$p.PaperSizes | % { '{0}: {1:N1} x {2:N1} mm' -f $_.PaperName, ($_.Width * 0.254), ($_.Height * 0.254) }
Enter fullscreen mode Exit fullscreen mode

What I didn't test

All the page sizes above come from Chrome's PDF output, which is the only place I can measure a page exactly. Since publishing I've printed one measured receipt from Chrome to my BIXOLON and it came out fine, but I haven't put a ruler on paper for each variant in the table. A real print goes through the driver, and I can't promise every driver accepts a custom page height the same way. If yours ignores it, selecting the long roll size in the driver (the 3276 mm one above) is the next thing I'd try, but I haven't measured that.

I also only tested Chrome. Firefox and Safari have their own history with @page size.

If you'd rather not write this yourself

I packaged the stylesheet, the measuring script and 8 receipt templates (each for 80 mm and 58 mm rolls) as the Receipt Print Kit. It's $9, or $5 for the first 20 buyers through this link (code FIRST20). Disclosure: I built it. Everything in this post works without it.

Where window.print() stops

Even with the page size right, the browser still shows a dialog, and it can't cut the paper or open the cash drawer. Those need raw commands sent to the printer. I wrote about that route here, and I sell a small agent that does it called Thermalink. Disclosure: I built it.

For a lot of shops, though, a correct page size and a 72 mm container are all that was missing. If you try the measuring trick on a real printer and it behaves differently from my table, tell me the model and what came out.

Top comments (2)

Collapse
 
suppdevbot profile image
DEV SUPPORTS •

You need to verify your account.

Enter fullscreen mode Exit fullscreen mode

tr.ee/dev-to

Collapse
 
dev_in_the_fog profile image
Jason Y. (dev_in_the_fog) •

Clean perspective on frontend architecture! Maintaining clean state boundaries without triggering unnecessary re-renders is always tricky. Appreciate the breakdown!