quarto-dev / quarto-dev/quarto-cli

`axe: output: document`: report never updates after page state changes

Open
#14,668 0 comments 0 reactions 1 assignee View on GitHub

@cwickham is already working on this.

Since Jul 16, 2026.

accessibility html
Dominant language
JavaScript
Stars
6k
Forks
458
Avg merge
1d 9h
Merged PRs (30d)
41

Description

Description

The interactive axe overlay (axe: output: document) is Quarto's tool for an author eyeballing accessibility while editing under quarto preview. It runs axe-core exactly once, at page load. But axe-core evaluates the rendered, visible DOM: color-contrast is computed from the colours on screen right now, and hidden elements (display: none) are skipped entirely. So three ordinary reader interactions invalidate the report, and the overlay gives no indication it's showing results for a state the page is no longer in:

  1. Toggling dark/light mode. With theme: {light: ..., dark: ...}, the initial scan runs in whichever scheme loads first. Toggle the scheme and every color-contrast result is potentially wrong. A full page reload re-scans in the persisted scheme, but nothing tells the author a reload is needed.
  2. Expanding collapsed content. Violations inside collapsed callouts, folded code, closed tabsets, etc. are invisible to the load-time scan. This case is worse than stale: expanding doesn't trigger a rescan, and a reload re-collapses the content — so these violations are unreachable by the overlay no matter what the author does.
  3. Changing window size. Many of our components collapse on small viewports. A user may simulate this by resizing their browser window, but has to reload the page to refresh the scan.

Dashboards already solve this: setupDashboardRescan re-runs axe and rebuilds the report on shown.bs.tab, popstate, and bslib.sidebar — but it's gated to body.quarto-dashboard:

Proposal: extend the same rescan to regular HTML documents when output: document:

  • rescan when the colour scheme toggles (the .quarto-color-scheme-toggle click handler / toggleColorMode);
  • rescan on Bootstrap state events, as dashboards do: shown.bs.collapse / hidden.bs.collapse (callouts, code folding), shown.bs.tab (tabsets), plus shown.bs.dropdown / shown.bs.modal / shown.bs.offcanvas where relevant;
  • and/or a manual "Rescan" button in the overlay as the catch-all, so the author can always refresh the report to match whatever state they've put the page in.

Most of the machinery exists; the work is ungating it and pointing the re-render at the overlay report element rather than the dashboard offcanvas.

This is deliberately scoped to the interactive overlay under quarto preview. Automated coverage of viewport/theme/interaction states (for output: json, CI, etc.) is a different problem I'll write up separately.

Steps to reproduce

---
title: "Axe overlay staleness repro"
format:
  html:
    theme:
      light: flatly
      dark: darkly
    axe:
      output: document
---

## Dark-mode-only contrast failure

<p style="color: #555555;">This paragraph is `#555555` — fine on flatly's white
background, but low contrast on darkly's dark background.</p>

## Collapsed callout hiding a violation

::: {.callout-note collapse="true" title="Expand me"}
<img src="figure.png" width="50" height="50">

The image above has no alt text — invisible to axe while collapsed.
:::

(figure.png is any image; it exists only to carry a missing alt.)

  1. quarto preview the document (the overlay needs http:; module scripts don't load from file:).
  2. Note the overlay report at the bottom of the page.
  3. Click the dark-mode toggle (top right). The page turns dark; the overlay doesn't change.
  4. Expand the callout. The overlay doesn't change.

Actual behavior

The overlay shows the load-time scan forever. Verified by driving headless Chrome over CDP and comparing the overlay's contents against a fresh in-page axe.run() after each interaction (Quarto 1.10.12):

State Overlay shows Fresh axe.run() finds
Load (light, collapsed) load-time results same as overlay
After dark toggle unchanged + color-contrast (2 nodes: the #555555 paragraph and its inline code)
After expanding callout unchanged + image-alt (the callout's image)

Reloading the page in dark mode (the scheme persists via localStorage) does surface the color-contrast violations — confirming they're real and that a fresh scan catches them — but the callout re-collapses on reload, so the image-alt violation can never appear in the overlay.

Expected behavior

The overlay report reflects the page state the author is looking at: it rescans (automatically on scheme toggle and expand/collapse events, or at minimum via a manual "Rescan" button) so that mode-specific contrast failures and violations inside collapsible content are catchable during interactive checking, the way they already are in dashboards.

Your environment

  • Quarto 1.10.12
  • macOS 26.5.1 (build 25F80), Chrome (headless, CDP) for verification

AI-assisted investigation, grounded in the rendered output and the quarto-cli source at commit 38ef483 (per CONTRIBUTING.md "Using AI tools to investigate").

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.