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.11.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All changes included in 1.11:
- ([#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 (`Site`), the sidebar (`Section`, or `Site` when there is no navbar), the narrow-viewport toolbar (`Toolbar`), the previous/next page navigation (`Page`), and the breadcrumbs (`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)): Translate the new `navigation-*-label` keys in all 33 built-in language files. The values are drawn from existing human-translated interface strings (LibreOffice, GNOME, Wikidata) and each one carries a comment naming its source; values that were adapted rather than used verbatim are marked `needs review`.
- ([#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.
- ([#14844](https://github.com/quarto-dev/quarto-cli/issues/14844)): With `toc-location: left-body` or `right-body`, give the ids in the cloned body table of contents a `-body` suffix. The clone previously repeated every id from the sidebar copy, including the `toc-title` heading that `aria-labelledby` now points at.

## Formats

Expand Down
15 changes: 15 additions & 0 deletions src/format/html/format-html-bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,21 @@ function bootstrapHtmlPostprocessor(
// Leave it where it is in the document, and just mutate it
const clonedToc = toc.cloneNode(true) as Element;
clonedToc.id = "TOC-body";

// Ids must be unique, so the clone can't keep the originals: suffix
// every id it carries, and repoint its aria-labelledby at the renamed
// heading. Nothing outside the nav references these ids, and the
// originals stay on `toc`, so existing lookups resolve as before.
const clonedLabelId = clonedToc.getAttribute("aria-labelledby");
const clonedIdEls = clonedToc.querySelectorAll("[id]");
for (let i = 0; i < clonedIdEls.length; i++) {
const clonedIdEl = clonedIdEls[i] as Element;
clonedIdEl.id = `${clonedIdEl.id}-body`;
}
if (clonedLabelId) {
clonedToc.setAttribute("aria-labelledby", `${clonedLabelId}-body`);
}
Comment on lines +359 to +371

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suffix isn't guaranteed unique against the rest of the document. Pandoc dedupes identical heading slugs, but it doesn't check whether a slug already matches another heading's slug plus -body.

# Setup produces toc-setup. # Setup Body produces toc-setup-body, already sitting in the sidebar TOC. Suffixing the clone's toc-setup gives toc-setup-body too, colliding with the second heading's own entry, and we're back to the duplicate-id-aria failure this PR closes.

I don't know if this case can happen a lot or if this is really edge case ? What do you think ?

If this is possibly, I think this needs a real uniqueness check against the existing ids rather than a fixed suffix, plus a fixture with a heading pair that triggers this (something like Setup / Setup Body) so it doesn't come back silently.


const tocActionsEl = clonedToc.querySelector(".toc-actions");
if (tocActionsEl) {
tocActionsEl.remove();
Expand Down
5 changes: 4 additions & 1 deletion src/project/types/website/website-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,10 @@ function makeBreadCrumbs(
navEl.classList.add(cls);
});
}
navEl.setAttribute("aria-label", language[kNavigationBreadcrumbsLabel]!);
navEl.setAttribute(
"aria-label",
language[kNavigationBreadcrumbsLabel] || "Breadcrumbs",
);

const olEl = doc.createElement("ol");
olEl.classList.add("breadcrumb");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,3 @@ if (nav.layout === "article" || nav.layout === "full") {




9 changes: 9 additions & 0 deletions tests/docs/smoke-all/issues/3473-toc-side-body/left-body.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ _quarto:
ensureHtmlElements:
-
- "nav#TOC.toc-active"
- "nav#TOC h2#toc-title"
- 'nav#TOC[aria-labelledby="toc-title"]'
- "nav#TOC-body"
- "nav#TOC-body h2#toc-title-body"
- 'nav#TOC-body[aria-labelledby="toc-title-body"]'
- "nav#TOC-body a#toc-section-1-body"
-
- "nav#TOC-body h2#toc-title"
- 'nav#TOC-body[aria-labelledby="toc-title"]'
- "nav#TOC-body a#toc-section-1"
---

# Section 1
Expand Down
9 changes: 9 additions & 0 deletions tests/docs/smoke-all/issues/3473-toc-side-body/right-body.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ _quarto:
ensureHtmlElements:
-
- "nav#TOC.toc-active"
- "nav#TOC h2#toc-title"
- 'nav#TOC[aria-labelledby="toc-title"]'
- "nav#TOC-body"
- "nav#TOC-body h2#toc-title-body"
- 'nav#TOC-body[aria-labelledby="toc-title-body"]'
- "nav#TOC-body a#toc-section-1-body"
-
- "nav#TOC-body h2#toc-title"
- 'nav#TOC-body[aria-labelledby="toc-title"]'
- "nav#TOC-body a#toc-section-1"
---

# Section 1
Expand Down
Loading