DEV Community

tmns
tmns

Posted on • Updated on

YouTube iframe, shadows, rounded corners, and Safari

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>
Enter fullscreen mode Exit fullscreen mode
.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%;
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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):

Strange square shadow applied to iframe in Safari

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:

Upon switching applications the correct shadow is rendered

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));
  }
Enter fullscreen mode Exit fullscreen mode

The only issue I noticed with this approach was that sometimes a strange extra border would peek out from behind the player:

Strange extra border

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));
  }
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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)