Home / Snippets / Typography /
Outlined/stroke heading
Bold typographic headlines with -webkit-text-stroke and paint-order — outlined, filled, or gradient-filled text in pure CSS.
Thin outline
Outline
Thick outline
Stroke
Stroke + solid fill (paint-order)
Filled
Stroke + gradient fill
Gradient
Quick implementation
/* Outlined heading — transparent fill, visible stroke */
.stroke-heading {
font-family: var(--font-display);
font-size: clamp(2.5rem, 7vw, 4rem);
font-weight: 800;
line-height: 1;
color: transparent;
-webkit-text-stroke: 2px oklch(0.72 0.19 265);
}
/* Stroke + solid fill — paint-order keeps stroke behind fill */
.stroke-heading--filled {
color: oklch(0.93 0.01 260);
-webkit-text-stroke: 3px oklch(0.72 0.19 265);
paint-order: stroke fill;
}
/* Stroke + gradient fill via background-clip */
.stroke-heading--gradient {
background: linear-gradient(
135deg,
oklch(0.72 0.19 265) 0%,
oklch(0.75 0.18 320) 50%,
oklch(0.78 0.16 40) 100%
);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-stroke: 1px oklch(0.72 0.19 265);
}
Prompt this to your LLM
Includes role, constraints, two variants, and edge cases to handle.
You are a senior frontend engineer building bold typographic hero headings.
Goal: Create outlined/stroke heading styles using -webkit-text-stroke and
oklch() colors — no SVG, no JavaScript, no pseudo-elements required.
Technical constraints:
- Use -webkit-text-stroke: <width> <color> to draw the stroke.
Example: -webkit-text-stroke: 2px oklch(0.72 0.19 265);
- For a pure outline (no fill), set color: transparent.
- For a stroke + solid fill, add paint-order: stroke fill so the stroke
renders beneath the fill instead of overlapping the letter interiors.
- For a gradient fill with stroke, combine background-clip: text,
-webkit-background-clip: text, color: transparent, and a
background: linear-gradient(...) — then add a thin -webkit-text-stroke
to define the edge.
- Use oklch() for all color values — no hex or rgba.
- Use CSS custom properties (var(--font-display), var(--text), etc.)
where applicable.
Variant A — Pure outline heading:
color: transparent;
-webkit-text-stroke: 2px oklch(0.72 0.19 265);
Variant B — Stroke + solid fill:
color: oklch(0.93 0.01 260);
-webkit-text-stroke: 3px oklch(0.72 0.19 265);
paint-order: stroke fill;
Edge cases to handle:
- Thick strokes without paint-order will bleed into letter interiors and
obscure the fill — always add paint-order: stroke fill when combining.
- The -webkit- prefix is required even in modern browsers for text-stroke;
the unprefixed text-stroke is not yet in baseline.
- Very thin strokes (under 1px) may disappear on low-DPI displays —
test on 1x screens and use at least 1px.
- Gradient fills require both -webkit-background-clip and background-clip
for cross-browser coverage; include both.
Return CSS only — no HTML markup.
Why -webkit-text-stroke is widely supported despite the prefix
The -webkit-text-stroke property has an unusual status in CSS: it carries the -webkit- vendor prefix but is implemented by every major rendering engine, including Firefox and Safari. This happened because browser vendors agreed to support the WebKit prefix for a handful of properties that became de-facto standards before the W3C formally specified them. The result is that -webkit-text-stroke has near-universal support — over 97% of global browser usage — while the unprefixed text-stroke specification is still being finalized in the CSS Working Group.
Because the prefix here signals legacy origin rather than experimental status, you can use it in production without a fallback in most cases. If you want to be explicit, you can add a @supports guard and let unsupported browsers fall back to a regular filled heading.
paint-order for cleaner strokes
The paint-order property controls the order in which the fill, stroke, and markers of an element are drawn. By default, SVG and CSS text draws the fill first, then the stroke on top — meaning a thick stroke overlaps the inner edges of the letterforms and bleeds into the counter shapes (the enclosed areas of letters like "o", "e", and "d").
Setting paint-order: stroke fill reverses this: the stroke is painted first, then the fill is painted on top of it. Because the fill covers the inner portion of the stroke, only the outer half of the stroke is visible. This doubles the effective "visible thickness" for a given stroke width and keeps letter interiors clean. A -webkit-text-stroke of 6px with paint-order: stroke fill looks the same as a 3px stroke without it — but with perfectly crisp interior edges.
This technique is especially valuable for heavy display type where the counter shapes are defining features of the typeface. Without paint-order: stroke fill, a thick stroke on a weight-800 heading can make counters unreadable.
Fallback strategy
The fraction of browsers that lack -webkit-text-stroke support is extremely small, but a defensive fallback is still good practice in high-stakes typography. The simplest approach is to rely on the cascade: define a normal filled heading first, then override with stroke styles. Browsers that don't understand -webkit-text-stroke will ignore it and display the filled heading.
For a more surgical fallback, use @supports:
/* Default: filled heading visible in all browsers */
.stroke-heading {
color: oklch(0.72 0.19 265);
}
/* Progressive enhancement: outlined style */
@supports (-webkit-text-stroke: 1px) {
.stroke-heading {
color: transparent;
-webkit-text-stroke: 2px oklch(0.72 0.19 265);
}
}
This pattern ensures the heading is always readable — users on the rare unsupported browser see a bold colored heading rather than invisible text.