diff --git a/guided-navigation/CHANGELOG.MD b/guided-navigation/CHANGELOG.MD
index 68424624..afec84db 100644
--- a/guided-navigation/CHANGELOG.MD
+++ b/guided-navigation/CHANGELOG.MD
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.0.1] – 2026-10-02
+
+### Added
+
+- Fixtures for `aria-labelledby` pointing to `hidden` and `aria-hidden` elements
+
+### Fixed
+
+- `aria-labelledby` now uses the elements it references even when they are hidden (`hidden` or `aria-hidden="true"`), as AccName requires, instead of ignoring them
+
## [1.0.0] – 2026-10-01
### Added
diff --git a/guided-navigation/fixtures/ROLES_COVERAGE.md b/guided-navigation/fixtures/ROLES_COVERAGE.md
index d3a3a547..d209b143 100644
--- a/guided-navigation/fixtures/ROLES_COVERAGE.md
+++ b/guided-navigation/fixtures/ROLES_COVERAGE.md
@@ -8,6 +8,7 @@ renaming, or removing a fixture.
| Role | Fixtures |
| --- | --- |
| `abstract` | `abstract-epub-type`, `abstract-role-aria` |
+| `accessible-name` | `accessible-name-labelledby-aria-hidden`, `accessible-name-labelledby-hidden-attribute` |
| `acknowledgments` | `acknowledgments-epub-type`, `acknowledgments-role-aria` |
| `afterword` | `afterword-epub-type`, `afterword-role-aria` |
| `appendix` | `appendix-epub-type`, `appendix-role-aria` |
@@ -77,4 +78,4 @@ renaming, or removing a fixture.
| `toc` | `toc-epub-type`, `toc-role-aria` |
| `video` | `video-html-native`, `video-no-description` |
-178 fixtures across 69 roles.
+180 fixtures across 70 roles.
diff --git a/guided-navigation/fixtures/accessible-name-labelledby-aria-hidden/gnd.json b/guided-navigation/fixtures/accessible-name-labelledby-aria-hidden/gnd.json
new file mode 100644
index 00000000..b608c4c3
--- /dev/null
+++ b/guided-navigation/fixtures/accessible-name-labelledby-aria-hidden/gnd.json
@@ -0,0 +1,31 @@
+{
+ "children": [
+ {
+ "role": [
+ "paragraph"
+ ],
+ "text": {
+ "language": "en",
+ "plain": "Into speech.",
+ "ssml": "Into speech.
Into speech. *
+synthesized speech can be created.
+ diff --git a/guided-navigation/fixtures/accessible-name-labelledby-aria-hidden/meta.json b/guided-navigation/fixtures/accessible-name-labelledby-aria-hidden/meta.json new file mode 100644 index 00000000..5f1f21a9 --- /dev/null +++ b/guided-navigation/fixtures/accessible-name-labelledby-aria-hidden/meta.json @@ -0,0 +1,10 @@ +{ + "id": "accessible-name-labelledby-aria-hidden", + "description": "A link named via aria-labelledby pointing at an element marked aria-hidden=\"true\": the hidden label still names the link, and is not emitted on its own", + "role": "accessible-name", + "rolesCovered": [ + "paragraph" + ], + "sourceRef": "https://www.w3.org/TR/accname/#computation-steps", + "inputKind": "fragment" +} diff --git a/guided-navigation/fixtures/accessible-name-labelledby-hidden-attribute/gnd.json b/guided-navigation/fixtures/accessible-name-labelledby-hidden-attribute/gnd.json new file mode 100644 index 00000000..b608c4c3 --- /dev/null +++ b/guided-navigation/fixtures/accessible-name-labelledby-hidden-attribute/gnd.json @@ -0,0 +1,31 @@ +{ + "children": [ + { + "role": [ + "paragraph" + ], + "text": { + "language": "en", + "plain": "Into speech.", + "ssml": "Into speech.Into speech. *
+synthesized speech can be created.
+see glossary entry diff --git a/guided-navigation/fixtures/accessible-name-labelledby-hidden-attribute/meta.json b/guided-navigation/fixtures/accessible-name-labelledby-hidden-attribute/meta.json new file mode 100644 index 00000000..83de2c82 --- /dev/null +++ b/guided-navigation/fixtures/accessible-name-labelledby-hidden-attribute/meta.json @@ -0,0 +1,10 @@ +{ + "id": "accessible-name-labelledby-hidden-attribute", + "description": "A link named via aria-labelledby pointing at an element hidden with the native hidden attribute: the hidden label still names the link, and is not emitted on its own", + "role": "accessible-name", + "rolesCovered": [ + "paragraph" + ], + "sourceRef": "https://www.w3.org/TR/accname/#computation-steps", + "inputKind": "fragment" +} diff --git a/guided-navigation/fixtures/manifest.json b/guided-navigation/fixtures/manifest.json index c7bbe9c9..4994e71a 100644 --- a/guided-navigation/fixtures/manifest.json +++ b/guided-navigation/fixtures/manifest.json @@ -19,6 +19,26 @@ "gnd": "gnd.json" } }, + { + "id": "accessible-name-labelledby-aria-hidden", + "dir": "accessible-name-labelledby-aria-hidden", + "role": "accessible-name", + "description": "A link named via aria-labelledby pointing at an element marked aria-hidden=\"true\": the hidden label still names the link, and is not emitted on its own", + "files": { + "input": "input.html", + "gnd": "gnd.json" + } + }, + { + "id": "accessible-name-labelledby-hidden-attribute", + "dir": "accessible-name-labelledby-hidden-attribute", + "role": "accessible-name", + "description": "A link named via aria-labelledby pointing at an element hidden with the native hidden attribute: the hidden label still names the link, and is not emitted on its own", + "files": { + "input": "input.html", + "gnd": "gnd.json" + } + }, { "id": "acknowledgments-epub-type", "dir": "acknowledgments-epub-type", diff --git a/guided-navigation/package.json b/guided-navigation/package.json index 83ca1ed9..0a2abe01 100644 --- a/guided-navigation/package.json +++ b/guided-navigation/package.json @@ -1,6 +1,6 @@ { "name": "@readium/guided-navigation", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "description": "Guided Navigation document generation from HTML and XHTML content", "author": "readium", diff --git a/guided-navigation/src/a11y.ts b/guided-navigation/src/a11y.ts index 376476d8..25222646 100644 --- a/guided-navigation/src/a11y.ts +++ b/guided-navigation/src/a11y.ts @@ -69,8 +69,8 @@ export function extractNodeAria(el: Element): [string | null, boolean] { if (labelNodes.length > 0) { let text = ""; + // Directly-referenced label nodes count even when hidden (AccName 2.A). labelNodes.forEach((n, i) => { - if (nodeIsHidden(n)) return; const label = n.getAttribute("aria-label"); text += label ? label : nodeTextExcludingExplicitRoles(n); if (i < labelNodes.length - 1) text += " ";