fix(target-size): ignore position: fixed elements that are offscreen when page is scrolled#5066
fix(target-size): ignore position: fixed elements that are offscreen when page is scrolled#5066WilcoFiers merged 7 commits intodevelopfrom
Conversation
…when page is scrolled
There was a problem hiding this comment.
Pull request overview
This PR fixes a target-size false-positive when the page is scrolled: position: fixed elements that are outside the viewport (e.g., above the top edge) should remain ignored rather than being treated as on-screen due to scroll offsets.
Changes:
- Update offscreen detection to treat fixed-position subtrees differently (use viewport-relative coordinates).
- Introduce a new
dom.isFixedPositionhelper and export it fromaxe.commons.dom. - Add/adjust unit + integration tests to cover fixed-position + scroll scenarios.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| test/integration/full/target-size/fixed-scroll.html | New integration fixture page with fixed elements and large scroll height. |
| test/integration/full/target-size/fixed-scroll.js | New integration test that scrolls and asserts target-size yields no violations. |
| test/commons/dom/is-visible.js | Updates tests to set up the flat tree before calling dom.isVisible. |
| test/commons/dom/is-offscreen.js | Refactors tests and adds fixed-position scroll coverage for dom.isOffscreen. |
| test/commons/dom/is-fixed-position.js | Adds unit tests for the new dom.isFixedPosition helper. |
| lib/commons/dom/is-offscreen.js | Adjusts isOffscreen to use getBoundingClientRect() for fixed-position subtrees and adds a fixed-bottom check. |
| lib/commons/dom/is-fixed-position.js | Adds the new helper to detect whether a node is in a position: fixed subtree. |
| lib/commons/dom/index.js | Exports isFixedPosition from the dom namespace. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export default function isFixedPosition(node, { skipAncestors } = {}) { | ||
| const { vNode } = nodeLookup(node); | ||
|
|
||
| // detached element | ||
| if (!vNode) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
isFixedPosition returns false when nodeLookup can’t find a vNode. For an Element input this can happen simply because axe.setup()/flatTreeSetup() hasn’t been run yet (even though the element is connected and actually position: fixed). Since this is exported as VirtualNode|Element, consider adding a DOM-only fallback (e.g., check getComputedStyle(node).position and traverse composed parents) or documenting/enforcing the requirement that a virtual tree must exist.
There was a problem hiding this comment.
Not necessary. We can assume setup was run in commons.
| return done(err); | ||
| } | ||
| results = r; | ||
| console.log(results); |
There was a problem hiding this comment.
Should we leave this log in? It is only a test, but still adds cruft to the output data.
There was a problem hiding this comment.
Definitely not. 🤨 I saw that and didn't even question it.
There was a problem hiding this comment.
Looks like Steve copied it. We have it in a couple more places. I'll just clean those up as well.
lib/commons/dom/is-offscreen.js
Outdated
| //This is an edge case, an empty (zero-width) element that isn't positioned 'off screen'. | ||
| return false; | ||
| if (isFixed && coords.top >= window.innerHeight) { | ||
| return true; // Fixed above the viewport |
There was a problem hiding this comment.
Is coords.top >= window.innerHeight not determining if the element is below the Viewport? Is the comment wrong, the code, or my understanding of it?
There was a problem hiding this comment.
Yeah, comment is wrong.
lib/commons/dom/is-offscreen.js
Outdated
| return true; // Fixed above the viewport | ||
| } | ||
|
|
||
| if (isFixed && coords.left >= window.innerWidth) { |
There was a problem hiding this comment.
Was RTL mode considered here? This feels like in that context things might slip through.
There was a problem hiding this comment.
I had, twice. But thinking about it a third time there is a little edge case here we missed. Cheers!
chutchins25
left a comment
There was a problem hiding this comment.
One question for you. Everything else was already fixed or found by Garbee.
| if ( | ||
| coords.bottom < 0 && | ||
| coords.bottom <= 0 && | ||
| (noParentScrolled(domNode, coords.bottom) || styl.position === 'absolute') |
There was a problem hiding this comment.
Do we need to bypass this for fixed elements?
There was a problem hiding this comment.
What situation would propose that we should?
There was a problem hiding this comment.
Yeah this code is buggy. It's not a new problem. I looked at fixing it while I was in here but it's too complex. I don't want to rush into it so I opened an issue for it instead #5069
closes #5065