Grow your CSS skills. Land your dream job.

Adding Stroke to Web Text

Published by Chris Coyier

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! At least in WebKit. Example:

h1 {
   -webkit-text-stroke-width: 1px;
   -webkit-text-stroke-color: black;
}

Or shorthand:

h1 {
   -webkit-text-stroke: 1px black;
}

Right away, I'm sure you are thinking: "Cool, but only WebKit supports, this, so if I set my text color to white and my background is white, the stroke makes it look cool in WebKit but disappears in other browsers!"

WebKit has your back on that one, you can set the text color with another proprietary property, so you're safe for all browsers:

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:

Properly stroked!

Fallback to solid color. Shown here in Firefox

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 use remove the WebKit propreitary -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;
}

This is a stroke using all text-shadow. Pretty close to just as good as a real stroke. The primary issue is that you can only get 1px of stroke this way. Any more, and you see gaps. Any more with WebKit text stroke and there is issues too though, so it's kind of a horse apiece.

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;
}

Lookin' good

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:

From left to right: center, inside, outside

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).

From top to bottom: inside, centered, outside.

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.

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 WebKit stroke (hex, rgba, hsla, keyword). That means transparent strokes if you want them, which indeed to "stack" in that if strokes overlap each other (common) they will be darker.

As far as WebKit keyframe animation, the stroke color will animate but the stroke width will not.

/* Only the color will change, not the width */
@-webkit-keyframes colorchange {
	0% {
		-webkit-text-stroke: 10px red;
	}
	100% {
		-webkit-text-stroke: 20px green;
	}
}

Demo & Download

View Demo   Download Files

Thanks

Thanks to Hendra Susanto and Cory Simmons for sending in ideas and demos.

Comments

  1. fmvilas
    Permalink to comment#

    Nice!

    Thanks!

  2. Permalink to comment#

    Actually, I think the “Thick Text Shadow Only Problem” is pretty cool.

  3. Sam
    Permalink to comment#

    Hi Chris,
    Thanks for the mention and yet another inspiring post.

    Cheers, Sam

  4. Permalink to comment#

    That ‘combining’ is awesome, thanks!

  5. Permalink to comment#

    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

  6. Permalink to comment#

    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!

    • Permalink to comment#

      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.

  7. Permalink to comment#

    Thanks Chris…awesome!!!

  8. Permalink to comment#

    Thanks Chris, I really like the fallback effect in FireFox.

  9. This is very, very well thought out, and has much potential for daily use. Congratulations on all who contributed!

  10. Permalink to comment#

    Many thanks. did not know I could have multiple strokes. (Rogers 1; Favre 0)

    • Permalink to comment#

      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 ;)

    • Permalink to comment#

      Amount of trust anyone could ever have in Favre: 0%
      Number of man-cards revoked from Favre: 1

  11. Permalink to comment#

    Fantastic effect. Thanks for sharing.

  12. Permalink to comment#

    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.

    text-shadow: -3px -3px 0 #000,
                              0 -3px 0 #000,
                              3px -3px 0 #000,
                              3px 0 0 #000,
                              3px 3px 0 #000,
                              0 3px 0 #000,
                              -3px 3px 0 #000,
                              -3px 0 0 #000;

    It’s really a shame the shadow spread property isn’t available for text; we could solve this with one shadow declaration:

    0 0 0 3px #000;
    • Permalink to comment#

      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/

    • Niall
      Permalink to comment#

      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.

          text-shadow:
                  4px  0px 0 rgba(0,0,0,0.7),
                  4px  1px 0 rgba(0,0,0,0.7),
                  3px  2px 0 rgba(0,0,0,0.7),
                  3px  3px 0 rgba(0,0,0,0.7),
                  2px  3px 0 rgba(0,0,0,0.7),
                  1px  4px 0 rgba(0,0,0,0.7),
      
                  0px  4px 0 rgba(0,0,0,0.7),
                 -1px  4px 0 rgba(0,0,0,0.7),
                 -2px  3px 0 rgba(0,0,0,0.7),
                 -3px  3px 0 rgba(0,0,0,0.7),
                 -3px  2px 0 rgba(0,0,0,0.7),
                 -4px  1px 0 rgba(0,0,0,0.7),
      
                 -4px  0px 0 rgba(0,0,0,0.7),
                 -4px -1px 0 rgba(0,0,0,0.7),
                 -3px -2px 0 rgba(0,0,0,0.7),
                 -3px -3px 0 rgba(0,0,0,0.7),
                 -2px -3px 0 rgba(0,0,0,0.7),
                 -1px -4px 0 rgba(0,0,0,0.7),
      
                  0px -4px 0 rgba(0,0,0,0.7),
                  1px -4px 0 rgba(0,0,0,0.7),
                  2px -3px 0 rgba(0,0,0,0.7),
                  3px -3px 0 rgba(0,0,0,0.7),
                  3px -2px 0 rgba(0,0,0,0.7),
                  4px -1px 0 rgba(0,0,0,0.7);
      

      Just adjust the alpha to suit your font really. Pointy Font = Almost Fully Opaque shadow and not so smooth.

      http://jsfiddle.net/r4EAG/

  13. Permalink to comment#

    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.

  14. Permalink to comment#

    Thanks for the nice post but I think we would be able to use atleast after when modern browser will support it.

  15. Permalink to comment#

    Excelent efect, thanks!

    But for Firefox?
    I try to watch the demo with Firefox 4 Beta 5 and the browser fail…

  16. Permalink to comment#

    Excellent effects, and not too much code to achieve, Thank you!

  17. Permalink to comment#

    very nice

    looks like comic/sketch font

  18. 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.

  19. mdennisa
    Permalink to comment#

    very good things about text-stroke!

  20. 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.

  21. Permalink to comment#

    Very informative and helpful. Thaks very much.

  22. 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.

  23. 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!

  24. ddk
    Permalink to comment#

    thanx for sharing chris!!!

  25. Permalink to comment#

    This is something really new for me
    Never thought that CSS is able to do something like this

    Thanks for sharing Chris!

  26. Thanks a lot man! You saved me!

  27. Gurlal
    Permalink to comment#

    But its not working in IE. what i do ?

    • Permalink to comment#

      IE ??????

      the common browsers are : chrome ,firefox,safari and opera
      this code working on chrome ,firefox,safari and opera.

      IE is not very important !

  28. Neil Thielen

    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?

  29. Aaron

    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.

  30. socialblogsite

    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 :(

  31. Miss A

    Try increasing the font size to mega size (258px on the example) when using text stroke. What is going on there!

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".