Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/changelog-1.11.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ All changes included in 1.11:
- ([#14952](https://github.com/quarto-dev/quarto-cli/issues/14952)): Fix `revealjs` presentations disabling pinch-zoom on touch devices. The viewport `<meta>` tag no longer sets `maximum-scale=1.0` and `user-scalable=no`, which axe-core flagged (`meta-viewport`).
- ([#15011](https://github.com/quarto-dev/quarto-cli/issues/15011)): Fix the slide menu of `revealjs` presentations being read by screen readers while it is closed and off screen, and Tab moving keyboard focus to it once it lists more slides than fit. (author: @jooyoungseo)
- ([#15015](https://github.com/quarto-dev/quarto-cli/issues/15015)): Fix the "Back to Article" link of notebook previews in website projects not being reachable or usable with the keyboard.
- ([#15030](https://github.com/quarto-dev/quarto-cli/pull/15030)): Fix `axe` checks in `revealjs` presentations hiding the current slide from screen readers when moving to another slide while the scan runs.

## Formats

Expand Down
18 changes: 10 additions & 8 deletions src/resources/formats/html/axe/axe-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,16 +493,18 @@ class QuartoAxeChecker {

revealRestoreSlides(saved) {
if (!saved) return;
saved.forEach(({ el, hidden, ariaHidden }) => {
if (hidden) el.setAttribute("hidden", "");
if (ariaHidden !== null) el.setAttribute("aria-hidden", ariaHidden);
});
// Recompute inert from live state, mirroring reveal.js's own rule (6.0.2+),
// so a slide change during the scan cannot leave the current slide inert.
// Use live state for the current slide, mirroring reveal.js's own inert
// rule (6.0.2+), so a slide change during the scan cannot leave the
// current slide hidden, aria-hidden or inert.
const allowAll = Reveal.isOverview() || Reveal.isScrollView() || Reveal.isPrintView();
const current = Reveal.getCurrentSlide();
saved.forEach(({ el }) => {
el.toggleAttribute("inert", !allowAll && el !== current && !el.contains(current));
saved.forEach(({ el, hidden, ariaHidden }) => {
const isCurrent = el === current || el.contains(current);
if (!isCurrent) {
if (hidden) el.setAttribute("hidden", "");
if (ariaHidden !== null) el.setAttribute("aria-hidden", ariaHidden);
}
el.toggleAttribute("inert", !allowAll && !isCurrent);
});
}

Expand Down
26 changes: 26 additions & 0 deletions tests/integration/playwright/tests/axe-accessibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,32 @@ test.describe('RevealJS axe — cross-slide scanning and state restoration', ()
await expect(highlightedImg).toBeAttached({ timeout: 3000 });
});

test('slide navigated to during the scan stays accessible afterwards', async ({ page }) => {
// Navigate as soon as the scan unhides the slides, i.e. while axe.run is pending.
await page.addInitScript(() => {
document.addEventListener('DOMContentLoaded', () => {
const observer = new MutationObserver(() => {
const slide2 = document.querySelectorAll('.reveal .slides > section')[1];
if (slide2 && !slide2.hasAttribute('aria-hidden')) {
observer.disconnect();
Reveal.slide(1);
}
});
observer.observe(document.body, {
subtree: true, attributes: true, attributeFilter: ['aria-hidden'],
});
});
});
await page.goto('/revealjs/axe-console.html', { waitUntil: 'networkidle' });
await waitForAxeCompletion(page);

expect(await page.evaluate(() => Reveal.getIndices().h)).toBe(1);
const current = page.locator('.reveal .slides > section.present');
await expect(current).not.toHaveAttribute('hidden');
await expect(current).not.toHaveAttribute('aria-hidden');
await expect(current).not.toHaveAttribute('inert');
});

test('detects image-alt violation on hidden vertical slide', async ({ page }) => {
await page.goto('/revealjs/axe-accessibility-vertical.html', { waitUntil: 'networkidle' });
const reportSlide = page.locator('section.quarto-axe-report-slide');
Expand Down
Loading