diff --git a/.changeset/strong-islands-relax.md b/.changeset/strong-islands-relax.md new file mode 100644 index 0000000000..580a77e1a3 --- /dev/null +++ b/.changeset/strong-islands-relax.md @@ -0,0 +1,9 @@ +--- +"@siteimprove/alfa-style": patch +--- + +**Fixed:** `` elements are now considered as respecting their specified dimensions + +`` elements whose `width` or `height` is specified are now considered to respect it when computing their concrete dimensions (i.e., they rescale rather than overflow, independantly from the `overflow` property). + +This is especially meaningful for tracking pixels with specified dimensions of 0 that are now correcly considered as invisible. diff --git a/packages/alfa-style/src/node/predicate/is-clipped.ts b/packages/alfa-style/src/node/predicate/is-clipped.ts index 6da7f9b0bf..4873b3ed04 100644 --- a/packages/alfa-style/src/node/predicate/is-clipped.ts +++ b/packages/alfa-style/src/node/predicate/is-clipped.ts @@ -105,6 +105,7 @@ function isClippedBySize( const y = style.computed("overflow-y").value.value; const width = style.computed("width").value; const height = style.computed("height").value; + const overflows = test(canOverflow(device), element); // Does the element always show a scrollbar, no matter whether there is // enough room in it to show the content? @@ -115,7 +116,7 @@ function isClippedBySize( ["height", y, height], ] as const) { // Is the element reduced to nothingness in this axis? - if (overflow === "visible") { + if (overflow === "visible" && overflows) { // The content overflows in this axis, go to next axis. continue; } @@ -296,3 +297,25 @@ function isClipping(elementBox: Rectangle, device: Device): Predicate { )(ancestor); }; } + +/** + * Check if an element scales to its specified dimensions + * + * @remarks + * Replaced elements are not rendered by CSS directly. Instead, they have + * natural dimension (e.g., an image size) and specified ones (e.g., the width + * CSS property). Negotiation between the two computes a concrete size with which + * the element is effectively render. In practice, the concrete size is often + * the specified one, and while the rendering may result in smaller or larger + * object, it is also often fitting quite well. + * {@link https://drafts.csswg.org/css-images/#sizing} + * + * @privateRemarks + * For now, we mostly use this to discard the check on overflow for . + * seem to be always rendered at their specified size, if any. This is + * especially important here for tracking pixels whose specified size is 0 + * and we need to treat them as invisible. + */ +function canOverflow(device: Device): Predicate { + return not(Element.hasName("img")); +} diff --git a/packages/alfa-style/test/node/predicate/is-visible.spec.tsx b/packages/alfa-style/test/node/predicate/is-visible.spec.tsx index d05e441de7..05821f8f4e 100644 --- a/packages/alfa-style/test/node/predicate/is-visible.spec.tsx +++ b/packages/alfa-style/test/node/predicate/is-visible.spec.tsx @@ -313,3 +313,17 @@ test(`isVisible() returns true for text with the same color as their background t.equal(isVisible(text), true); }); + +test(`isVisible() consider that images' concrete dimensions are the specified ones`, (t) => { + const img = ( + + ); + const div = ( +
Hello World
+ ); + + h.document([img, div]); + + t.equal(isVisible(img), false); + t.equal(isVisible(div), true); +});