I have an SVG image that uses masking to cut out a hole in another shape. I've simplified the complex shapes involved down to this representative example:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
<defs>
<mask id="mask">
<rect x="0" y="0" width="256" height="256" fill="#ffffff" />
<circle cx="128" cy="128" r="32" fill="#000000" />
</mask>
</defs>
<circle mask="url(#mask)" cx="128" cy="128" r="64" fill="#ff0000" />
</svg>
This works as expected, i.e., draws the following donut shape, in pretty much every context:
However, if I inline the SVG in an email and view it in macOS Mail (16.0, comes with Monterey 12.3.1) or iOS Mail while dark mode is enabled, I get this peculiar version:
What appears to be happening is that these two apps are trying to guess at a dark-mode interpretation of the SVG, and have erroneously changed the mask's #ffffff
to a very dark grey (#232323
, if I had to guess, which is the background color of the application itself in dark mode) and #000000
to #ffffff
, thereby mostly but not entirely inverting the masking behavior.
This seems like a bug in Mail. Is there a way to tell Mail to not mess with the colors defined in the mask? I tried a couple workarounds, but they were insufficient:
prefers-color-scheme
to define the mask's colors so I can "pre-invert" it for dark mode, which works here but breaks the image in every other contextlinear-gradient(#ffffff,#ffffff)
instead of just #ffffff
), which just breaks the images everywhereWorking off of ccprog's suggestion, I got this SVG to work as expected:
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
<defs>
<mask id="mask" mask-type="alpha">
<path
d="
M 0 0
L 256 0
L 256 256
L 0 256
L 0 0
M 128 96
a 32 32 180 1 1 0 64
a 32 32 180 1 1 0 -64
z
"
fill-rule="evenodd"
/>
</mask>
</defs>
<circle mask="url(#mask)" cx="128" cy="128" r="64" fill="#ff0000" />
</svg>
In addition to the switch to mask-type
(so that we are masking using something that iOS/macOS Mail won't try to invert), the mask had to be redefined. In particular, it had to fill in everywhere that isn't in the circle, using fill-rule="evenodd"
to get it to fill in the desired areas. I couldn't just switch the existing shapes to use opacity, because the circle
would be want to be opacity 0 (in order to specify that the circle should be cut out) and the rect
would come through behind it with opacity 1, making the mask a no-op.
The new mask path, if rendered in black against a white background, looks like this: