Skip to content
Open
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.10.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ All changes included in 1.10:
- ([#14602](https://github.com/quarto-dev/quarto-cli/pull/14602), [#14632](https://github.com/quarto-dev/quarto-cli/pull/14632)): Fix ORCID profile link having no accessible name for screen readers in HTML, Reveal.js, and ipynb title-block author metadata. (author: @mcanouil for #14602)
- ([#14604](https://github.com/quarto-dev/quarto-cli/issues/14604)): The `axe` accessibility report UI now shows each violation's WCAG conformance level (e.g. `WCAG 2.0 AA (1.4.3)`) or `Best Practice`, derived from the violation's axe-core tags.
- ([#14655](https://github.com/quarto-dev/quarto-cli/issues/14655)): Add accessible names to code line-number links so screen readers and accessibility audits no longer report them as empty links.
- ([#14684](https://github.com/quarto-dev/quarto-cli/issues/14684)): Add a "Skip to main content" link to Bootstrap-themed HTML output (documents, websites, books, dashboards) so keyboard users can bypass navigation blocks (WCAG 2.4.1). The link is the first tab stop on every page, visually hidden until focused, and its text is customizable via the `language` key `skip-to-content`. Dashboards additionally mark their content container as the `main` landmark.

## Formats

Expand Down
2 changes: 2 additions & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ export const kToggleNavigation = "toggle-navigation";
export const kCopyButtonTooltip = "copy-button-tooltip";
export const kCopyButtonTooltipSuccess = "copy-button-tooltip-success";
export const kBackToTop = "back-to-top";
export const kSkipToContent = "skip-to-content";
export const kRepoActionLinksEdit = "repo-action-links-edit";
export const kRepoActionLinksSource = "repo-action-links-source";
export const kRepoActionLinksIssue = "repo-action-links-issue";
Expand Down Expand Up @@ -436,6 +437,7 @@ export const kLanguageDefaultsKeys = [
kCopyButtonTooltip,
kCopyButtonTooltipSuccess,
kBackToTop,
kSkipToContent,
kRepoActionLinksEdit,
kRepoActionLinksSource,
kRepoActionLinksIssue,
Expand Down
2 changes: 2 additions & 0 deletions src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ import {
kSelfContainedMath,
kShiftHeadingLevelBy,
kShortcodes,
kSkipToContent,
kSlideLevel,
kSourceNotebookPrefix,
kStandalone,
Expand Down Expand Up @@ -696,6 +697,7 @@ export interface FormatLanguage {
[kCopyButtonTooltip]?: string;
[kCopyButtonTooltipSuccess]?: string;
[kBackToTop]?: string;
[kSkipToContent]?: string;
[kToggleDarkMode]?: string;
[kToggleNavigation]?: string;
[kToggleReaderMode]?: string;
Expand Down
3 changes: 3 additions & 0 deletions src/format/dashboard/format-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ function dashboardHtmlPostProcessor(
// Mark the page container with layout instructions
const containerEl = doc.querySelector("div.page-layout-custom");
if (containerEl) {
// dashboards have no <main> element, so mark the content container
// as the main landmark for assistive technology
containerEl.setAttribute("role", "main");
const containerClz = [
"quarto-dashboard-content",
"bslib-gap-spacing",
Expand Down
36 changes: 36 additions & 0 deletions src/format/html/format-html-bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
kQuartoTemplateParams,
kRelatedFormatsTitle,
kSectionDivs,
kSkipToContent,
kTocDepth,
kTocExpand,
kTocLocation,
Expand Down Expand Up @@ -526,6 +527,10 @@ function bootstrapHtmlPostprocessor(
doc.body.insertBefore(beforeBodyScript, doc.body.firstChild);
}

// add a skip-to-content link as the first element of the body so that
// keyboard users can bypass navigation (WCAG 2.4.1 Bypass Blocks)
injectSkipLink(doc, format);

// Process the elements of this document into an appendix
if (
format.metadata[kAppendixStyle] !== false &&
Expand All @@ -545,6 +550,37 @@ function bootstrapHtmlPostprocessor(
};
}

function injectSkipLink(doc: Document, format: Format) {
// resolve the link target, from most to least specific: the article-layout
// main content element, a hand-authored main, the custom-layout container
// (standalone custom layouts and dashboards), or the website custom-layout
// content container
const target = doc.querySelector("#quarto-document-content") ||
doc.querySelector("main") ||
doc.querySelector("div.page-layout-custom") ||
doc.querySelector("#quarto-content");
if (!target) {
return;
}
if (!target.id) {
target.id = "quarto-document-content";
}
// tabindex=-1 so that activating the link moves keyboard focus into the
// content (not just scroll position) across browsers and assistive tech
target.setAttribute("tabindex", "-1");

const skipLink = doc.createElement("a");
skipLink.id = "quarto-skip-link";
skipLink.classList.add("visually-hidden-focusable");
skipLink.setAttribute("href", `#${target.id}`);
skipLink.appendChild(
doc.createTextNode(
format.language[kSkipToContent] || "Skip to main content",
),
);
doc.body.insertBefore(skipLink, doc.body.firstChild);
}

function createLinkChild(formatLink: AlternateLink, doc: Document) {
const link = doc.createElement("a");
link.setAttribute("href", formatLink.href);
Expand Down
45 changes: 23 additions & 22 deletions src/resources/editor/tools/vs-code.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10362,6 +10362,7 @@ var require_yaml_intelligence_resources = __commonJS({
"search-no-results-text": "string",
"copy-button-tooltip": "string",
"copy-button-tooltip-success": "string",
"skip-to-content": "string",
"repo-action-links-edit": "string",
"repo-action-links-source": "string",
"repo-action-links-issue": "string",
Expand Down Expand Up @@ -21838,9 +21839,9 @@ var require_yaml_intelligence_resources = __commonJS({
short: "Name that should be displayed for the overall site",
long: "Name that should be displayed for the overall site. If not explicitly\nprovided in the <code>open-graph</code> metadata, Quarto will use the\nwebsite or book <code>title</code> by default."
},
"Footer left content",
"Footer right content",
"Footer center content",
"Footer left content. Supports markdown formatting.",
"Footer right content. Supports markdown formatting.",
"Footer center content. Supports markdown formatting.",
"Footer border (<code>true</code>, <code>false</code>, or a border\ncolor)",
"Footer background color",
"Footer foreground color",
Expand Down Expand Up @@ -21885,7 +21886,7 @@ var require_yaml_intelligence_resources = __commonJS({
},
"Path to a file containing the Plausible Analytics script snippet",
"Provides an announcement displayed at the top of the page.",
"The content of the announcement",
"The content of the announcement. Supports markdown formatting.",
"Whether this announcement may be dismissed by the user.",
{
short: "The icon to display in the announcement",
Expand Down Expand Up @@ -21943,7 +21944,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Field that contains the section of index entries",
"Additional parameters to pass when executing a search",
"Top navigation options",
"The navbar title. Uses the project title if none is specified.",
"The navbar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed to the left of the\ntitle.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -21959,7 +21960,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Collapse tools into the navbar menu when the display becomes\nnarrow.",
"Side navigation options",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -21976,7 +21977,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Markdown to place above sidebar content (text or file path)",
"Markdown to place below sidebar content (text or file path)",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand Down Expand Up @@ -22052,7 +22053,7 @@ var require_yaml_intelligence_resources = __commonJS({
},
"Path to a file containing the Plausible Analytics script snippet",
"Provides an announcement displayed at the top of the page.",
"The content of the announcement",
"The content of the announcement. Supports markdown formatting.",
"Whether this announcement may be dismissed by the user.",
{
short: "The icon to display in the announcement",
Expand Down Expand Up @@ -22110,7 +22111,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Field that contains the section of index entries",
"Additional parameters to pass when executing a search",
"Top navigation options",
"The navbar title. Uses the project title if none is specified.",
"The navbar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed to the left of the\ntitle.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -22126,7 +22127,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Collapse tools into the navbar menu when the display becomes\nnarrow.",
"Side navigation options",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -22143,7 +22144,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Markdown to place above sidebar content (text or file path)",
"Markdown to place below sidebar content (text or file path)",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand Down Expand Up @@ -24479,7 +24480,7 @@ var require_yaml_intelligence_resources = __commonJS({
},
"Path to a file containing the Plausible Analytics script snippet",
"Provides an announcement displayed at the top of the page.",
"The content of the announcement",
"The content of the announcement. Supports markdown formatting.",
"Whether this announcement may be dismissed by the user.",
{
short: "The icon to display in the announcement",
Expand Down Expand Up @@ -24537,7 +24538,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Field that contains the section of index entries",
"Additional parameters to pass when executing a search",
"Top navigation options",
"The navbar title. Uses the project title if none is specified.",
"The navbar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed to the left of the\ntitle.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -24553,7 +24554,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Collapse tools into the navbar menu when the display becomes\nnarrow.",
"Side navigation options",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -24570,7 +24571,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Markdown to place above sidebar content (text or file path)",
"Markdown to place below sidebar content (text or file path)",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand Down Expand Up @@ -24850,7 +24851,7 @@ var require_yaml_intelligence_resources = __commonJS({
},
"Path to a file containing the Plausible Analytics script snippet",
"Provides an announcement displayed at the top of the page.",
"The content of the announcement",
"The content of the announcement. Supports markdown formatting.",
"Whether this announcement may be dismissed by the user.",
{
short: "The icon to display in the announcement",
Expand Down Expand Up @@ -24908,7 +24909,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Field that contains the section of index entries",
"Additional parameters to pass when executing a search",
"Top navigation options",
"The navbar title. Uses the project title if none is specified.",
"The navbar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed to the left of the\ntitle.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -24924,7 +24925,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Collapse tools into the navbar menu when the display becomes\nnarrow.",
"Side navigation options",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand All @@ -24941,7 +24942,7 @@ var require_yaml_intelligence_resources = __commonJS({
"Markdown to place above sidebar content (text or file path)",
"Markdown to place below sidebar content (text or file path)",
"The identifier for this sidebar.",
"The sidebar title. Uses the project title if none is specified.",
"The sidebar title. Uses the project title if none is specified.\nSupports markdown formatting.",
"Specification of image that will be displayed in the sidebar.",
"Alternate text for the logo image.",
"Target href from navbar logo / title. By default, the logo and title\nlink to the root page of the site (/index.html).",
Expand Down Expand Up @@ -25374,12 +25375,12 @@ var require_yaml_intelligence_resources = __commonJS({
mermaid: "%%"
},
"handlers/mermaid/schema.yml": {
_internalId: 222617,
_internalId: 222619,
type: "object",
description: "be an object",
properties: {
"mermaid-format": {
_internalId: 222609,
_internalId: 222611,
type: "enum",
enum: [
"png",
Expand All @@ -25395,7 +25396,7 @@ var require_yaml_intelligence_resources = __commonJS({
exhaustiveCompletions: true
},
theme: {
_internalId: 222616,
_internalId: 222618,
type: "anyOf",
anyOf: [
{
Expand Down

Large diffs are not rendered by default.

Loading
Loading