iossvgsafarimask

SVG undesired alpha mask behavior in Safari


I'm trying to apply a single mask to two overlapping <rect> SVG elements. I've created a simplified live example with the following code:

<svg viewBox="0 0 100 100" style="background: #000">
	<defs>
		<!-- 	Define radial gradient	 -->
		<radialGradient id="radialFill">
			<stop stop-color="white" offset="0.8"/>
			<stop stop-color="black" offset="1"/>
		</radialGradient>
		
		<!-- 	Define mask with gradient	 -->
		<mask id="SVGMask" mask-type="luminance">
			<circle fill="url(#radialFill)" cx="50" cy="50" r="50"/>
		</mask>
	</defs>
	
	<!-- 	Use mask on group	 -->
	<g id="combined" style="mask: url(#SVGMask);">
		<rect id="rect" width="100" height="100" fill="#f90"/>
		<rect id="rect" width="50" height="100" fill="#fff"/>
	</g>
</svg>
<figcaption>Mask 2+ elements Safari bug</figcaption>

In the code above I have an orange <rect> for the background color, a white <rect> occupying half the viewBox, then I wrap them up in a <g> and apply the mask to the whole group.

This setup works fine in both Firefox and Chrome, but does not work well in Safari. As you can see, the orange from the background shows through the white while it fades away. It looks like the mask is being applied separately to the front and back rects instead of once to the whole group.

enter image description here

Does anybody have a solution or workaround to this bug? It happens on both OSX Safari and iOS Safari. (My use-case is more complex than this, but I've created this simplified example to demonstrate the bug).


Solution

  • There is a (hacky) workaround for this - but it does work. The challenge is to get the mask to treat its input as a bitmap rather than as overlaid shapes. You can kick Safari to do this, by inserting a dummy filter on a group between the shape group and the mask. Make the following changes:

    Add this to your SVG defs:

    <filter id="dummy-filter">
       <feColorMatrix type="saturate" values="1"/>
    </filter>
    

    and change your drawing markup to this:

    <g id="combined" ... etc.
     <g filter="url(#dummy-filter)">
       <rect ... etc.
       <rect ... etc.
     </g>
    </g>