Skip to content

Commit 647bce4

Browse files
authored
fix(html/axe): use literal overlay colors to avoid clobbering brand (#14471)
* fix(html/axe): use literal colors in axe overlay so it doesn't clobber brand (#14468) The axe report's sass bundle declared `$body-bg`, `$body-color`, and `$link-color` defaults in its user SassLayer, which precede brand's user defaults during sass compilation and so clobbered brand colors on the page. Since the axe report is developer chrome (not document content), its overlay rules now use literal hex values instead of theme-derived SCSS variables. Brand and theme colors win on the page; the overlay stays readable regardless of how the page is themed. Same treatment applied to the revealjs report slide (force #fff slide background via data-background-color and #222 text) and the dashboard report offcanvas (override --bs-offcanvas-bg/-color to axe defaults). * Update changelog entry to lead with the UI behavior change * Move axe entry under new ## Accessibility heading * Use WCAG AA-safe link color in axe overlay (#0a58ca) * Use WCAG AAA-passing link color in axe overlay (#0747a6)
1 parent 6ade5e0 commit 647bce4

7 files changed

Lines changed: 123 additions & 17 deletions

File tree

news/changelog-1.10.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ All changes included in 1.10:
77
- ([#14298](https://github.com/quarto-dev/quarto-cli/issues/14298)): Fix `quarto preview` browse URL including output filename (e.g., `hello.html`) for single-file documents, breaking Posit Workbench proxied server access.
88
- ([rstudio/rstudio#17333](https://github.com/rstudio/rstudio/issues/17333)): Fix `quarto inspect` on standalone files emitting project metadata that breaks RStudio's publishing wizard.
99

10+
## Accessibility
11+
12+
- ([#14468](https://github.com/quarto-dev/quarto-cli/issues/14468)): The `axe` accessibility report UI (HTML overlay, revealjs report slide, dashboard offcanvas) now uses its own theme-independent colors instead of inheriting from `brand` or theme. Keeps the report readable regardless of page styling, and stops `axe` from clobbering brand colors set via `_brand.yml`.
13+
1014
## Formats
1115

1216
### `pdf`

src/format/html/format-html-axe.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,27 @@ export function axeFormatDependencies(
3535
): FormatExtras {
3636
if (!options) return {};
3737

38-
// Use reveal-theme for revealjs, bootstrap for other HTML formats.
39-
// Note: For revealjs, sass-bundles compile separately from the theme
40-
// (which compiles in format-reveal-theme.ts), so the !default values
41-
// below are used instead of actual theme colors. This is a known
42-
// limitation - see GitHub issue for architectural context.
38+
// The axe report is developer chrome, not document content, so its colors
39+
// are literals here rather than theme-derived (`$body-bg` etc.). This keeps
40+
// the overlay readable regardless of the page's brand or theme — which
41+
// matters most when the user is auditing exactly those colors.
4342
const isRevealjs = isRevealjsOutput(format.pandoc);
4443
const isDashboard = isHtmlDashboardOutput(format.identifier["base-format"]);
4544
const sassDependency = isRevealjs ? "reveal-theme" : "bootstrap";
4645

47-
// Base overlay rules shared by all formats (also serves as fallback for revealjs)
46+
// Base overlay rules shared by all formats. For revealjs, the `var(--r-*)`
47+
// lookups pick up the slide's actual colors at runtime; the literal
48+
// fallbacks apply everywhere else.
4849
const baseRules = `
4950
body div.quarto-axe-report {
5051
position: fixed;
5152
bottom: 3rem;
5253
right: 3rem;
5354
padding: 1rem;
54-
border: 1px solid var(--r-main-color, $body-color);
55+
border: 1px solid var(--r-main-color, #222);
5556
z-index: 9999;
56-
background-color: var(--r-background-color, $body-bg);
57-
color: var(--r-main-color, $body-color);
57+
background-color: var(--r-background-color, #fff);
58+
color: var(--r-main-color, #222);
5859
max-height: 50vh;
5960
overflow-y: auto;
6061
}
@@ -63,7 +64,7 @@ body div.quarto-axe-report {
6364
.quarto-axe-violation-selector { padding-left: 1rem; }
6465
.quarto-axe-violation-target {
6566
padding: 0.5rem;
66-
color: $link-color;
67+
color: #0747a6;
6768
text-decoration: underline;
6869
cursor: pointer;
6970
}
@@ -73,21 +74,26 @@ body div.quarto-axe-report {
7374
border: 2px solid red;
7475
}`;
7576

76-
// RevealJS: override overlay styles when report is embedded as a slide
77+
// RevealJS: override overlay styles when report is embedded as a slide.
78+
// The slide's white background is set by the JS via data-background-color;
79+
// here we only need to keep text/link colors readable on white.
7780
const revealjsRules = isRevealjs
7881
? `
7982
.reveal .slides section.quarto-axe-report-slide {
8083
text-align: left;
8184
font-size: 0.55em;
85+
color: #222;
8286
h2 {
8387
margin-bottom: 0.5em;
8488
font-size: 1.8em;
89+
color: #222;
8590
}
8691
div.quarto-axe-report {
8792
position: static;
8893
padding: 0;
8994
border: none;
9095
background-color: transparent;
96+
color: #222;
9197
max-height: none;
9298
overflow-y: visible;
9399
z-index: auto;
@@ -102,15 +108,20 @@ body div.quarto-axe-report {
102108
}`
103109
: "";
104110

105-
// Dashboard: report inside offcanvas sidebar (not fixed overlay)
111+
// Dashboard: report inside offcanvas sidebar (not fixed overlay).
112+
// Override Bootstrap's themed offcanvas vars so the panel keeps axe's own
113+
// colors regardless of brand/theme.
106114
const dashboardRules = isDashboard
107115
? `
108116
.quarto-dashboard .offcanvas.quarto-axe-offcanvas {
117+
--bs-offcanvas-bg: #fff;
118+
--bs-offcanvas-color: #222;
109119
.quarto-axe-report {
110120
position: static;
111121
padding: 0;
112122
border: none;
113123
background-color: transparent;
124+
color: #222;
114125
max-height: none;
115126
overflow-y: visible;
116127
z-index: auto;
@@ -148,11 +159,7 @@ body div.quarto-axe-report {
148159
dependency: sassDependency,
149160
user: [{
150161
uses: "",
151-
defaults: `
152-
$body-color: #222 !default;
153-
$body-bg: #fff !default;
154-
$link-color: #2a76dd !default;
155-
`,
162+
defaults: "",
156163
functions: "",
157164
mixins: "",
158165
rules: baseRules + revealjsRules + dashboardRules,

src/resources/formats/html/axe/axe-check.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ class QuartoAxeDocumentReporter extends QuartoAxeReporter {
140140

141141
const section = document.createElement("section");
142142
section.className = "slide quarto-axe-report-slide scrollable";
143+
// Force a white slide background so the report stays readable regardless
144+
// of the deck's brand/theme colors. Reveal applies this to the slide's
145+
// generated `.slide-background` element during sync().
146+
section.setAttribute("data-background-color", "#fff");
143147

144148
const title = document.createElement("h2");
145149
title.textContent = "Accessibility Report";
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
title: "Issue 14468 - axe must not clobber brand colors"
3+
format:
4+
html:
5+
axe:
6+
output: document
7+
brand:
8+
color:
9+
background: "#5e1717"
10+
typography:
11+
link:
12+
color: "#18a918"
13+
_quarto:
14+
tests:
15+
html:
16+
ensureCssRegexMatches:
17+
- ['--bs-body-bg:\s*#5e1717', '--bs-link-color:\s*#18a918', 'quarto-axe-report\s*\{[^}]*background-color:\s*var\(--r-background-color,\s*#fff\)']
18+
- ['--bs-body-bg:\s*#fff[\s;]']
19+
---
20+
21+
## Section
22+
23+
Some [link](http://example.org) text.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
title: "Issue 14468 - axe must not clobber brand colors (dashboard)"
3+
format:
4+
dashboard:
5+
axe:
6+
output: document
7+
brand:
8+
color:
9+
background: "#5e1717"
10+
typography:
11+
link:
12+
color: "#18a918"
13+
_quarto:
14+
tests:
15+
dashboard:
16+
ensureCssRegexMatches:
17+
- ['--bs-body-bg:\s*#5e1717', '--bs-link-color:\s*#18a918', 'quarto-axe-offcanvas\s*\{[^}]*--bs-offcanvas-bg:\s*#fff']
18+
- ['--bs-body-bg:\s*#fff[\s;]']
19+
---
20+
21+
## Row
22+
23+
Content for accessibility checking.
24+
25+
[example](http://example.org)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: "Issue 14468 - axe defaults still work without brand"
3+
format:
4+
html:
5+
axe: true
6+
_quarto:
7+
tests:
8+
html:
9+
ensureCssRegexMatches:
10+
- ['--bs-body-bg:\s*#fff', '\.quarto-axe-report']
11+
---
12+
13+
## Section
14+
15+
Some [link](http://example.org) text.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: "Issue 14468 - axe sass bundle still compiles for revealjs"
3+
format:
4+
revealjs:
5+
axe:
6+
output: document
7+
brand:
8+
color:
9+
background: "#5e1717"
10+
foreground: "#d6c50f"
11+
typography:
12+
link:
13+
color: "#18a918"
14+
_quarto:
15+
tests:
16+
revealjs:
17+
ensureCssRegexMatches:
18+
- ['\.quarto-axe-report', 'quarto-axe-report-slide\s*\{[^}]*color:\s*#222']
19+
- ['\$body-bg', '\$body-color', '\$link-color']
20+
---
21+
22+
## Slide 1
23+
24+
Content for accessibility checking. <http://example.org>.
25+
26+
## Slide 2
27+
28+
More content.

0 commit comments

Comments
 (0)