Skip to content

Commit 6a61de0

Browse files
authored
Backport five accessibility fixes to prerelease (#2271)
* Fix skipped heading levels in markdown headings (#2227) * Heading order fixes * Remove extra h1 and reflow * Fix headings that should have been "captions" * Update freeze (cherry picked from commit c56f6cb) * Fix contrast of the extensions chooser and listing description links (#2235) * Use the theme link color for the extensions category chooser The chooser tiles hard-coded #5092dd, which is 3.23:1 on white (WCAG 1.4.3 needs 4.5:1). var(--bs-link-color) gives 5.15:1 in light mode and 7.27:1 in dark mode. Clears axe color-contrast-0f9515 (20 instances on 5 pages). * Underline links in listing-table descriptions Quarto's listing-table styles remove link underlines, so description links rely on color alone: 2.23:1 against body text, short of the 3:1 WCAG 1.4.1 needs. Clears axe link-in-text-block-82c7eb and the extensions-page instances of link-in-text-block-7b0c90. (cherry picked from commit e26940f) * Make the tool choosers consistent (#2258) * Make the tool choosers consistent with a shared page-chooser shortcode * Use --bs-border-color for chooser tiles in light mode (cherry picked from commit dab304a) * Close the listing-link rule so the visually-hidden code rule applies again (#2257) #2235 left out the closing brace of the listing-description link rule. Browsers then read the #2234 rule for screen-reader-only code blocks as a nested rule that never matches, and the blocks became scrollable regions again (axe scrollable-region-focusable). (cherry picked from commit 6f27b3f) * fix(a11y): give embedded videos an iframe title (#2141) Screen readers announce an iframe by its title. The video shortcode emits title="" when the author omits the title argument, so these two embeds failed axe's frame-title check (serious, WCAG 2.0 A, SC 4.1.2). Add title="CERN: The Journey of Discovery" to the two live {{< video >}} calls, and to the escaped copies the same pages display as source, so the shown example matches the rendered output. In tables.qmd the call sits inside a data-qmd="..." attribute, so the title uses single quotes. (cherry picked from commit ccf7a9e)
1 parent 742450e commit 6a61de0

55 files changed

Lines changed: 483 additions & 526 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/rules/docs-authoring.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ When referring to a Quarto format by its identifier (the value used in `format:`
2222
## Toolbar icons
2323

2424
Refer to a UI control by its name in bold, then "button" if it is one, then the icon as a decorative image: `**Render** button ![](images/rstudio-render-button.png){.ui-icon alt="" width="25" height="20"}`. Never wrap icons or control names in `<kbd>` (it is for keyboard input, and `kbd.js` uppercases one-word `<kbd>` text on macOS). Never leave an icon as the only name for a control. Use the name the control itself exposes (label, tooltip, or accessible name). See the "Toolbar Icons" section of `_style-guide.md`.
25+
26+
## Headings and example labels
27+
28+
Use a heading only to start a section, and never skip a level. A label for one example ("Markdown Syntax", "Output", "HTML output", a theme name under a screenshot) is a caption, not a heading. Label code blocks with `filename="..."`, images with a caption (plus `fig-cap-location: top` for a sequence of images), output with a lead-in sentence ("This renders as:"), and source/output pairs with a table. A bold paragraph is the fallback. Never use `{.unlisted}` headings as captions. When you fix an axe `heading-order` finding, first decide if the skipped heading is a real section. If it is a caption, replace it; do not change its level. See the "Headings and Example Labels" section of `_style-guide.md`.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
title: Page chooser
2+
author: Quarto
3+
version: 1.0.0
4+
quarto-required: ">=1.9.0"
5+
contributes:
6+
shortcodes:
7+
- page-chooser.lua
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* page-chooser: plain CSS using Bootstrap custom properties, so it works in
2+
light and dark themes and can be renamed to an SCSS partial unchanged. */
3+
4+
.page-chooser {
5+
margin-bottom: 1rem;
6+
}
7+
8+
/* The dark theme's --bs-border-color is near-invisible (1.08:1), so keep
9+
#2237's border until the theme's $border-color is fixed. Light mode uses
10+
--bs-border-color as is. */
11+
body.quarto-dark .page-chooser {
12+
--page-chooser-border: #6c757d;
13+
}
14+
15+
.page-chooser .page-chooser-label {
16+
margin-top: 1rem;
17+
margin-bottom: 0.25rem;
18+
}
19+
20+
.page-chooser .page-chooser-list {
21+
display: flex;
22+
flex-wrap: wrap;
23+
gap: 10px;
24+
list-style: none;
25+
margin: 0;
26+
padding: 0;
27+
}
28+
29+
.page-chooser .page-chooser-list a {
30+
display: block;
31+
width: var(--page-chooser-item-width, 102px);
32+
height: 100%;
33+
padding: 0.5rem 0.25rem;
34+
text-align: center;
35+
font-size: 0.8em;
36+
color: var(--bs-body-color);
37+
text-decoration: none;
38+
border: 1px solid var(--page-chooser-border, var(--bs-border-color));
39+
border-radius: 10px;
40+
}
41+
42+
.page-chooser .page-chooser-list a:hover {
43+
border-color: var(--bs-link-color);
44+
}
45+
46+
.page-chooser .page-chooser-list a[aria-current="page"] {
47+
font-weight: 600;
48+
border-color: var(--bs-link-color);
49+
box-shadow: inset 0 0 0 1px var(--bs-link-color);
50+
}
51+
52+
.page-chooser .page-chooser-list img {
53+
display: block;
54+
width: 65px;
55+
height: 65px;
56+
margin: 0 auto 2px;
57+
}
58+
59+
.page-chooser .page-chooser-list .bi {
60+
display: block;
61+
font-size: 3rem;
62+
line-height: 1;
63+
margin-bottom: 5px;
64+
color: var(--bs-link-color);
65+
}
66+
67+
.page-chooser .page-chooser-list .text-editor-logo {
68+
opacity: 0.7;
69+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Mark the link to the current page and remember it, for every .page-chooser.
2+
(function () {
3+
const filename = window.location.pathname.split("/").slice(-1)[0];
4+
for (const chooser of document.querySelectorAll(".page-chooser")) {
5+
const isIndex = filename === "" || filename === "index.html";
6+
const target = isIndex && chooser.dataset.default ? chooser.dataset.default : filename;
7+
for (const link of chooser.querySelectorAll("a")) {
8+
if (target && link.pathname.split("/").slice(-1)[0] === target) {
9+
link.setAttribute("aria-current", "page");
10+
break;
11+
}
12+
}
13+
if (chooser.dataset.storageKey && filename) {
14+
window.localStorage.setItem(chooser.dataset.storageKey, filename);
15+
}
16+
}
17+
})();
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
-- page-chooser: a labelled row of links to sibling pages ("Choose your tool").
2+
--
3+
-- Usage: {{< page-chooser tools >}}
4+
--
5+
-- Reads `page-choosers.<name>` from document metadata (usually a directory
6+
-- _metadata.yml):
7+
--
8+
-- page-choosers:
9+
-- tools:
10+
-- label: Choose your tool # visible label; also names the <nav>
11+
-- storage-key: tutorialToolGetStarted # optional: remember the last page
12+
-- default: listing-filters.html # optional: item marked current on index.html
13+
-- item-width: 147px # optional: tile width (default 102px)
14+
-- items:
15+
-- - text: Positron
16+
-- href: positron.html
17+
-- img: ../images/positron-logo.svg # decorative logo, or
18+
-- icon: gear # a Bootstrap icon name
19+
-- icon-class: text-editor-logo # optional extra class on the logo
20+
21+
local function str(v)
22+
if v == nil then return nil end
23+
return pandoc.utils.stringify(v)
24+
end
25+
26+
local function esc(s)
27+
return (s:gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;"):gsub('"', "&quot;"))
28+
end
29+
30+
local function attr(name, value)
31+
if value == nil or value == "" then return "" end
32+
return " " .. name .. '="' .. esc(value) .. '"'
33+
end
34+
35+
return {
36+
["page-chooser"] = function(args, kwargs, meta)
37+
if not quarto.doc.is_format("html") then
38+
return pandoc.Null()
39+
end
40+
41+
local name = str(args[1])
42+
local choosers = meta["page-choosers"]
43+
local cfg = name and choosers and choosers[name]
44+
if cfg == nil then
45+
quarto.log.warning("page-chooser: no `page-choosers." .. tostring(name) .. "` in metadata")
46+
return pandoc.Null()
47+
end
48+
49+
quarto.doc.add_html_dependency({
50+
name = "page-chooser",
51+
version = "1.0.0",
52+
stylesheets = { "page-chooser.css" },
53+
scripts = { { path = "page-chooser.js", afterBody = true } },
54+
})
55+
56+
local label_id = name .. "-chooser-label"
57+
local width = str(cfg["item-width"])
58+
local style = width and ("--page-chooser-item-width: " .. width) or nil
59+
60+
local out = {}
61+
table.insert(out, '<div class="page-chooser"' ..
62+
attr("data-storage-key", str(cfg["storage-key"])) ..
63+
attr("data-default", str(cfg["default"])) .. '>')
64+
table.insert(out, '<p class="page-chooser-label h3"' .. attr("id", label_id) .. '>' ..
65+
esc(str(cfg.label) or "") .. '</p>')
66+
table.insert(out, '<nav' .. attr("aria-labelledby", label_id) .. '>')
67+
table.insert(out, '<ul class="page-chooser-list"' .. attr("style", style) .. '>')
68+
for _, item in ipairs(cfg.items or {}) do
69+
local logo = ""
70+
if item.img then
71+
logo = '<img' .. attr("src", str(item.img)) .. ' alt=""' ..
72+
attr("class", str(item["icon-class"])) .. '>'
73+
elseif item.icon then
74+
logo = '<i class="bi bi-' .. esc(str(item.icon)) ..
75+
(item["icon-class"] and (" " .. esc(str(item["icon-class"]))) or "") ..
76+
'" aria-hidden="true"></i>'
77+
end
78+
table.insert(out, '<li><a' .. attr("href", str(item.href)) .. '>' ..
79+
logo .. esc(str(item.text) or "") .. '</a></li>')
80+
end
81+
table.insert(out, '</ul>')
82+
table.insert(out, '</nav>')
83+
table.insert(out, '</div>')
84+
85+
return pandoc.RawBlock("html", table.concat(out, "\n"))
86+
end
87+
}

‎_freeze/docs/authoring/tables/execute-results/html.json‎

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

‎_freeze/docs/computations/execution-options/execute-results/html.json‎

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)