29

I have the following HTML and CSS:

body { background-color: gray; }
h1 {
  color: white;
  font-size: 2.5em;
}
<h1>WHAT CARRER SHOULD YOU HAVE ?</h1>

Which renders like this:

enter image description here

I want to add a stroke around it, that means a black border around these text.
I Googled and found -webkit-text-stroke, and came up with:

body { background-color: gray; }
h1 {
  color: white;
  font-size: 2.5em;
  -webkit-text-stroke: 2px black;
}
<h1>WHAT CARRER SHOULD YOU HAVE ?</h1>

However, the effect is not what I want:

enter image description here

As you can see, it seems that the stroke is added inside the text, which make the text looks too thin for me.

How can I make the stroke outside the text?

Fiddle: http://jsfiddle.net/jpjbk1z7/

PS: only webkit support is needed

James McCormack
  • 9,048
  • 3
  • 46
  • 55
wong2
  • 31,730
  • 45
  • 128
  • 170
  • 1
    use text-shadow. Example : http://jsfiddle.net/lotusgodkk/jpjbk1z7/1/ – K K Oct 29 '14 at 15:24
  • 1
    I found this on google: "use the :before pseudo-element with the -webkit-text-stroke property set to create a "stroked" copy of the main text, then use z-index to put the copy behind the original, ending up with an outside stroke" http://www.petercarrero.com/examples/stroke/ – wf4 Oct 29 '14 at 15:25
  • Possible duplicate of [CSS- webkit-text-stroke but stroke covers font-color](http://stackoverflow.com/questions/17484262/css-webkit-text-stroke-but-stroke-covers-font-color) – Tobi Reif Mar 04 '17 at 16:25

11 Answers11

44

For a smooth outside stroke emulated by text shadow, use the following 8-axis shadow:

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

For customisation, you can use this SASS mixin instead (although changing the size does have side effects on rendering):

@mixin stroke($color: #000, $size: 1px) {
  text-shadow:
    -#{$size} -#{$size} 0 $color,
     0        -#{$size} 0 $color,
     #{$size} -#{$size} 0 $color,
     #{$size}  0        0 $color,
     #{$size}  #{$size} 0 $color,
     0         #{$size} 0 $color,
    -#{$size}  #{$size} 0 $color,
    -#{$size}  0        0 $color;
}

This gives a very smooth stroke, without missing parts, like on the 4 axis solution.

Ryall
  • 11,669
  • 11
  • 50
  • 75
  • Very cool and simple. The only thing is, I can't understand why it appears to have a slightly thicker stroke on the top than the bottom. Until I zoom in, then it looks correct. – BBaysinger Nov 24 '18 at 02:23
  • 1
    Thank you, Ryall ! Your 8-Axis "outline" rescued me when trying to emulate an outline around a subtitle text. I had tried canvas, but then found that doesn't support underlines. Using JS - with variables used to replace the user selected pixel widths - have solved the software conundrum in a couple of hours. – Cristofayre Jun 10 '20 at 11:25
  • Thanks but what if instead of "#{$" use "var(...)"? Rendering will suffer either way? – DDRRSS Dec 13 '21 at 05:36
29

Firefox and Safari now support a new CSS property called paint-order which can be used to simulate an outside stroke:

h1 {
  color: #00ff01;
  font-size: 3em;
  -webkit-text-stroke: 5px black;
}

.fix-stroke {
   paint-order: stroke fill;
}
<h1>the default often is ugly</h1>
<h1 class="fix-stroke">paint-order: stroke fill </h1>

Screenshot:

CSS paint-order screenshot

Salman von Abbas
  • 22,912
  • 8
  • 69
  • 56
  • 2
    Will be great if this gets added to Chrome also! – Ryall Jan 17 '19 at 13:07
  • 1
    Yes, unfortunately it still doesn't work in Chrome :( – Bruno Marotta Jun 09 '20 at 20:06
  • @mmaismma Thanks for the edit but the support table on caniuse.com is about `paint-order` support on SVG `` elements. Unfortunately Google Chrome still doesn't support `paint-order` on HTML elements. – Salman von Abbas Jul 29 '20 at 00:58
  • As of Chrome 84 (July 13, 2020) - Chrome now supports paint-order. https://caniuse.com/#search=paint-order – Rocky Kev Aug 17 '20 at 05:23
  • 3
    @RockyKev No, unfortunately it does not. See my comment above. (Or just click the "Run code snippet" button above using Chrome) – Salman von Abbas Aug 17 '20 at 06:51
  • It's a shame that Chrome *still* doesn't support it for standard `HTMLElements`. [MDN Has demos of it that work](https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order), but it only works on SVG elements, like: `Demo` – Marquizzo Dec 16 '21 at 23:51
23

One option is to use text-shadow to simulate a stroke. Example:

text-shadow:
    -1px -1px 0 #000,  
     1px -1px 0 #000,
    -1px  1px 0 #000,
     1px  1px 0 #000;
nunoarruda
  • 2,430
  • 4
  • 20
  • 43
12

The -webkit-text-stroke doesn't support placing the stroke on the outside of the text

as this CSS-Tricks article explains:

The stroke drawn by text-stroke is aligned to the center of the text shape (as is the default in Adobe Illustrator), and there is currently no option to set the alignment to the inside or outside of the shape. Unfortunately this makes it much less usable, as no matter what now the stroke interferes with the shape of the letter destroying the original type designers intent. A setting would be ideal, but if we had to pick one, outside stroke would have been much more useful.

What about SVG?

Well it seems that it also places the stroke on the inside -

FIDDLE

However,

you might be able to simulate this effect (depending on what you need) by:

  1. Change your font to a sans serif like verdana and

  2. Increase the font-size of the text you are adding a stroke to.

body {
  background: grey;
  font-family: verdana;
}
.stroke,
.no-stroke {
  color: white;
  font-size: 2.5em;
}
.stroke {
  -webkit-text-stroke: 2px black;
   font-size: 2.7em;
}
<h1 class="stroke">WHAT CAREER SHOULD YOU HAVE?</h1>
<h1 class="no-stroke">WHAT CAREER SHOULD YOU HAVE?</h1>
Joseph Didion
  • 142
  • 1
  • 6
Danield
  • 114,415
  • 36
  • 213
  • 236
  • 3
    It is now possible to place the stroke outside the text with [paint-order](https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order) – Felipe Cortez May 28 '18 at 15:24
  • 2
    @FelipeCortez shame it isn't supported in many browsers (e.g. Chrome) because `paint-order` would be really useful – drmrbrewer Mar 03 '19 at 12:27
7

Further elaborating on the other text-shadow solutions, for pixel-perfect thick round outlines the number of shadows should increase with the radius of the stroke. For example, a 10px thick outline requires 2⋅10⋅π ≈ 63 shadows distributed in all directions. To generate this in javascript, one could something like:

  const color = "#FFF" /* white outline */
  const r = 10 /* width of outline in pixels */
  const n = Math.ceil(2*Math.PI*r) /* number of shadows */
  var str = ''
  for(var i = 0;i<n;i++) /* append shadows in n evenly distributed directions */
  {
    const theta = 2*Math.PI*i/n
    str += (r*Math.cos(theta))+"px "+(r*Math.sin(theta))+"px 0 "+color+(i==n-1?"":",")
  }
  document.querySelector("#myoutlinedtext").style.textShadow = str

example of thick outlined text

If the thickness is static, then just run this once to generate the string and paste it into your CSS.

Alec Jacobson
  • 5,693
  • 4
  • 44
  • 79
2

One way I found was to stack two elements on each other having the element that stays behind get the double stroke width. The element behind leaks the stroke to the outside of the visible element making it a true stroke outside.

Also, another option is to use :after to create this second element. The downside is that you can't programmatically change the stroke

This might not work properly on big stroke values.

body { background-color: gray; }
h1 {
  color: white;
  font-size: 2.5em;
  font-family: verdana;
}

.stroke { -webkit-text-stroke: 2px black; }

.stack .stroke { -webkit-text-stroke: 4px black; }
h1.stroke-fs { font-size: 2.7em; }

.stack { position: relative; }
.stack > h1:not(:first-child) {
  left: 0;
  margin: 0;
  position: absolute;
  top: 0;
}


.stroke-after:after { 
  -webkit-text-stroke: 4px black;
  content: attr(value);
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
}
Stack
<div class="stack">
  <h1 class="stroke">WHAT CARRER SHOULD YOU HAVE ?</h1>
  <h1>WHAT CARRER SHOULD YOU HAVE ?</h1>
</div>

<hr />

After
<div style="position: relative">
  <h1 class="stroke-after" value="WHAT CARRER SHOULD YOU HAVE ?">WHAT CARRER SHOULD YOU HAVE ?</h1>
</div>

<hr />

Native
<h1 class="stroke">WHAT CARRER SHOULD YOU HAVE ?</h1>

<hr />

Font size
<h1 class="stroke stroke-fs">WHAT CARRER SHOULD YOU HAVE ?</h1>
BrunoLM
  • 94,090
  • 80
  • 289
  • 441
1

I have also tried text-shadow and -webkit-text-stroke, but unsuccessful in all ways. I could only get the result by making two divs(background and foreground) i.e,

background div with -webkit-text-stroke and foreground div without -webkit-text-stroke.

<div style="position:absolute;width:1280px;height:720px;left:0px;top:0px" >
    <div style="color:#CDCDCD;
                font-family:sans-serif;
                font-size:57px;
                -webkit-text-stroke-color:black;
                -webkit-text-stroke-width:0.04em;" >
        <p>
            Text with outine stroke outwards.
        </p>
    </div>
</div>

<div style="position:absolute;width:1280px;height:720px;left:0px;top:0px" >
    <div style="color:#CDCDCD;
                font-family:sans-serif;
                font-size:57px;" >
        <p>
            Text with outine stroke outwards.
        </p>
    </div>
</div>

Bt the only problem with the html generated through is twice the size(size in memory) as the div is repeated twice.

Does anyone has some better solution?

Prateek
  • 51
  • 6
1

i know this is an old question but have a look at this:

https://jsfiddle.net/1dbta9cq/

you can simply place another text on top of the stroked text with absolute positioning, it's not so tidy but it solves the problem

<div class="container">
  <h1 class="stroke">Stroke</h1>
  <h1 class="no-stroke">Stroke</h1>
</div>

.container{
  width:300px;
}

.stroke{
  position:absolute;
  -webkit-text-stroke: 10px black;
}

.no-stroke{
  color:white;
  position:absolute
}
Dan Levin
  • 668
  • 7
  • 16
-1

Text shadow might be used to achieve this result http://css3generator.com

  • 1
    With `text-shadow` you can just get some blurred shadow and the best you can get for border solid with this is just 1px ... I don't think the OP wants that. Also avoid just link answer include some code an example to get upvotes :) – DaniP Oct 29 '14 at 15:33
  • Not really a fair comment, especially considering the correct answer and its improvement. However, the answer still doesn't support an upvote. The broken link, for one thing. – Parapluie Sep 28 '21 at 18:22
-1

Here's a SASS mixin I created to make it easy to create a text outline using the prefixed properties (-webkit, -moz) when supported, but falling back to just a color with text shadow. Note that the fallback doesn't work well with opaque fill colors, but other than that I think it's a pretty good solution, until we can get a standard method that has better browser support.

Fiddle

Mixin

@mixin text-stroke($fill-color, $stroke-color, $stroke-width) {
  color: $fill-color;
  text-shadow: -$stroke-width -$stroke-width 0 $stroke-color,
                $stroke-width -$stroke-width 0 $stroke-color,
               -$stroke-width  $stroke-width 0 $stroke-color,
                $stroke-width  $stroke-width 0 $stroke-color;

  @supports ((-webkit-text-stroke-color: $stroke-color) and (-webkit-text-fill-color: white))
            or ((-moz-text-stroke-color: $stroke-color) and (-moz-text-fill-color: white)) {
                        color: unset;
                  text-shadow: unset;
         -moz-text-fill-color: $fill-color;
      -webkit-text-fill-color: $fill-color;
       -moz-text-stroke-color: $stroke-color;
    -webkit-text-stroke-color: $stroke-color;
       -moz-text-stroke-width: $stroke-width;
    -webkit-text-stroke-width: $stroke-width;
  }
}

Example Usage

(Makes text semi-transparent black with a white outline)

.heading-outline {
  @include text-stroke(rgba(#000,.5), #fff, 1px);
}
dmbaughman
  • 558
  • 2
  • 7
-1

I'm obviously late to the party but I find the easiest is probably the best way forward to a solution.

For me, I did the outside stroke effect by just making the weight of the font larger.

body { background-color: gray; }
h1 {
  color: white;
  font-size: 2.5em;
  font-weight: bold;
  -webkit-text-stroke: 2px black;
}

Is it the most correct ?
Depending on the situation then the answer is yes Or no.

Does it work in your use case ?
...for me , yes perfefctly.