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 !

Leave a Comment

Current day month ye@r *

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