Skip to content

fix(target-size): ignore position: fixed elements that are offscreen when page is scrolled#5066

Merged
WilcoFiers merged 7 commits intodevelopfrom
target-size-fixed
Apr 10, 2026
Merged

fix(target-size): ignore position: fixed elements that are offscreen when page is scrolled#5066
WilcoFiers merged 7 commits intodevelopfrom
target-size-fixed

Conversation

@straker
Copy link
Copy Markdown
Contributor

@straker straker commented Apr 9, 2026

closes #5065

@straker straker requested a review from a team as a code owner April 9, 2026 22:40
@WilcoFiers WilcoFiers requested a review from Copilot April 10, 2026 08:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.isFixedPosition helper and export it from axe.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.

Comment on lines +10 to +16
export default function isFixedPosition(node, { skipAncestors } = {}) {
const { vNode } = nodeLookup(node);

// detached element
if (!vNode) {
return false;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary. We can assume setup was run in commons.

return done(err);
}
results = r;
console.log(results);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we leave this log in? It is only a test, but still adds cruft to the output data.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely not. 🤨 I saw that and didn't even question it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like Steve copied it. We have it in a couple more places. I'll just clean those up as well.

//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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, comment is wrong.

return true; // Fixed above the viewport
}

if (isFixed && coords.left >= window.innerWidth) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was RTL mode considered here? This feels like in that context things might slip through.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had, twice. But thinking about it a third time there is a little edge case here we missed. Cheers!

Copy link
Copy Markdown
Contributor

@chutchins25 chutchins25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to bypass this for fixed elements?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What situation would propose that we should?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@chutchins25 chutchins25 self-requested a review April 10, 2026 14:42
Copy link
Copy Markdown
Contributor

@chutchins25 chutchins25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@WilcoFiers WilcoFiers merged commit 1229a6e into develop Apr 10, 2026
23 checks passed
@WilcoFiers WilcoFiers deleted the target-size-fixed branch April 10, 2026 14:46
straker added a commit that referenced this pull request Apr 13, 2026
…when page is scrolled (#5066)

closes #5065

---------

Co-authored-by: Wilco Fiers <WilcoFiers@users.noreply.github.com>
Co-authored-by: Wilco Fiers <wilco.fiers@deque.com>
@straker straker mentioned this pull request Apr 13, 2026
WilcoFiers added a commit that referenced this pull request Apr 13, 2026
### Bug Fixes

- **aria-allowed-attr:** restrict br and wbr elements to aria-hidden
only ([#4974](#4974))
([1d80163](1d80163))
- **target-size:** ignore position: fixed elements that are offscreen
when page is scrolled
([#5066](#5066))
([5906273](5906273)),
closes [#5065](#5065)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

target-size should ignore position: fixed elements outside the viewport when the page is scrolled

5 participants