diff --git a/client/src/utils/__tests__/svg.test.ts b/client/src/utils/__tests__/svg.test.ts
index ad86bb6cb1..3740685742 100644
--- a/client/src/utils/__tests__/svg.test.ts
+++ b/client/src/utils/__tests__/svg.test.ts
@@ -466,6 +466,12 @@ describe('isMonochromeSvg', () => {
expect(isMonochromeSvg(svg)).toBe(true);
});
+ it('ignores a background rect in an opacity-zero group despite its own fill-opacity', () => {
+ const svg =
+ '';
+ expect(isMonochromeSvg(svg)).toBe(true);
+ });
+
it('rejects a full-size percentage background rect', () => {
const svg =
'';
@@ -527,6 +533,20 @@ describe('isMonochromeSvg', () => {
});
});
+ describe('opacity-zero ancestors hide their descendants', () => {
+ it('ignores a colored paint inside an opacity-zero group despite its own fill-opacity', () => {
+ const svg =
+ '';
+ expect(isMonochromeSvg(svg)).toBe(true);
+ });
+
+ it('still counts a paint whose fill-opacity is one under a visible group', () => {
+ const svg =
+ '';
+ expect(isMonochromeSvg(svg)).toBe(false);
+ });
+ });
+
describe('embedded raster/foreign content (not tintable)', () => {
it('rejects an SVG wrapping an embedded raster image (href)', () => {
const svg = '';
diff --git a/client/src/utils/svg.ts b/client/src/utils/svg.ts
index c70d4eaa06..72b4db2b57 100644
--- a/client/src/utils/svg.ts
+++ b/client/src/utils/svg.ts
@@ -513,9 +513,11 @@ function isHidden(el: Element, root: Element, rules: StyleRule[]): boolean {
}
/**
- * True when a paint is fully transparent through opacity: the element (or an
- * ancestor group) has `opacity:0`, or the inherited paint-specific opacity (e.g.
- * `fill-opacity`) resolves to zero.
+ * True when a paint is fully transparent through opacity: the element or any
+ * ancestor group has `opacity:0` (group opacity is not inherited and composites
+ * the whole subtree), or the nearest paint-specific opacity (e.g. `fill-opacity`)
+ * resolves to zero. The nearest paint-specific opacity wins, but the walk keeps
+ * checking ancestors for `opacity:0` even after a nonzero one is found.
*/
function paintInvisible(
el: Element,
@@ -524,15 +526,19 @@ function paintInvisible(
paintProp: string,
): boolean {
const opacityProp = PAINT_OPACITY.get(paintProp);
+ let paintOpacityResolved = false;
let current: Element | null = el;
while (current != null) {
if (styleNumber(current, rules, 'opacity') === 0) {
return true;
}
- if (opacityProp != null) {
+ if (opacityProp != null && !paintOpacityResolved) {
const value = styleNumber(current, rules, opacityProp);
if (value != null) {
- return value === 0;
+ if (value === 0) {
+ return true;
+ }
+ paintOpacityResolved = true;
}
}
if (current === root) {