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
5 changes: 5 additions & 0 deletions news/changelog-1.11.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All changes included in 1.11:

- ([#14741](https://github.com/quarto-dev/quarto-cli/issues/14741)): Don't wrap the `longtable` environment of a cross-referenceable table in a `{ ... }` group. Pandoc emits that group to scope its `\def\LTcaptype{none}`, which Quarto removes when adding its own `\caption`; keeping the now-pointless group broke packages that move the environment out of the text flow, such as `endfloat` with `\DeclareDelayedFloatFlavor*{longtable}{table}`.

## Accessibility

- ([#14376](https://github.com/quarto-dev/quarto-cli/issues/14376)): Add a distinct, localizable `aria-label` to each navigation landmark of websites and books: the navbar, the sidebar, the mobile secondary nav, the previous/next page navigation, and the breadcrumbs (previously hardcoded English `breadcrumb`). The new `navigation-*-label` language keys can be overridden with `language:` metadata.
- ([#14376](https://github.com/quarto-dev/quarto-cli/issues/14376)): Label the table of contents `<nav>` with its localized title (`aria-labelledby`), in `html` and `revealjs` output, so assistive technology can tell it apart from other navigation landmarks.

## Engines

### `knitr`
Expand Down
11 changes: 11 additions & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ export const kToggleDarkMode = "toggle-dark-mode";
export const kToggleReaderMode = "toggle-reader-mode";
export const kToggleNavigation = "toggle-navigation";

export const kNavigationMainLabel = "navigation-main-label";
export const kNavigationSectionLabel = "navigation-section-label";
export const kNavigationSecondaryLabel = "navigation-secondary-label";
export const kNavigationPageLabel = "navigation-page-label";
export const kNavigationBreadcrumbsLabel = "navigation-breadcrumbs-label";

export const kCopyButtonTooltip = "copy-button-tooltip";
export const kCopyButtonTooltipSuccess = "copy-button-tooltip-success";
export const kBackToTop = "back-to-top";
Expand Down Expand Up @@ -433,6 +439,11 @@ export const kLanguageDefaultsKeys = [
kToggleReaderMode,
kToggleSidebar,
kToggleSection,
kNavigationMainLabel,
kNavigationSectionLabel,
kNavigationSecondaryLabel,
kNavigationPageLabel,
kNavigationBreadcrumbsLabel,
kCopyButtonTooltip,
kCopyButtonTooltipSuccess,
kBackToTop,
Expand Down
10 changes: 10 additions & 0 deletions src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ import {
kMathml,
kMergeIncludes,
kMermaidFormat,
kNavigationBreadcrumbsLabel,
kNavigationMainLabel,
kNavigationPageLabel,
kNavigationSecondaryLabel,
kNavigationSectionLabel,
kNotebookLinks,
kNotebookPreserveCells,
kNotebookPreviewBack,
Expand Down Expand Up @@ -701,6 +706,11 @@ export interface FormatLanguage {
[kToggleReaderMode]?: string;
[kToggleSection]?: string;
[kToggleSidebar]?: string;
[kNavigationMainLabel]?: string;
[kNavigationSectionLabel]?: string;
[kNavigationSecondaryLabel]?: string;
[kNavigationPageLabel]?: string;
[kNavigationBreadcrumbsLabel]?: string;
[kSearchMatchingDocumentsText]?: string;
[kSearchCopyLinkTitle]?: string;
[kSearchMoreMatchText]?: string;
Expand Down
15 changes: 12 additions & 3 deletions src/project/types/website/website-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ import {
import {
kBackToTop,
kIncludeInHeader,
kNavigationBreadcrumbsLabel,
kNumberSections,
kRepoActionLinksEdit,
kRepoActionLinksIssue,
Expand Down Expand Up @@ -472,7 +473,7 @@ function navigationHtmlPostprocessor(
);
if (secondaryNavTitleEl) {
if (showBreadCrumbs) {
const navEl = makeBreadCrumbs(doc);
const navEl = makeBreadCrumbs(doc, language);
if (secondaryNavTitleEl.parentElement) {
secondaryNavTitleEl.parentElement.replaceChild(
navEl,
Expand Down Expand Up @@ -506,6 +507,7 @@ function navigationHtmlPostprocessor(
if (navigation.breadCrumbs && navigation.breadCrumbs.length > 1) {
const titleBreadCrumbEl = makeBreadCrumbs(
doc,
language,
["quarto-title-breadcrumbs", "d-none", "d-lg-block"],
);
// See if there is deeper target
Expand Down Expand Up @@ -907,7 +909,11 @@ async function resolveFooter(
return footer;
}

function makeBreadCrumbs(doc: Document, clz?: string[]) {
function makeBreadCrumbs(
doc: Document,
language: FormatLanguage,
clz?: string[],
) {
// Make bootstrap breadcrumbs
const navEl = doc.createElement("nav");
navEl.classList.add("quarto-page-breadcrumbs");
Expand All @@ -916,7 +922,10 @@ function makeBreadCrumbs(doc: Document, clz?: string[]) {
navEl.classList.add(cls);
});
}
navEl.setAttribute("aria-label", "breadcrumb");
navEl.setAttribute(
"aria-label",
language[kNavigationBreadcrumbsLabel] || "Breadcrumbs",
);

const olEl = doc.createElement("ol");
olEl.classList.add("breadcrumb");
Expand Down
11 changes: 8 additions & 3 deletions src/resources/editor/tools/vs-code.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10385,6 +10385,11 @@ var require_yaml_intelligence_resources = __commonJS({
"search-text-placeholder": "string",
"search-detached-cancel-button-title": "string",
"search-submit-button-title": "string",
"navigation-main-label": "string",
"navigation-section-label": "string",
"navigation-secondary-label": "string",
"navigation-page-label": "string",
"navigation-breadcrumbs-label": "string",
"crossref-fig-title": "string",
"crossref-tbl-title": "string",
"crossref-lst-title": "string",
Expand Down Expand Up @@ -25387,12 +25392,12 @@ var require_yaml_intelligence_resources = __commonJS({
mermaid: "%%"
},
"handlers/mermaid/schema.yml": {
_internalId: 223031,
_internalId: 223771,
type: "object",
description: "be an object",
properties: {
"mermaid-format": {
_internalId: 223023,
_internalId: 223763,
type: "enum",
enum: [
"png",
Expand All @@ -25408,7 +25413,7 @@ var require_yaml_intelligence_resources = __commonJS({
exhaustiveCompletions: true
},
theme: {
_internalId: 223030,
_internalId: 223770,
type: "anyOf",
anyOf: [
{
Expand Down

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions src/resources/editor/tools/yaml/web-worker.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions src/resources/editor/tools/yaml/yaml-intelligence-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -3353,6 +3353,11 @@
"search-text-placeholder": "string",
"search-detached-cancel-button-title": "string",
"search-submit-button-title": "string",
"navigation-main-label": "string",
"navigation-section-label": "string",
"navigation-secondary-label": "string",
"navigation-page-label": "string",
"navigation-breadcrumbs-label": "string",
"crossref-fig-title": "string",
"crossref-tbl-title": "string",
"crossref-lst-title": "string",
Expand Down Expand Up @@ -18355,12 +18360,12 @@
"mermaid": "%%"
},
"handlers/mermaid/schema.yml": {
"_internalId": 223031,
"_internalId": 223771,
"type": "object",
"description": "be an object",
"properties": {
"mermaid-format": {
"_internalId": 223023,
"_internalId": 223763,
"type": "enum",
"enum": [
"png",
Expand All @@ -18376,7 +18381,7 @@
"exhaustiveCompletions": true
},
"theme": {
"_internalId": 223030,
"_internalId": 223770,
"type": "anyOf",
"anyOf": [
{
Expand Down
2 changes: 1 addition & 1 deletion src/resources/formats/html/pandoc/toc.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<nav id="$idprefix$TOC" role="doc-toc">
<nav id="$idprefix$TOC" role="doc-toc"$if(toc-title)$ aria-labelledby="$idprefix$toc-title"$endif$>
$if(toc-title)$
<h2 id="$idprefix$toc-title">$toc-title$</h2>
$endif$
Expand Down
2 changes: 1 addition & 1 deletion src/resources/formats/revealjs/pandoc/toc-slide.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<section id="$idprefix$TOC">
<nav role="doc-toc">
<nav role="doc-toc"$if(toc-title)$ aria-labelledby="$idprefix$toc-title"$endif$>
$if(toc-title)$
<h2 id="$idprefix$toc-title">$toc-title$</h2>
$endif$
Expand Down
8 changes: 8 additions & 0 deletions src/resources/language/_language.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ toggle-dark-mode: "Toggle dark mode"
toggle-reader-mode: "Toggle reader mode"
toggle-navigation: "Toggle navigation"

# Landmark labels: screen readers announce the role after the label, so do not
# include the word "navigation" (https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/)
navigation-main-label: "Main"
navigation-section-label: "Section"
navigation-secondary-label: "Secondary"
navigation-page-label: "Page"
navigation-breadcrumbs-label: "Breadcrumbs"

crossref-fig-title: "Figure"
crossref-tbl-title: "Table"
crossref-lst-title: "Listing"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<% if (nav.layout) { %>

<% if (nav.prevPage || nav.nextPage) { %>
<nav class="page-navigation">
<nav class="page-navigation" aria-label="<%- nav.language['navigation-page-label'] %>">
<div class="nav-page nav-page-previous">
<% if (nav.prevPage) { %>
<a href="<%- nav.prevPage.href %>" class="pagination-link" aria-label="<%- nav.prevPage.plainText || nav.prevPage.text %>">
Expand Down
4 changes: 2 additions & 2 deletions src/resources/projects/website/templates/nav-before-body.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const navbarTocRight = nav['toc-location'] === "right" || nav['toc-location'] ==
<% } %>

<% if (nav.navbar) { %>
<nav class="navbar navbar-expand<%- nav.navbar["collapse-below"] %> <%- nav.navbar.background === "none" || nav.navbar.background === "body" ? "border-bottom" : "" %>" data-bs-theme="dark">
<nav class="navbar navbar-expand<%- nav.navbar["collapse-below"] %> <%- nav.navbar.background === "none" || nav.navbar.background === "body" ? "border-bottom" : "" %>" data-bs-theme="dark" aria-label="<%- nav.language['navigation-main-label'] %>">
<div class="navbar-container container-fluid">
<% partial('navbrand.ejs', {
navbar: nav.navbar
Expand Down Expand Up @@ -61,7 +61,7 @@ const navbarTocRight = nav['toc-location'] === "right" || nav['toc-location'] ==


<% if (nav.sidebar && nav.layout) { %>
<nav class="quarto-secondary-nav">
<nav class="quarto-secondary-nav" aria-label="<%- nav.language['navigation-secondary-label'] %>">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn"
data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item"
Expand Down
2 changes: 1 addition & 1 deletion src/resources/projects/website/templates/sidebar.ejs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation <%- sidebarStyle || (toc ? "floating" : "") %> overflow-auto">
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation <%- sidebarStyle || (toc ? "floating" : "") %> overflow-auto" aria-label="<%- navbar ? language['navigation-section-label'] : language['navigation-main-label'] %>">

<% if (sidebar) { %>
<%
Expand Down
5 changes: 5 additions & 0 deletions src/resources/schema/definitions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1405,6 +1405,11 @@
search-text-placeholder: string
search-detached-cancel-button-title: string
search-submit-button-title: string
navigation-main-label: string
navigation-section-label: string
navigation-secondary-label: string
navigation-page-label: string
navigation-breadcrumbs-label: string
crossref-fig-title: string
crossref-tbl-title: string
crossref-lst-title: string
Expand Down
15 changes: 15 additions & 0 deletions src/resources/schema/json-schemas.json
Original file line number Diff line number Diff line change
Expand Up @@ -1764,6 +1764,21 @@
"search-submit-button-title": {
"type": "string"
},
"navigation-main-label": {
"type": "string"
},
"navigation-section-label": {
"type": "string"
},
"navigation-secondary-label": {
"type": "string"
},
"navigation-page-label": {
"type": "string"
},
"navigation-breadcrumbs-label": {
"type": "string"
},
"crossref-fig-title": {
"type": "string"
},
Expand Down
5 changes: 5 additions & 0 deletions src/resources/types/schema-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,11 @@ export type FormatLanguage = {
"search-text-placeholder"?: string;
"search-detached-cancel-button-title"?: string;
"search-submit-button-title"?: string;
"navigation-main-label"?: string;
"navigation-section-label"?: string;
"navigation-secondary-label"?: string;
"navigation-page-label"?: string;
"navigation-breadcrumbs-label"?: string;
"crossref-fig-title"?: string;
"crossref-tbl-title"?: string;
"crossref-lst-title"?: string;
Expand Down
5 changes: 5 additions & 0 deletions src/resources/types/zod/schema-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,11 @@ export const ZodFormatLanguage = z.object({
"search-text-placeholder": z.string(),
"search-detached-cancel-button-title": z.string(),
"search-submit-button-title": z.string(),
"navigation-main-label": z.string(),
"navigation-section-label": z.string(),
"navigation-secondary-label": z.string(),
"navigation-page-label": z.string(),
"navigation-breadcrumbs-label": z.string(),
"crossref-fig-title": z.string(),
"crossref-tbl-title": z.string(),
"crossref-lst-title": z.string(),
Expand Down
34 changes: 34 additions & 0 deletions tests/docs/smoke-all/accessibility/toc-nav-label.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
title: "TOC nav label"
toc: true
toc-title: "Contents"
format:
html: default
revealjs: default
_quarto:
tests:
html:
ensureHtmlElements:
-
- 'nav#TOC[role="doc-toc"][aria-labelledby="toc-title"]'
- 'nav#TOC h2#toc-title'
- []
ensureFileRegexMatches:
- ['id="toc-title"[^>]*>Contents<']
- []
revealjs:
ensureHtmlElements:
-
- 'nav[role="doc-toc"][aria-labelledby="toc-title"]'
- 'nav[role="doc-toc"] h2#toc-title'
- []
ensureFileRegexMatches:
- ['id="toc-title"[^>]*>Contents<']
- []
---

The TOC `nav` is labelled by its (customizable) title via `aria-labelledby` (#14376).

## A heading

Content so the TOC renders.
3 changes: 3 additions & 0 deletions tests/docs/smoke-all/website/nav-landmark-labels/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.quarto/
/_site/
**/*.quarto_ipynb
Loading
Loading