Context
Recently I was tasked with embedding a YouTube video in one of our landing pages. Straight-forward enough it seemed and indeed it was, until a new requirement came in last minute (as they tend to do): the embedded player needed to also have rounded corners.
This requirement complicated things a bit because the embed also had a shadow applied to it. Ultimately, implementing it would lead me to noticing strange behavior regarding shadows in Safari.
Read on if you dare! Alternatively, if you want to just see the code, check out this codepen for an example.
Approach #1: border-radius
First, let's take a look at our baseline markup and styles to get a feel for what we were working with:
<div class="container">
<div class="iframe-wrapper">
<iframe src="https://www.youtube.com/embed/FcANFVcJeOM?disablekb=1" title="Live performance of Weird Fishes by Radiohead" frameborder="0"></iframe>
</div>
</div>
.iframe-wrapper {
position: relative;
aspect-ratio: 16 / 9;
box-shadow: 0 0 6px rgb(0 0 0 / 50%);
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
Note: the styles above are in scss.
My initial thought was to just throw a border-radius
on .iframe-wrapper
and call it a day. Indeed this seemed to work fine in Chrome and Safari. However, in Firefox the corners looked very choppy and ugly. As such, that option was out.
So, unfortunately, our simple approach #1 == ❌
Approach #2: clip-path
and filter: drop-shadow()
My next thought was to apply a clip-path
to the iframe
in order to generate the desired border radius and then apply a drop-shadow
filter on .iframe-wrapper
(so that the generated shadow would respect the shape created by the clip-path
):
.iframe-wrapper {
position: relative;
aspect-ratio: 16 / 9;
filter: drop-shadow(0 0 4px rgb(0 0 0 / 50%));
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
clip-path: inset(0% 0% 0% 0% round 10px);
}
}
This looked great in Chrome and Firefox! The jagged corners that were previously being generated by border-radius
were gone and in their place was nothing but wonderful smoothness.
I opened up Safari and expected to see the same... but to my horror... what I saw on the screen was a shadow being applied to the player that looked as if I had set a box-shadow
on .iframe-wrapper
. See the following screenshot for an example (if the quality isn't good enough you can open the codepen in Safari and replace the styles with those above):
What is that??! 🤯
To make things even more strange, on non-touch screen devices this weird extra box shadow would disappear and the correct drop-shadow
would appear as soon as Safari lost focus (e.g. switching applications or interacting with something in dev tools). Conversely on mobile it would disappear once I interacted with the page 😱
See the following gif for an example of this behavior (again, if the quality isn't good enough you can open the codepen in Safari and replace the styles with those above). Notice, upon switching to the calendar and back the weird square box shadow disappears and the desired drop shadow comes in:
I still don't understand exactly what the issue was here but indeed it seems that in Safari drop-shadow
is not applied correctly on first load.
This, of course, was not acceptable.
So, unfortunately, our approach #2 also == ❌
Approach #3: ?
Now, where could I go from there? How could I simulate a filter: drop-shadow()
, i.e. a shadow that respects an inner element's contour?
Somewhat unexpectedly, I discovered that keeping the clip-path
on the iframe
and applying border-radius
and box-shadow
to .iframe-wrapper
would give me the best of both worlds. That is, the corners would remain smooth in Firefox and the shadow would render correctly in Safari:
.iframe-wrapper {
--border-radius: 10px;
position: relative;
aspect-ratio: 16 / 9;
border-radius: var(--border-radius);
box-shadow: 0 0 6px rgb(0 0 0 / 50%);
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
clip-path: inset(0% 0% 0% 0% round var(--border-radius));
}
The only issue I noticed with this approach was that sometimes a strange extra border would peek out from behind the player:
Further, it wasn't always on the right side. Sometimes it would show up on the bottom 😵
However, this ended up being solved quite easily by adjusting the background-color
of .iframe-wrapper
! Further, this also had the side effect of providing a quasi-placeholder as the video loads 🤙
In the end, the following declarations gave me the perfect rounded corners and shadow I was after:
.iframe-wrapper {
--border-radius: 10px;
position: relative;
aspect-ratio: 16 / 9;
/* The next three declarations simulate
`filter: drop-shadow(0 0 4px rgb(0 0 0 / 50%));`
*/
border-radius: var(--border-radius);
box-shadow: 0 0 6px rgb(0 0 0 / 50%);
background-color: rgba(0, 0, 0, 0.3);
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
clip-path: inset(0% 0% 0% 0% round var(--border-radius));
}
So, eventually Approach #3 == 👍
Conclusion
At the end of the day I was quite happy with the result and it worked for the given task. Of course, I would have liked to stop at Approach #2 (just drop-shadow
); however, I couldn't release something that would look bad on Safari and in turn iOS.
What do you think about this Safari issue? Have you ever ran into it yourself? I couldn't find anything about it in my research 🤷♂️
Let me know!
Update / Approach #4
Just saw a cool effect being used on the YouTube embed at Next.js Conf. They set a linear gradient background with a blur on a pseudo element of the embed's container / wrapper div
.
This could also be used in our case to achieve our desired shadow, replacing the three declarations added in Approach #3:
.iframe-wrapper {
position: relative;
aspect-ratio: 16 / 9;
&::before {
content: "";
position: absolute;
inset: -5px;
z-index: -1;
background: #000;
filter: blur(10px);
opacity: .75;
}
iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
clip-path: inset(0% 0% 0% 0% round 10px);
}
}
This is a nice approach and seems to work well in all browsers. This conference hasn't even started yet and I've already learned something 😄
Top comments (0)