WebCraft

paint-order: stroke fill

An outline that does not eat the letters

Craft stroke alone Craft stroke, then fill

Caveat-webkit-text-stroke centres the stroke on the glyph outline, so half of a five-pixel stroke grows inward and thins the letterform until the counters in a, e and o close up. paint-order: stroke fill repaints the fill over the stroke and the shape comes back, which is the whole trick and the only fix that does not involve a second copy of the text. Two things to budget for: the stroke takes no space in layout, so it overflows the line box, crowds the line above and is cut by any overflow: hidden ancestor; and the property is still prefixed after fifteen years, with no unprefixed alias on the way.

FallbackAn engine that ignores paint-order still applies the stroke, so the word renders in the default centred form — heavier and tighter, but legible. One that drops both paints plain filled type. That is why the fill has to carry the contrast and the stroke has to be the decoration: reverse them and the fallback is a word nobody can read.

CSS
.display {
  /* centred on the outline: half of
     it grows in and eats the shape */
  -webkit-text-stroke: 5px #6fa8dc;
  color: #e6eaf2;

  /* repaint the fill on top and the
     letterform comes back */
  paint-order: stroke fill;
}

Published

Questions

<details name>

Why does my text look thinner after adding a stroke?

The stroke is centred on the glyph outline, so half of its width grows inward over the letterform. At 1px nobody notices; at 5px the counters close and the word turns into a blob. paint-order: stroke fill paints the fill back over that inner half.

Is there an unprefixed text-stroke?

No. -webkit-text-stroke is what every engine implements, prefix and all, and the unprefixed text-stroke in old drafts never shipped anywhere. Write the prefixed one and do not add a fallback for a property that does not exist.

Why is the top of my outlined heading clipped?

The stroke does not take part in layout, so it paints outside the line box and the first ancestor with overflow: hidden cuts it. Add the padding yourself, roughly the stroke width on each side.