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);
+});