PDFPipe

Fulfilment / A4, 21 labels

21-per-sheet address label HTML template

Twenty-one addresses in a fixed 3 by 7 grid on A4, with absolute row heights so the print cannot drift out of the die cuts.

When this document is the right one

A mailing goes out and the labels are printed on an office laser rather than a label printer. The geometry is decided by the label stock, so this template is a reproduction of a die rather than a design.

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.

  • Page margins of 15.15mm top and bottom and 7.25mm at the sides, which are the die's numbers and are not adjustable.
  • Three columns of 63.5mm with a 2.5mm gap, and seven rows of 38.1mm with no gap at all, because the labels touch vertically and do not touch horizontally.
  • Absolute row heights rather than content-driven ones, so a long address cannot push the grid.
  • Overflow hidden on each label, which makes a too-long address a visible fault rather than an invisible drift.
  • Empty labels at the end kept as elements, so a part-used sheet still positions correctly.

The template

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

html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Address labels, 21 per A4 sheet</title>
<style>
  /*
   * The geometry here is fixed by the die that cuts the label stock, not by
   * design: 15.15mm clear at the top and bottom, 7.25mm at each side, three
   * columns of 63.5mm with 2.5mm gaps, and seven rows of exactly 38.1mm with
   * no gap at all. Every one of those numbers has to be reproduced or the
   * print drifts out of the labels down the page.
   */
  @page { size: A4; margin: 15.15mm 7.25mm; }
  * { box-sizing: border-box; }
  body { margin: 0; font: 8pt/1.2 Arial, Helvetica, sans-serif; color: #000; }

  .sheet {
    display: grid;
    grid-template-columns: repeat(3, 63.5mm);
    column-gap: 2.5mm;
    /* Absolute row height. If content is allowed to stretch a row, an error
       of half a millimetre is invisible on label one and 3mm out by label
       twenty-one, which puts the last row on the backing paper. */
    grid-auto-rows: 38.1mm;
  }

  .label {
    padding: 3mm 3.5mm;
    /* Anything that does not fit is clipped rather than pushing the grid.
       A clipped label is a visible fault; a stretched grid ruins the sheet. */
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  .label address { font-style: normal; }
  .label .name { font-weight: 700; }
  .label .postcode { letter-spacing: 0.04em; }

  /* Uncomment to see the die cuts while setting up. Never leave this on for
     a real run: the rules print. */
  /* .label { outline: 0.2mm dashed #ccc; } */

  .blank { }
</style>
</head>
<body>
  <div class="sheet">
    <div class="label">
      <address>
        <span class="name">Ms H Ashworth</span><br>
        41 Sylvan Road<br>
        Exeter<br>
        <span class="postcode">EX4 6EW</span>
      </address>
    </div>
    <div class="label">
      <address>
        <span class="name">Mr D Quinn</span><br>
        Ravenshill Hotel Group<br>
        The Old Rectory<br>
        Bath<br>
        <span class="postcode">BA1 2QP</span>
      </address>
    </div>
    <div class="label">
      <address>
        <span class="name">Accounts Payable</span><br>
        Calder Interiors Ltd<br>
        7 Meridian Court<br>
        Leeds<br>
        <span class="postcode">LS11 5AL</span>
      </address>
    </div>
    <div class="label">
      <address>
        <span class="name">Ms A Ferreira</span><br>
        Sagres Comercio Lda<br>
        Rua do Alecrim 88<br>
        1200-018 Lisboa<br>
        <span class="postcode">Portugal</span>
      </address>
    </div>
    <div class="label">
      <address>
        <span class="name">Dr M Oyelaran</span><br>
        Riverside Academy<br>
        Walcot Street<br>
        Bath<br>
        <span class="postcode">BA1 5BN</span>
      </address>
    </div>
    <div class="label blank"></div>
    <div class="label blank"></div>
  </div>
</body>
</html>

The layout decision worth understanding

grid-auto-rows is set to an absolute 38.1mm rather than to auto. An error of half a millimetre per row is invisible on the first label and 3mm out by the twenty-first, which puts the last row half onto the backing paper. Fixing the height absolutely is the entire technique.

The mistake people make adapting it

Assuming a 21-up A4 sheet and a 30-up Letter sheet are interchangeable because both hold addresses. Different label sizes on different pitches: a template built for one prints across the die cuts on the other and ruins every label on the sheet.

Rendering it

The page box is declared in the template's own CSS as A4 sheet, 21 labels, so the size travels with the markup rather than living in the code that calls the renderer. A4 is also one of the six named formats the API accepts, so it can be passed in the request options instead if that suits your setup better.

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": { "format": "A4" }
  }' \
  --output label-sheet.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 address label sheet is a A4, 21 labels 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.