Let’s do a little step-by-step of a situation where you can’t quite do what seems to make sense, but you can still get it done with CSS trickery. In this case, it’ll be applying a shadow to a shape.
You make a box
.tag {
background: #FB8C00;
color: #222;
font: bold 32px system-ui;
padding: 2rem 3rem 2rem 4rem;
}
You fashion it into a nice tag shape
You use clip-path
because it’s great for that.
.tag {
/* ... */
clip-path: polygon(30px 0%, 100% 0%, 100% 100%, 30px 100%, 0 50%)
}
You want a shadow on it, so you…
Try to use box-shadow
.
.tag {
/* ... */
box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.5);
}
But it doesn’t work. Nothing shows up. You think you’re going crazy. You assume you have the syntax wrong. You don’t. The problem is that clip-path
is cutting it off.
You can drop-shadow a parent element instead
There is a filter that does shadows as well: drop-shadow()
. But you can’t use it directly on the element because the clip-path
will cut it off as well. So you make a parent:
<span class="tag-wrap">
<span class="tag">
Tag
</span>
</span>
You can’t use box-shadow
on that parent either, because the parent is still a rectangle and the shadow will look wrong. But you can use filter
, and the shadow will follow the shape.
See the Pen
Shadow on Shape by Chris Coyier (@chriscoyier)
on CodePen.
That’s all.
To avoid using an extra element, an absolutely positioned
:before
can be clipped and thedrop-shadow()
applied on the element itself. Here’s a fork doing this.One more note: when writing prefixes manually, it’s probably best to leave the unprefixed version last. This is because what a certain property value does may change over time and having the unprefixed version last ensures the latest implemented version is used in browsers that support it.
Thx for the post and for notes as well :)
really nice approach.
Huh…
That’s really simple.
Thanks Chris
Looks good, but filter drop-shadow can be a performance killer and must be used with caution. Always measure performance when combining these two.
Awesome stuff!!!
Why does filter follow the child element like that? I’ve read through the MDN docs as a refresher (https://developer.mozilla.org/en-US/docs/Web/CSS/filter) and I don’t see any obvious reason.
From your given MDN link: “A drop shadow is effectively a blurred, offset version of the input image’s alpha mask drawn in a particular color, composited below the image.” (MDN)
The “input” image is the graphic source that is filtered with a drop shadow, ie. a transparent span below a child span with a clipped background.
Nice one
I stumbled upon a similar case this week, in SVG, and learned that filters are applied after clipping. This might be the same order when applied to an HTML element. That would explain why applying filter: drop-shadow on the clipped span doesn’t work.
https://www.w3.org/TR/SVG11/render.html#Elements
The pseudo-element solution from Ana is nice! I solved my own case by “substracting” (in Adobe Illustrator Pathfinder) the shape from a rectangle that is wrapping it.
I would say be careful with this, as filter: drop-shadow is performance heavy, I have used this method in my own projects and ran into this issue.
Hi! Nice article, but what about compatibility clip-path with browsers?
I was looking for a article like this a few month ago. I ended up giving up of using Clip Path with Box Shadows for a while. Nice tutorial.
Fantastic approach. Love it. Thank you for our dedicated service! :)