Search code examples
csssafarivuejs3

Swapping CSS Drop-Shadow Filters in Safari


I have a transparent PNG image element where I want to apply a drop-shadow that will change color based on certain conditions. I am triggering the property change in a Vue 3 bound class selector:

:class="conditional ? 'trueCondition' : 'falseCondition'"
.trueCondition {
  filter: drop-shadow(0 0 5px #FF0000);
}
.falseCondition {
  filter: drop-shadow(0 0 5px #00FF00);
}

The proper drop-shadow is displayed when the page is first rendered, but when the condition changes, the drop-shadow color doesn't change (weirdly, it does sometimes). The proper conditional class is being set -- it's just that the filter isn't actually applied when the conditional changes.

Things I have tried:

  • adding filter: none; before setting the desired filter,
  • moving the CSS selectors from the view to main.css,
  • changing the false condition to filter: none; (which always works), and
  • things listed in updates below.

It seems as though Safari is having trouble changing from one declared filter to another declared filter. This behavior is being seen in Safari 16.1, but may apply to other versions as well. With Firefox, the expected filter change always works.

Is there a way to apply the filter change without having to render multiple times:

old filter --> no filter --> new filter?

Update:

I added a conditional source selector to force the <img> to be reloaded, but that didn't solve the issue (still works as expected in Firefox).

:src="conditional ? require('@/assets/image0.png') : require('@/assets/image1.png')"/>

Solution

  • I believe I’ve stumbled onto a solution for this issue. By including the following transform, the drop-shadows render as expected in Safari (and still in Firefox).

    transform: translateZ(0);
    

    The transform moves the rendering of the element to hardware acceleration (rather than in-browser). So, this functions as expected in both Safari and Firefox (I have not tested other browsers):

    .trueCondition {
      transform: translateZ(0);
      filter: drop-shadow(0 0 5px #FF0000);
    }
    .falseCondition {
      transform: translateZ(0);
      filter: drop-shadow(0 0 5px #00FF00);
    }
    

    Source(s): I found this solution in a comment to this GitHub post: https://github.com/mdn/browser-compat-data/issues/17726.

    The post references this Stack Overflow question (the GitHub comment above was sufficient for the solution): CSS performance relative to translateZ(0)