Skip to content

Axe overlay polish: left-align report and scroll highlighted elements clear of it#14680

Open
cwickham wants to merge 6 commits into
axe/vendor-axe-corefrom
axe/overlay-polish
Open

Axe overlay polish: left-align report and scroll highlighted elements clear of it#14680
cwickham wants to merge 6 commits into
axe/vendor-axe-corefrom
axe/overlay-polish

Conversation

@cwickham

@cwickham cwickham commented Jul 13, 2026

Copy link
Copy Markdown
Member

Description

Note

Stacked on #14677 (vendored axe-core) and based against that branch; when it merges this PR retargets to main automatically.

Three quality-of-life fixes to the axe: output: document fixed overlay in HTML output.

Left-align the report overlay. The overlay is appended into <main>, so page-level centering leaks into the report text — e.g. an about page with about: template: jolla ships div.quarto-about-jolla main { text-align: center } and centers the whole report. The reveal report slide already pins text-align: left; the base overlay rule now does the same.

Don't scroll highlighted elements underneath the overlay. Hovering a selector in the report scrolls the target element into view with scrollIntoView({block: "center"}), but the overlay is position: fixed; bottom: 3rem; right: 3rem; max-height: 50vh — a centered element (especially a full-width one) could land partly or wholly underneath it. The scroll target is now computed by a new exported pure helper, overlayAwareScrollTop, which centers the element in the viewport band above the overlay. Elements with no horizontal overlap keep the default centering, and only the plain-HTML fixed overlay takes this path: reveal navigates slides (the report is its own slide) and the dashboard offcanvas is a static full-height panel that vertical scrolling can't dodge. The overlay itself never moves or fades, so the mouse stays inside it during the scroll and no mouseleave → unhighlight flicker can fire.

Make the overlay itself pass its own scan. Running axe-core against the injected report (it never audits itself — it's added after the scan) flagged scrollable-region-focusable (serious, WCAG 2.1.1): once the report exceeds max-height: 50vh it scrolls, but wasn't keyboard-focusable. The overlay is now a focusable, labeled region (tabindex="0", role="region", aria-label), added only in the fixed-overlay path — the reveal slide and dashboard offcanvas don't scroll the report element itself, so they skip the extra tab stop. The violation-target spans remain mouse-only; full keyboard operability of the report is a separate issue.

Tests: unit tests for the helper's geometry (tests/unit/axe-overlay-scroll.test.ts), a Playwright test that hovers a violation near the bottom of a long page and asserts the highlighted element settles fully above the overlay (verified to fail against the old scrolling behavior), and assertions on the existing HTML overlay test for text-align: left and the focusable-region attributes. A self-scan regression test runs the vendored axe-core build (window.axe) against the injected overlay on the long-report page and asserts zero violations — the overlay is added after the page scan, so nothing else ever audits it (verified this test fails with scrollable-region-focusable when the fix is removed).

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes
  • updated the appropriate changelog in the PR
  • ensured the present test suite passes
  • added new tests
  • created a separate documentation PR in Quarto's website repo and linked it to this PR
AI-assisted PR
  • AI tool used: Claude Code
  • Codebase grounding: local clone
  • Human review: I have reviewed, tested, and verified the AI-generated content before submitting.

@posit-snyk-bot

posit-snyk-bot commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

cwickham added 6 commits July 13, 2026 10:54
The overlay is appended into <main>, so page-level centering (e.g. the
jolla about template's 'div.quarto-about-jolla main { text-align:
center }') centered the report text. The reveal report slide already
pins text-align: left; do the same in the base overlay rule.
Hovering a selector in the report scrolled the target element to the
viewport center with scrollIntoView({block: 'center'}), where the fixed
bottom-right overlay (max-height: 50vh) could cover it. Compute the
scroll target with a new exported pure helper, overlayAwareScrollTop,
that centers the element in the viewport band above the overlay instead;
elements with no horizontal overlap (or when the overlay leaves no
usable band) keep the default centering. Only the plain-HTML fixed
overlay takes this path: reveal navigates slides and the dashboard
offcanvas is a static full-height panel that vertical scrolling can't
dodge.

The overlay itself never moves or fades, so the mouse stays inside it
during the scroll and no mouseleave -> unhighlight flicker can fire.

Unit tests cover the helper's geometry; a Playwright test hovers a
violation near the bottom of a long page (with enough violations to
inflate the report to its max-height, without which default centering
already clears the overlay and the test has no teeth) and asserts the
highlighted element settles fully above the overlay. The spec also
gains a text-align: left assertion on the HTML overlay for the
left-alignment fix.
…board

The overlay scrolls once the report exceeds max-height: 50vh, but wasn't
keyboard-focusable — failing axe's own scrollable-region-focusable rule
(serious, WCAG 2.1.1) when scanned directly. Add tabindex=0 plus
role/aria-label in createReportOverlay only: the reveal slide and
dashboard offcanvas variants don't scroll the report element itself, so
they don't get the extra tab stop.

Verified by running the pinned axe-core build against the rendered
overlay on the long-report test page: scrollable-region-focusable before
the change, no violations after. Note the violation-target spans remain
mouse-only; full keyboard operability of the report is tracked
separately.
The overlay is injected after the page scan, so it never audits itself
and nothing else guards against new violations as the report UI grows.
Scan it with the vendored axe-core build the page already loaded
(window.axe), on the long-report page where the overlay is scrollable.
Requires the vendored axe-core from #14677, which this branch is now
stacked on. Verified the test fails with scrollable-region-focusable
when the tabindex fix is removed.
@cwickham cwickham force-pushed the axe/overlay-polish branch from 27cc6b2 to fa02ea5 Compare July 13, 2026 17:56
@cwickham cwickham changed the base branch from main to axe/vendor-axe-core July 13, 2026 17:56
@cwickham

Copy link
Copy Markdown
Member Author

Stacked on #14677 — please don't merge this until #14677 has landed. The base branch is set to axe/vendor-axe-core, so GitHub will retarget this PR to main automatically once #14677 merges; it should then show only the overlay-polish commits.

@cwickham cwickham requested a review from cderv July 13, 2026 18:44
@cwickham cwickham marked this pull request as ready for review July 13, 2026 18:44
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.

2 participants