There is a non-standard way to stroke HTML text (SVG has a standard way). It’s not particularly new. There are -webkit-
and -moz-
prefixes for it. Jen Simmons recently posted about it, with an example:
span {
-moz-text-fill-color: #fde;
-webkit-text-fill-color: #fde;
-moz-text-stroke-color: #666;
-webkit-text-stroke-color: #666;
-moz-text-stroke-width: 2px;
-webkit-text-stroke-width: 2px;
}
And she’s right:
This CSS isn’t fully-baked or fully-supported. But it’s good enough to be used today, especially since it’s simply offering a visual enhancement. It’s not mission critical to making a website usable.
I’d only perhaps add that if you were going to do something like add a stroke around white text, you could wrap it in a @supports
to be extra sure it’ll be OK (just in case a browser exists that supports text-fill-color but not text-stroke-color) :
@supports
((-webkit-text-stroke-color: #666)
and
(-webkit-text-fill-color: white))
or
((-moz-text-stroke-color: #666)
and
(-moz-text-fill-color: white)) {
span {
-moz-text-fill-color: white;
-webkit-text-fill-color: white;
-moz-text-stroke-color: #666;
-webkit-text-stroke-color: #666;
-moz-text-stroke-width: 2px;
-webkit-text-stroke-width: 2px;
}
}
See the Pen Text stroke in action by Chris Coyier (@chriscoyier) on CodePen.
It Ruins Most Typefaces
That’s the thing that gets me about it. When you set a stroke straddled over the designed edge of a character, you’re losing the integrity of the shape.

And that’s the trouble with text-stroke in CSS: you have no choice. It’s center-aligned stroke only. Either of the other options, arguably, would have been more useful. It’s not the world’s biggest deal. The larger the text and the beefier the characters, the easier it is to get away with.
Set Behind Trick
If you’d like to simulate an outside-aligned stroke, James Nowland shows how you can set psuedo-element text behind the original text and still use text-stroke:
See the Pen CSS3 Stroke and Gradient Text by James Nowland (@jnowland) on CodePen.
Update! `paint-order` helps
As I write this update (February 2018), Tobi Reif tells me Firefox Nightly now has a CSS property called paint-order
, which can help control the order of what is painted on top of what, stroke or fill (or “markers”, which I admittedly don’t know what are).
.stroke-behind {
-webkit-text-stroke: 5px red;
paint-order: stroke fill;
}

This doesn’t help us actually set outside strokes, but it fakes it pretty well. No word on inside set strokes.
Maybe it is a bit naive, but why use text-fill-color when you have color?
Wouldn’t be more practical to use color and text-stroke-color combined?
I think the point of text-fill-color is that it’s an override of color for use in conjunction with other properties. For example, you could use it like this:
I think it’s a bit of a relic though. Now that @supports exists I can’t imagine browsers creating override properties like that again.
Thanks Chris Coyer for the feedback.
This is super awesome, but I think i’d have a hard time getting a client to support this in a project. Any thoughts on how I’d approach a client about this level of browser support and why they should try it?
Nice to know they are looking for add
text-stroke
.Right know I use multiple
text-shadow
(like a lot) to get similar effect in few cases (and it’s not that good obviously).I’m going to use
@supports
to removetext-shadow
false strokes and replace it with prefixedtext-stroke
properties.I also would like if
text-shadow
had the value spread-radius, likebox-shadow
.This is so cool. I’ve been thinking the last few days or so, “why don’t they have a “text-outline” feature or something?” But now I guess they apparently do! This is awesome.
I’ve been waiting for this!
I hate redundant hard-coded data. And I know many of us will already know what I’m about to say, but it wasn’t mentioned, so I thought I’d say something for those who wouldn’t have thought of this. If one doesn’t need the other pseudo element for anything, one could remove “100” from the text node inside “.Winning-text” and re-use the attr() function to inject “100” into the ::before pseudo element. Some re-organization of styles will be required, but I would argue that this is slightly better. Veer right at the fork: http://codepen.io/trenda/pen/jBMwrL?editors=1100
This is a very valid point but it does depend if the data you are trying to show is semantically important.
Why not just set a non-blurry box-shadow on text instead?
Text shadow doesn’t have that fourth spread parameter that box-shadow has to non-blur it. You can make text-shadow non-blurry with the third parameter, but then you can’t see it because it’s perfectly behind the text. In order to see it, you have to offset it, but then it’s not a stroke but it’s hanging off one side (like a shadow).
My wish sent to the CSS working group:
https://github.com/w3c/fxtf-drafts/issues/117