skip to content

In a design system, why do hairline dividers and centred icons sometimes look blurry on certain screens, and how do specs prevent it?

level: middleimportance: nice to knowfreq 26%

answer

  1. fractional scale factors
  2. half a hardware pixel
  3. anti-aliasing versus rounding
  4. odd difference when centring
  5. hairline as a platform primitive

basics

~20 s

When a logical value maps to a fraction of a hardware pixel, such as a 1-unit line at 1.5x or an icon centred with a half-unit offset, the renderer blurs or rounds it. Specs prefer values that land on whole pixels.

solid answer

~40 s

Everything is finally drawn on whole **hardware pixels**. At a scale factor of 1.5, a 1-unit divider is 1.5 pixels, so the renderer either **anti-aliases** it across two partly shaded rows, a soft grey smear, or **snaps** it to 1 or 2 pixels, making identical dividers look different thicknesses. Positions do the same: centring a 15-unit icon in a 24-unit button leaves a 4.5-unit offset, a half pixel at 1x. Specs prevent it by keeping thin, repeated edges on values that stay whole at common scale factors, keeping centring differences even, defining a **hairline** as the thinnest line the platform can draw rather than a fractional logical value, and testing on fractional-density screens, not only on 1x and 2x.

go deeper

for a junior

Recall that logical units become hardware pixels by multiplication, and that fractional results get blurred or rounded.

for a middle

Explain the two failure modes, anti-aliased blur and uneven rounding, and why centring an odd size in an even box creates half-pixel offsets.

for a senior

Show how you would diagnose a blurry divider or icon on a 1.5x device and turn the fix into a system rule rather than a one-off nudge.

for a principal

Weigh how strictly to enforce whole-pixel values against fluid and text-relative sizing, which produce fractions by design.

## Where half pixels come from Everything a design system specifies ends up drawn on whole **hardware pixels**. A logical value becomes hardware pixels by multiplying it by the device's **scale factor**. When the product is a whole number, the edge lands on a pixel boundary and looks crisp. When it is not, the renderer has to decide what to do with a fraction of a pixel. Fractions appear in three common ways: 1. **Fractional scale factors.** Many devices use factors such as 1.5, 1.75 or 2.75, so a 1-unit line becomes 1.5, 1.75 or 2.75 hardware pixels. 2. **Half-unit positions.** Centring a 15-unit icon in a 24-unit button leaves 4.5 units on each side; at a scale factor of 1 that is half a pixel. 3. **Computed sizes.** Space-relative and text-relative values, such as a third of a container or a size derived from a text size the user raised, routinely produce fractions. ## What the renderer does with a fraction | Strategy | What you see | Where it hurts | |---|---|---| | **Anti-aliasing**: shade the two pixels the edge straddles | a soft, greyish edge; a 1.5-pixel line looks like a faint two-pixel smear | hairline dividers, icon strokes, small status dots | | **Pixel snapping**: round the edge to a whole pixel | crisp edges, but identical values render at different sizes | repeated dividers, evenly spaced rows, icon alignment | Many rendering engines snap some things, such as box edges and text baselines, and anti-alias others, such as vector shapes and transformed layers, so one screen can show both symptoms. Snapping trades blur for **inconsistency**: three 1.5-pixel dividers in a list of appointments might render at 1, 2 and 1 pixels depending on where each falls, and the gaps between identical rows can differ by a pixel. It reads as sloppy even when nobody can say why. ## How specs avoid it - **Prefer values that stay whole at common scale factors.** Multiples of 2 are whole at 1x, 1.5x and 2x; odd values and halves are the usual offenders. - **Keep centring differences even.** Make icon and container sizes both even, or both odd, so the offset is a whole unit. - **Define the hairline as a platform primitive.** A hairline means the thinnest line the device can draw, one hardware pixel, not a fixed logical value. A 0.5-unit border is one hardware pixel only at 2x; at 1x it is blurred and at 3x it is 1.5 pixels. - **Keep icon geometry on whole units.** Strokes aligned to whole units land on pixel boundaries at 1x and 2x, while a 1-unit stroke centred on a grid line straddles two pixels at 1x. - **Export bitmaps per density** rather than letting the platform scale one bitmap by a fractional factor. ## Diagnosing a blurry element 1. Note the device's scale factor. Blur that appears only on 1.5x or 2.75x devices points at fractions. 2. Multiply the element's size and position by that factor; any non-integer result is a suspect. 3. Walk up the container chain. A parent sitting at a half-unit position shifts every child onto half pixels, however clean the child's own values are. 4. Fix the value in the component or the token, not with a one-off nudge on one screen, and add that device class to visual tests. ## Where to be pragmatic Whole-pixel discipline conflicts with fluid and text-relative sizing, which produce fractions by design. Most systems accept fractional sizes for large surfaces and for text, where a sub-pixel difference is invisible, and enforce whole values where edges are thin and repeated: dividers, borders, icon strokes and small indicators. In a veterinary clinic booking app, that means the appointment list's dividers and the small availability dot beside each slot are specified exactly, while the width of a slot chip in a fluid row is allowed to land wherever the layout puts it. The goal is not to eliminate every fraction; it is to keep fractions away from the thin, repeated edges where the eye notices them.

  • If renderers snap to whole pixels anyway, why should a spec care?
    Snapping trades blur for inconsistency. Three dividers that are each 1.5 hardware pixels may round to 1, 2 and 1 depending on where they fall, and the spacing between identical rows can differ by a pixel, which makes a list look uneven. Choosing values that are already whole at common scale factors removes the rounding decision.
  • Why does a 0.5-unit border fail as a universal hairline?
    It is one hardware pixel only at a scale factor of 2. At 1x it is half a pixel and gets anti-aliased into a faint blur; at 3x it is 1.5 pixels and blurs or rounds. A true hairline is defined as the thinnest line the device can draw, which each platform can supply directly.

saying these in an interview costs you the question

  • High-density screens make half-unit values harmless on every device.
  • A 0.5-unit border reliably draws a one-pixel hairline everywhere.
  • Blurry icons are always the export's fault, never the layout's.
  • Scale factors are always whole numbers, so fractions cannot occur.
  • Renderer pixel snapping means specs never need to choose values carefully.