Fonts on the web are essentially vector-based graphics. That’s why you can display them at 12px or 120px and they remain crisp and relatively sharp-edged. Vector means that their shape is determined by points and mathematics to describe the shape, rather than actual pixel data. Because they are vector, it would make sense if we could do things that other vector programs (e.g. Adobe Illustrator) can do with vector text, like draw a stroke around the individual characters. Well, we can! Example:
h1 {
/* Prefix required. Even Firefox only supports the -webkit- prefix */
-webkit-text-stroke-width: 1px;
-webkit-text-stroke-color: black;
}
Or shorthand:
h1 {
-webkit-text-stroke: 1px black;
}
You might be thinking “Cool, but if only some browsers support this, if I set my text color to white
and my background is white
, the stroke makes it look cool in supporting browsers but entirely disappears in non-supporting browsers!”
One possibility is this:
h1 {
color: black;
-webkit-text-fill-color: white; /* Will override color (regardless of order) */
-webkit-text-stroke-width: 1px;
-webkit-text-stroke-color: black;
}
Shown here with @font-face font Anime Ace 2 by Nate Piekos:
Another possibility is only applying when supported:
@supports (-webkit-text-stroke: 1px black) {
h1 {
-webkit-text-stroke: 1px black;
-webkit-text-fill-color: white;
}
}
Support
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
4* | 49* | No | 15* | 3.1* |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
115* | 115* | 2.1* | 4.0-4.1* |
Simulation
We can take this a bit further by not relying on the WebKit proprietary entirely. We can use the text-shadow
property (supported in Firefox, Opera, and IE 10 as well) and simulate a stroke. We’ll use four shadows, each 1px offset of black with no spread, one each to the top right, top left, bottom left, and bottom right. We’ll remove the WebKit proprietary -webkit-text-fill-color in favor of color since we’re cross-browser compatible now. The only holdout would be IE9 and down, which of course you can use IE-specific stylesheets to account for.
h1 {
color: white;
text-shadow:
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
}
Combining
Using both a stroke and a shadow can be a great effect. Let’s toss on a WebKit stroke, the all-around text-shadow stroke, as well as a deeper text-shadow stroke.
h1 {
-webkit-text-stroke: 1px black;
color: white;
text-shadow:
3px 3px 0 #000,
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
}
Demo
Alignment
If you are familiar with Adobe Illustrator, you may know that you can align a stroke on the inside of a shape, outside, or centered. That option looks like this in the palette:
For reasons unbeknownst to me, text in Illustrator can only be set to center stroke alignment as well. Once you expand the text into regular vector paths though, all three options become available. Reminder from Sam Frysteen: add a new stroke in the Appearance panel and move it below your text (basically mimics outside stroke alignment).
Only outside text stroke alignment looks any good at all to me. It’s unfortunate, both for CSS and for Illustrator, that the unchangeable default is centered. The solution is just not to go too crazy with the thickness of your stroke border and things should be OK. Note: the text-shadow-only solution doesn’t have this problem, but also is unable to stroke any more than 1px.
If you use a pseudo element, you can stroke the same text behind the original text and kinda fake outside stroke.
We have a whole article on this alignment issue: Text Stroke: Stuck In The Middle With You. A minor bit of good news, the paint-order
property allows you to esssentially have outside set strokes, once more browsers support it.
What we can’t do
There are other things that vector-based graphics programs can do with text. You can squish the letter horizontally / stretch them vertically. This type of text treatment is almost universally frowned upon, so no big loss that we can’t do that. You can also set type on an irregular line (like around a circle). It certainly would be cool to do this with web text. Perhaps we could set text to follow the border path of its parent element.
p.circular {
width: 200px;
height: 200px;
border-radius: 100px;
/* NOT REAL */
text-align: border-path;
}
In Illustrator, we can also tell a stroke how to handle sharp corners: rounded, beveled, or mitered. These can have nice effects, are not possible on the web. However, the WebKit handling of corners is pretty nice at its default (whatever it is), and arguably nicer than any of the options in Illustrator.
Fancies
For the record, you can use any type of color value for the color of stroke (hex, rgba, hsla, keyword). That means transparent strokes if you want them, which indeed “stack” in that if strokes overlap each other (common) they will be darker.
As far as the keyframe animation, the stroke color will animate but the stroke width will not (weird).
/* Only the color will change, not the width */
@keyframes colorchange {
0% {
-webkit-text-stroke: 10px red;
}
100% {
-webkit-text-stroke: 20px green;
}
}
More information
text-stroke
(CSS-Tricks Almanac)- Text Stroke: Stuck in the Middle With You (Chris Coyier)
- CSS Text Shadow (Chris Coyier)
- Classy Text Shadows (Chris Coyier)
- Cool Hover Effects That Use CSS Text Shadow (Temani Afif)
- How to Create Neon Text With CSS (Silvia O’Dwyer)
- The CSS
text-shadow
property (Alligator.io)
Nice!
Thanks!
Actually, I think the “Thick Text Shadow Only Problem” is pretty cool.
Hi Chris,
Thanks for the mention and yet another inspiring post.
Cheers, Sam
That ‘combining’ is awesome, thanks!
Thanks for this Chris. I’d love to do more of these.
Here is an example of just the text-shadow trick combined with subtle em measurements to create a really nice effect on any font:
http://blog.pressedweb.com/demos/css_text_stroke/
– Cory Simmons
very nice cory
Loved it! I don’t think i’m going to use it much though, I don’t think websites should look exactly the same in all browsers but this is about where I cross the line. Having a stroke or not is kind of a big graphical difference… Nevertheless, cool to know!
It actually looks identical on all major browsers and completely ignores IE. So you get cross-compatibility progressive enhancement WITH a big slap to the face to IE.
Thanks Chris…awesome!!!
Thanks Chris, I really like the fallback effect in FireFox.
This is very, very well thought out, and has much potential for daily use. Congratulations on all who contributed!
Many thanks. did not know I could have multiple strokes. (Rogers 1; Favre 0)
Super Bowl Rings: Favre 1, Rodgers 0;
MVPs: Favre 3, Rodgers 0;
Consecutive Starts: Favre 300; Rodgers 18;
Favre lost to last year’s Super Bowl champs; Rodgers narrowly beat a horrible side.
Be careful ;)
Amount of trust anyone could ever have in Favre: 0%
Number of man-cards revoked from Favre: 1
Fantastic effect. Thanks for sharing.
You can actually get a bit closer with the text shadow method if you just take it further by adding four more shadows. It’s still a little bit flat on the edges, but almost perfect.
It’s really a shame the shadow spread property isn’t available for text; we could solve this with one shadow declaration:
I fiddled around a little more and hacked together some sort of an Internet Explorer solution using filters. I actually feel a little dirty about the whole thing, and I can’t say it looks super great, but it’s reasonably functional. Great article as usual Chris. I’m always inspired to push the envelope after reading your stuff.
http://jsfiddle.net/kovalchik/yJff9/
So I took the Thick text shadow only problem and looked at it from a icon artists perspective. So basically trace the outline of a b&w circle that is X in radius using text-shadow, then use rgba to blend the shadows together to get something close to a circle smoothed by anti-alias.
This is probably asking the browser for a lot of juice & the rgba alpha mustn’t help much, but it’s nicer.
Just adjust the alpha to suit your font really. Pointy Font = Almost Fully Opaque shadow and not so smooth.
http://jsfiddle.net/r4EAG/
Thanks Chris for putting this up!
I think you forgot to mention that if there’s a border around the page ( doesn’t matter where ) the -webkit-text-stroke will strangely not work in Chrome IF there is NO text-shadow around. It will still work in Safari though.
Thanks for the nice post but I think we would be able to use atleast after when modern browser will support it.
Excelent efect, thanks!
But for Firefox?
I try to watch the demo with Firefox 4 Beta 5 and the browser fail…
Excellent effects, and not too much code to achieve, Thank you!
very nice
looks like comic/sketch font
Hey Chris, great post. Having the flexibility to be able to customise real text like this on the web is amazing – the powers of CSS3 shine through once again. Being able to add strokes that scale to any size is great as well as the ability to customise the stroke positioning is a bonus! Thanks for sharing.
very good things about text-stroke!
There is already a text-outline property in the current CSS3 Text Module, though it’s at risk of being cut:
http://www.w3.org/TR/css3-text/
Personally, I think “outline” makes slightly more sense than “stroke”, unless the stroke is beefed up to allow you to specify an inner or outer stroke. If that’s the case, it would be cool for that to be applied to block-level elements as well.
Very informative and helpful. Thaks very much.
I went through a phase about 5 years ago where I liked to “stroke” text a lot. I worked in the automotive industry and the style fit their needs pretty well (see racing team logo designs). If this had been available back then it would have been very helpful – even without the widespread support.
Thanks for the articles. They always get me thinking about other possibilities that are open to me and other CSS developers.
I generally use the text-shadow and -moz-text-shadow, but the new CSS3 potential is amazing, checkout -webkit-transition-duration: amd-webkit-transition-timing-function, We use it on our site for link fades!
thanx for sharing chris!!!
This is something really new for me
Never thought that CSS is able to do something like this
Thanks for sharing Chris!
Thanks a lot man! You saved me!
But its not working in IE. what i do ?
IE ??????
the common browsers are : chrome ,firefox,safari and opera
this code working on chrome ,firefox,safari and opera.
IE is not very important !
I am taking a Graphic Design class and the instructor says that text strokes is taboo in the graphic design industry. Is that true or at least universal? They say that it makes text unreadable. I can see that if not done properly but it seems like a very good way to make text readable against some backgrounds. What do you think?
Be careful with the HD renderings of Safari on Ipad. The cheat of the stroke works on low rez browsers but it is caught on high rez and creates an uncomfortable visual.
If I remember correctly from my 90’s reading of Adobe’s PostScript, the reason why fonts can’t have outer strokes only is eithe (a) because only PostScript fonts have the information of the path direction, which you can use to have overlapping paths and even though not generating a hole in the intersection, and (b) because the OS is what manages the font in the App (for speed purposes) and it’s the same reason why you couldn’t “view” stroke patterns and other PostScript effects until very recently.
Also, in Illustrator, if you add a fill and move it behind the “Characters”, as you mentioned, you still end up needing a fill color to cover the inside part of the stroke.
The correct way to do it is adding a new stroke and applying the “offset path” filter to it. That way you can make transparent-fill text outlined with the offset and width you want.
There used to be another way, with an “outline” filter, which would allowed you to add as many onion outlines as you want, but I can’t find it or it’s broken in AI CC :(
Try increasing the font size to mega size (258px on the example) when using text stroke. What is going on there!