PDFPipe

Payments received / 80mm roll

80mm thermal receipt HTML template

The point-of-sale receipt printed on an 80mm roll, designed for a print head that is narrower than the paper and has no greys.

When this document is the right one

A sale completes at a till, a kitchen needs an order ticket, or a queue system issues a collection slip. The output goes to a thermal printer rather than to a screen or a laser, and almost every design decision follows from that.

Why each part is there

The fields are visible in the markup. What is not visible is why they are present, which is the part that gets removed first when someone adapts a template for their own use.

  • A content width of 72mm rather than 80mm, because the media is 80mm and the print head is not.
  • A monospace face, because the columns are aligned by character position rather than by a layout engine and because monospace survives a one-bit print head.
  • Solid rules and white space instead of tinted panels, since thermal paper has no greys and dithers them into something that looks like dirt.
  • The total set larger than everything else, which is the only hierarchy the format supports.
  • Fifteen millimetres of blank paper at the foot so the cutter does not slice through the last line.

The template

A complete file. Doctype, stylesheet, body: paste it into a renderer unchanged and it produces a finished 80mm roll page. Replace the sample data and the styling holds.

html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Receipt 004192</title>
<style>
  /* 80mm media, but the print head is 72mm. The 3mm margins keep content
     inside the printable band on essentially every 80mm mechanism. */
  @page { size: 80mm 297mm; margin: 0 3mm; }
  * { box-sizing: border-box; }
  body {
    margin: 0;
    /* Thermal heads are one bit per dot. A monospace face at a size that maps
       to whole dots stays sharp; a hairline serif turns to mush. */
    font: 9pt/1.35 "IBM Plex Mono", "Courier New", monospace;
    color: #000;
    background: #fff;
  }
  .center { text-align: center; }
  .big { font-size: 12pt; font-weight: 700; }
  /* No greys anywhere. Thermal paper dithers them into something that looks
     like dirt and fades within a year. */
  hr { border: 0; border-top: 1px dashed #000; margin: 2mm 0; }
  .rule-solid { border-top: 2px solid #000; margin: 2mm 0; }

  .line { display: flex; justify-content: space-between; gap: 2mm; }
  .line .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .line .val { white-space: nowrap; }
  .qty { padding-left: 4mm; font-size: 8pt; }

  .total { display: flex; justify-content: space-between; font-size: 12pt; font-weight: 700; }
  .code { text-align: center; margin: 4mm 0 2mm; }
  .code img { width: 30mm; height: 30mm; }
  /* Leave blank paper at the foot so the cutter does not slice the last line. */
  .tail { height: 15mm; }
</style>
</head>
<body>
  <div class="center">
    <div class="big">THE BRASS KETTLE</div>
    22 Fore Street, Totnes<br>
    VAT GB 771 4409 22<br>
    Tel 01803 866 112
  </div>

  <hr>
  <div class="line"><span class="name">Receipt</span><span class="val">004192</span></div>
  <div class="line"><span class="name">Table</span><span class="val">7</span></div>
  <div class="line"><span class="name">Server</span><span class="val">Nadia</span></div>
  <div class="line"><span class="name">14/05/26</span><span class="val">13:24</span></div>
  <hr>

  <div class="line"><span class="name">Flat white</span><span class="val">3.40</span></div>
  <div class="qty">2 @ 1.70</div>
  <div class="line"><span class="name">Sourdough toastie</span><span class="val">8.50</span></div>
  <div class="line"><span class="name">Tomato soup</span><span class="val">6.20</span></div>
  <div class="line"><span class="name">Elderflower press</span><span class="val">3.10</span></div>

  <div class="rule-solid"></div>
  <div class="line"><span class="name">Subtotal</span><span class="val">21.20</span></div>
  <div class="line"><span class="name">VAT 20%</span><span class="val">4.24</span></div>
  <div class="total"><span>TOTAL</span><span>25.44</span></div>
  <div class="rule-solid"></div>

  <div class="line"><span class="name">Card ****4021</span><span class="val">25.44</span></div>
  <div class="line"><span class="name">AID</span><span class="val">A0000000031010</span></div>
  <div class="line"><span class="name">Auth</span><span class="val">044182</span></div>

  <div class="code">
    <img src="data:image/png;base64,iVBORw0KGgo=" alt="Order QR code">
    <div style="font-size:8pt">Scan to reorder</div>
  </div>

  <div class="center">
    Thank you<br>brasskettle.example/feedback
  </div>
  <div class="tail"></div>
</body>
</html>

The layout decision worth understanding

The page height is declared as 297mm and is essentially arbitrary: a roll has no fixed length and the paper is cut where the content ends. What matters is the width and the margins. Declaring a height is only necessary because a page box needs two dimensions.

The mistake people make adapting it

Designing this at 80mm because that is what the roll is called. The usable print width on a common 203 dot per inch head is 576 dots, which is 72.1mm, so a layout built to the full 80mm loses a character off the right of every line.

Rendering it

The page box is declared in the template's own CSS as 80mm receipt roll, so the size travels with the markup rather than living in the code that calls the renderer. 80mm receipt roll is not one of the six named formats the API accepts, so set prefer_css_page_size on the request and the renderer will use the page box the document declares.

bash
curl -X POST https://api.pdfpipe.xyz/v1/pdf \
  -H "Authorization: Bearer $PDFPIPE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<!doctype html>...",
    "options": { "prefer_css_page_size": true }
  }' \
  --output thermal-receipt.pdf

Frequently asked

Can I use this template commercially?

Yes. Copy it, change it, ship it. It is sample markup written to be taken, and the sample data in it is invented, so replace the names and the numbers before anyone sees it.

Why is the page size in the CSS rather than in the API call?

Because a thermal till receipt is a 80mm roll document, and that is a property of the document rather than of the code that renders it. Declaring it in the page box means the size cannot be lost between the template and the call, which is the usual way a document ends up on the wrong paper.

Will it look the same in every renderer?

The layout will, because it uses ordinary CSS. The page break behaviour will not: break-inside and break-after are implemented differently by browser engines and by dedicated print engines, and some of the paged media specification is not implemented in browser renders at all. That difference is documented per property in the paged CSS reference.

Other templates

Documents from the same part of a business, and one from each of the other groups.

Paste the template into the playground and get the PDF back. No signup, no key.