I find it difficult to believe that there is no standard and simple (and browser-independent) way to put a stroke effect around the outside of text using CSS.
We do have -webkit-text-stroke
but for some odd reason the stroke is centred around the border of the text rather than outside it, as bemoaned here.
So I'm trying to implement a workaround based on this idea, which places the stroked text in a pseudo element behind the original un-stroked text. I've demonstrated this in this jsfiddle, with the following code:
var jQueryAttr = function(selector, attr, setterFunction) {
document.querySelectorAll(selector).forEach((el, i) => {
el.setAttribute(attr, setterFunction.call(el, i, attr));
});
};
jQueryAttr('.myclass', 'data-myclass', function(index, attr) {
return this.innerHTML;
});
body {
background: none;
}
.basic {
color: rgba(186, 218, 85, 1);
font: 2.5em Georgia, serif;
}
.myclass {
position: relative;
background: transparent;
z-index: 0;
}
.myclass::before {
content: attr(data-myclass);
position: absolute;
-webkit-text-stroke: 0.2em rgba(0, 0, 0, 1);
z-index: -1;
}
.anotherclass {
-webkit-text-stroke: 0.2em rgba(0, 0, 0, 1);
}
<p class="basic">Text without any stroke</p>
<p class="myclass basic">Text with outer stroke</p>
<p class="anotherclass basic">Without the trick applied</p>
This works fine, except that if the text itself has some transparency then you see the dark stroke underneath, as shown in this variant (the only change is to peg the opacity of the text back to 0.3). As you can see, the black from the stroked element is leaching through into the text (in the top line).
So is there another neat trick to use to overcome this problem? I guess it's possible to add another pseudo element between the stroked layer and the un-stroked layer, with a pure white text (or one to match the background), but I'd like to apply this technique in a context where I don't know the colour of the background in advance... e.g. where this is laid over an arbitrary user-selected image. For this reason, I've set the background
of the body
to none
in the above example.
The CSS-property paint-order could do the trick:
The result will look like this in Firefox 65.0.2:
The easiest, and with best browser support might actually be SVG.
You can set up approximately the same thing you did with the ::before, with the difference that the background stroked version can have a mask, which will let only the outer-line visible.
From there, you can simply append a copy of the same text over, and you'll be able to apply the opacity as you wish, both on the stroke and the fill:
Here is an idea where you can consider
mix-blend-mode
and a combination of text-shadow to approximate this. The tricky part is to adjust the shadow in case you want a bigger stroke:Using CSS variable will probably make it easier to adjust:
If you want a transparent color for the text you can duplicate it using pseudo element:
Solution using SVG filters
To get a stroke around the text, you can use a combined SVG filter consisting of successively applied filters:
feMorphology
,feComposite
andfeColorMatrix
.