
CSS Text Stroke in 2026 — How to Get Outlined Type That Actually Looks Good
📷 Engin Akyurt / PexelsCSS Text Stroke in 2026 — How to Get Outlined Type That Actually Looks Good
Outlined typography with -webkit-text-stroke is harder to get right than it looks. The common mistakes, the trick that fixes small-size legibility, and when to reach for text-shadow instead.
The first time I tried to use -webkit-text-stroke was for a hero headline on a marketing page. The brief was simple — outlined letters in the brand color, sitting over a busy product screenshot. Two minutes of CSS, looked great in Chrome on my laptop. I shipped it. Then someone in Slack pinged me with a screenshot from their phone where the headline looked like a smudge — the outlined letters had merged into a single illegible blob at small viewport sizes. Another person pointed out that on Safari the gradient fill I had added was rendering behind the stroke, which meant the colored fill was effectively invisible.
That headline took me four hours to debug. The CSS for the final version was four lines longer than what I shipped. This guide is the version of the explanation I wish someone had handed me on day one.
If you want to try the recipe in the browser without writing CSS, the CSS Text Stroke Generator has a live preview with all the knobs — font, weight, size, stroke width, fill mode, and an optional drop shadow.
The actual property: -webkit-text-stroke
There are exactly two properties you need to know about, and they are essentially aliases:
.outlined {
-webkit-text-stroke: 2px #0f172a;
text-stroke: 2px #0f172a;
}
The unprefixed version comes from the original CSS3 Text Decoration module, which has been bouncing around editor's drafts for years without ever shipping in browsers. The prefixed version, despite the prefix, has been universally supported since around 2016 — every Chromium browser, Safari (where it originated), and Firefox all ship it.
Always include both. The prefixed version is what actually does the work. The unprefixed version is a polite gesture toward the day when, theoretically, the standard catches up. That day will probably never come, but the line of CSS is cheap.
There is also a longhand-shorthand split:
.outlined {
-webkit-text-stroke-width: 2px;
-webkit-text-stroke-color: #0f172a;
/* shorthand: */
-webkit-text-stroke: 2px #0f172a;
}
The shorthand is fine for most cases. The longhand is useful when you are inheriting a stroke color from a parent and only want to override the width — for instance, in a design system where the stroke color is themed but the width varies by component.
How the stroke is actually drawn
This is the detail that makes the rest of the article make sense. The stroke is rendered inside the glyph outline, not outside it. Imagine the letter O. The browser knows the bounding curves of that letter — the inside edge and the outside edge of the donut shape. When you apply a 2px stroke, the browser starts at the outside edge and paints inward, two pixels deep.
Two practical consequences follow.
Thick strokes eat the letter shape. A 4px stroke on a 24px font consumes most of the letter's interior. The result is letters that look chunky and read as though the typeface itself is bolder. This is also why thin display fonts often work badly with text-stroke — they don't have enough visual weight to spare.
The stroke does not extend the typographic footprint. The line box, the baseline, the letter spacing — none of these change when you apply a stroke. The text takes up exactly as much horizontal space as it would without the stroke. This is one of the practical reasons text-stroke is preferred over alternatives that involve overlaying SVG or duplicating the element.
The most common visual mistakes
After making each of these myself at least once, I have a short list of patterns that consistently produce ugly results.
Stroke too thick for the font weight
A 3px stroke on a 300-weight font looks broken. The thin original strokes of the typeface get overwhelmed by the new outline, and what was meant to be a delicate display style ends up reading as a bold weight that someone forgot to set the weight on.
The fix is to match the stroke width to the font weight. As a rough rule:
- 300 weight: stroke under 1px
- 400 weight: stroke under 1.5px
- 700 weight: stroke 1.5–3px
- 900 weight: stroke 2–5px
The thicker the original glyphs, the more outline they can carry. Use the lightest stroke you can get away with — heavier strokes flatten the letterforms.
Stroke color the same as background
Outlined text against a solid background of the same color disappears entirely. The classic case is a designer who picked a stroke color from the photograph behind the headline, then tested only on a single photo. Switch to a different image and the headline becomes invisible.
If you are stroking text over an image, pick a stroke color that has high contrast against most of the photographs in your image library. Black usually works for outdoor photography, white for studio shots. If the image library is varied, consider an outline that is itself a contrasting pair — a 1px white outer outline plus a 2px black inner outline gives you readability against any background, at the cost of more CSS.
Stroke width that does not scale with viewport
A 3px stroke that looks crisp on a 1920px-wide desktop monitor will look chunky on a 375px-wide phone, where the same headline is now half the physical size. Use viewport-relative units or media queries to scale the stroke down on smaller screens:
.outlined {
-webkit-text-stroke: clamp(1px, 0.2vw, 3px) #0f172a;
}
The clamp() function locks the value between a sensible minimum and maximum while interpolating based on viewport width. It is perfect for this kind of optical adjustment.
When to use text-shadow instead
Text-stroke is not always the right tool. The alternative — using text-shadow with multiple offsets to fake an outline — has real advantages in certain cases.
.outlined-via-shadow {
color: white;
text-shadow:
-1px -1px 0 #0f172a,
1px -1px 0 #0f172a,
-1px 1px 0 #0f172a,
1px 1px 0 #0f172a;
}
This produces an outline that lives outside the glyph, not inside. The visual result at small sizes is much cleaner — the letter shapes stay intact and the outline acts as a contrast halo rather than eating into the type.
When to reach for text-shadow over text-stroke:
- Body copy or small UI text (under 24px) — text-stroke eats too much of the letter interior at this scale
- Image overlays where the background is unpredictable — text-shadow with a darker color reads as a "halo" that helps the text pop
- When you need to combine outlined text with a complex background-clip: text gradient — Safari composes text-shadow more reliably than it composes text-stroke with background-clip
- When you need very thin outlines — sub-pixel strokes don't render consistently across browsers; a 1px text-shadow always works
When to prefer text-stroke:
- Large display headlines (48px and up) where the outline should be geometrically true to the typeface
- Any case where you want a crisp single-color outline — text-stroke produces a sharper edge than four stacked text-shadow copies, especially on retina displays
- When you want the option of a transparent fill —
color: transparentplus a stroke is the classic hollow-letter look that text-shadow cannot replicate without additional tricks
The gradient-fill recipe
This is the trick that turns a basic outlined headline into something that looks designed:
.gradient-outline {
font-size: 96px;
font-weight: 900;
background: linear-gradient(135deg, #f97316 0%, #ec4899 100%);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
-webkit-text-stroke: 2px #0f172a;
text-stroke: 2px #0f172a;
}
The mechanics: color: transparent makes the letter interior invisible; background-clip: text clips the gradient to the shape of the text, so the gradient only shows inside the letters; the text-stroke draws the outline on top. The result is a glyph with a colorful gradient interior and a clean dark outline.
Three things to watch:
Always include -webkit-text-fill-color: transparent. Safari sometimes inherits a non-transparent color and ignores color: transparent when background-clip: text is set. Adding the prefixed text-fill-color forces the issue.
Test with the gradient direction reversed. A 135-degree gradient looks great when the headline runs left-to-right. In RTL languages it can look wrong, since the brighter end of the gradient lands on the wrong side of the text.
Be aware of stacking-context bugs. If your headline is inside a parent with a transform, filter, or opacity, Safari can fail to composite the background-clip correctly. Move the gradient styles up to the parent, or wrap the headline in an extra element to break the stacking context.
Drop shadow for legibility on busy backgrounds
When the headline sits over a photo or video, a soft drop shadow underneath the outlined text adds a contrast halo that catches the gap between letters. This is the technique used by almost every cinema poster and album cover with hand-set type.
.outlined-over-photo {
-webkit-text-stroke: 2px white;
color: transparent;
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
The shadow is offset down and softened with a large blur radius, which produces a glow that does not visually compete with the outline. Use shadows like this sparingly — combined with the stroke they can quickly look cluttered. One shadow at low opacity is plenty.
Print and PDF gotchas
If your CSS is going to be printed or exported to PDF — for instance, an invoice or a certificate — text-stroke does not always print reliably. Browser print engines handle the property inconsistently. Some print the stroke at the wrong width because the rasterization happens at a different resolution than the screen. Some skip the stroke entirely.
The reliable workaround for print is to switch to text-shadow (which prints faithfully) inside an @media print block:
@media print {
.outlined {
-webkit-text-stroke: 0;
color: black;
text-shadow: none;
}
}
For most documents, the safest call is to flatten outlined display text to solid black or solid white before sending to print, since the visual nuance of the outline rarely survives a 300 dpi printer anyway.
Putting it together
The simplest version of outlined text — the version that works in every modern browser, looks crisp at headline sizes, and degrades gracefully on small screens — is four lines:
.headline {
font-size: clamp(48px, 8vw, 120px);
font-weight: 900;
-webkit-text-stroke: clamp(1px, 0.2vw, 3px) #0f172a;
color: transparent;
}
Drop that on a hero element, set the font to a display weight you trust, and you are 90 percent of the way to a headline that looks intentional. Add a gradient fill if you want it to look like a poster. Add a drop shadow if it sits over a photograph. Switch to text-shadow if you need it readable below 24px.
The generator at the top of the article will produce all of these patterns and more. Tweak the knobs, copy the CSS, and ship something that does not need a Slack debugging session at 11pm.
Related tools
- CSS Gradient Generator for the gradient fills that pair with outlined headlines
- CSS Box Shadow Generator for the drop shadows that make outlined text pop
- CSS Filter Generator for blur and contrast adjustments on the layer behind the text
- Color Converter for cycling between hex, RGB, and HSL when picking outline colors