I have figured out a solution which does not use filter
and z-index
. This can be used if you have a background or a foreground:
h1 {
position: relative;
}
h1::after {
background-image: linear-gradient(#f00, #fcc);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
content: attr(data-content);
position: absolute;
top: 0;
left: 0;
}
h1::before {
text-shadow: #000 1px 1px 2px;
content: attr(data-content);
position: absolute;
top: 0;
left: 0;
}
Effect: https://jsfiddle.net/m9dtf6po/2/
Of course, since I am using ::before
and ::after
, an attribute is needed, but the attribute can be shared. The problem of using z-index
is that:
This was the problem I faced when I did have a background and a foreground sandwiching my text, as in the following image:

The text S+
needs to be on top of a bar, but on top of all things there can be a box. I actually found this post and tried Karl's solution but the shadow disappeared behind the bar.