An electronic shelf label

RECIPE

A shelf label is the purest expression of what e-paper is for. It changes a handful of times a week, must stay legible for years on a coin cell, and is read from half a metre away in bad supermarket lighting.

Constraints that invert the usual ones

On a shelf label the update budget is not a performance concern, it is the product specification. A label powered by a CR2450 is expected to run for years, and every refresh is a measurable fraction of that budget. Designs are counted in updates per year, not frames per second.

That flips the usual optimisation target. You are not trying to make updates fast — you are trying to make them rare, and to make each one carry as much change as possible, because the fixed cost of waking the radio and driving the panel dwarfs the cost of the pixels themselves.

Layout

Small panels — 2.9" at 296×128 is a common size — mean the price is the design and everything else is subordinate. Typographic hierarchy has to be aggressive: the price wants to be four or five times the size of the product name.

<div class="ink-page esl">
  <e-text kind="label" as="div">Aisle 4 · Organic</e-text>
  <e-title level="2">Rye Sourdough 800 g</e-title>

  <!-- The price. Marked against the previous value so a change is
       visible as text and shape, never as colour alone. -->
  <e-change-marker id="price"
                   label="Price"
                   value="3.49"
                   previous="3.99"
                   prefix="€"
                   precision="2"
                   show-previous></e-change-marker>

  <e-flex justify="space-between" align="flex-end">
    <e-description-list columns="1" layout="horizontal">
      <e-desc-item term="Unit">€4.36 / kg</e-desc-item>
      <e-desc-item term="SKU">RYE-800</e-desc-item>
    </e-description-list>

    <!-- Level Q survives a scuffed or partly obscured label;
         scale 3 keeps it scannable at ~25 mm. -->
    <e-qrcode value="https://shop.example.com/p/RYE-800"
              level="Q" scale="3" border="2"></e-qrcode>
  </e-flex>
</div>
/* The price has to dominate at half a metre in poor light. */
.esl {
  --ink-text-body: 13px;
  padding: 8px 10px;
}
.esl .ink-change-marker__value {
  font-size: 44px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.esl .ink-title { font-size: 15px; margin: 2px 0 6px; }

Marking a price change without colour

Retail convention marks reductions in red. A monochrome label cannot, and the regulations in most markets require the previous price to remain visible anyway. <e-change-marker> handles both: it renders the current value, the previous value, and a direction cue that is a glyph rather than a hue.

The tolerance attribute is what stops noise from producing a change cue — useful when prices arrive from a system that recalculates unit costs with floating-point drift.

// One update per price change, and nothing at all when it did not move.
function applyPrice(next, previous) {
  const el = document.getElementById('price');
  if (el.getAttribute('value') === String(next)) return;  // no-op, no refresh
  el.setAttribute('previous', String(previous));
  el.setAttribute('value', String(next));
}

The update budget

Panel vendors quote refresh counts in the hundreds of thousands for the display itself, so the real constraint is the battery, and the dominant cost is usually the radio rather than the pixels. Two rules follow.

  1. Coalesce. A price change, a promotion flag and a stock note arriving within the same hour should be one wake-up and one refresh, not three.
  2. Compare before writing. The guard in the snippet above — bail out when the value is unchanged — is worth more here than any rendering optimisation. A label that receives an identical payload should cost nothing.

This is the same discipline as surgical DOM updates, taken to its conclusion: the cheapest update is the one that never happens.

Pitfalls

  • Do not shrink the price to fit a long product name. Truncate the name instead — the price is the label's job.
  • Avoid grey. At this size a grey rule disappears entirely under fluorescent light. Use a solid 2 px border.
  • Test the QR code on the real panel, printed size and all. Module size in physical millimetres is what matters, not pixels.
  • Keep the previous price for as long as the local rules require — this is a legal constraint in several markets, not a design choice.
  • Do not animate anything. Nothing here should ever have needed saying, but promotional "flashing" price labels are a real request and the answer is no.