@markuplint/ml-spec 5.0.0-rc.2 → 5.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +0 -8
- package/lib/algorithm/aria/accname/aria-steps.d.ts +0 -24
- package/lib/algorithm/aria/accname/aria-steps.js +0 -24
- package/lib/algorithm/aria/accname/compute.d.ts +0 -10
- package/lib/algorithm/aria/accname/compute.js +0 -10
- package/lib/algorithm/aria/accname/element-names.d.ts +0 -23
- package/lib/algorithm/aria/accname/element-names.js +0 -23
- package/lib/algorithm/aria/accname/helpers.d.ts +2 -64
- package/lib/algorithm/aria/accname/helpers.js +2 -72
- package/lib/algorithm/aria/accname/label-steps.d.ts +2 -18
- package/lib/algorithm/aria/accname/label-steps.js +5 -21
- package/lib/algorithm/aria/accname/types.d.ts +0 -3
- package/lib/algorithm/aria/get-explicit-role.d.ts +1 -8
- package/lib/algorithm/aria/get-explicit-role.js +1 -8
- package/lib/algorithm/aria/get-non-presentational-ancestor.d.ts +2 -10
- package/lib/algorithm/aria/get-non-presentational-ancestor.js +2 -10
- package/lib/algorithm/aria/get-permitted-roles-spec.js +1 -0
- package/lib/algorithm/aria/matches-context-role.d.ts +3 -10
- package/lib/algorithm/aria/matches-context-role.js +3 -10
- package/lib/algorithm/html/content-model-category-to-tag-names.d.ts +5 -0
- package/lib/algorithm/html/content-model-category-to-tag-names.js +5 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.js +28 -0
- package/lib/types/index.d.ts +35 -4
- package/lib/utils/schema-to-spec.d.ts +10 -0
- package/lib/utils/schema-to-spec.js +10 -0
- package/package.json +7 -7
- package/ARCHITECTURE.ja.md +0 -267
- package/ARCHITECTURE.md +0 -267
- package/SKILL.md +0 -116
- package/docs/aria-algorithms.ja.md +0 -802
- package/docs/aria-algorithms.md +0 -804
- package/docs/html-algorithms.ja.md +0 -469
- package/docs/html-algorithms.md +0 -469
- package/docs/maintenance.ja.md +0 -359
- package/docs/maintenance.md +0 -359
- package/docs/spec-resolution.ja.md +0 -575
- package/docs/spec-resolution.md +0 -588
- package/docs/type-definitions.ja.md +0 -584
- package/docs/type-definitions.md +0 -584
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [5.0.0-rc.5](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.4...v5.0.0-rc.5) (2026-08-28)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **html-spec:** div in dl allows exactly one dt+dd group per HTML LS ([6f344bb](https://github.com/markuplint/markuplint/commit/6f344bbc72bc82dbe2d1ebf05600a5fce9838de1))
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- **ml-spec:** add requiredCondition field to ARIARoleOwnedProperties ([6867215](https://github.com/markuplint/markuplint/commit/686721537b3d9b141dac12666a2796635d320151)), closes [#3682](https://github.com/markuplint/markuplint/issues/3682)
|
|
15
|
+
|
|
16
|
+
# [5.0.0-rc.4](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.3...v5.0.0-rc.4) (2026-04-19)
|
|
17
|
+
|
|
18
|
+
**Note:** Version bump only for package @markuplint/ml-spec
|
|
19
|
+
|
|
20
|
+
# [5.0.0-rc.3](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.2...v5.0.0-rc.3) (2026-04-19)
|
|
21
|
+
|
|
22
|
+
**Note:** Version bump only for package @markuplint/ml-spec
|
|
23
|
+
|
|
6
24
|
# [5.0.0-rc.2](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.1...v5.0.0-rc.2) (2026-04-15)
|
|
7
25
|
|
|
8
26
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -126,14 +126,6 @@ yarn add @markuplint/ml-spec
|
|
|
126
126
|
- "HTML Schema" and "Specs" are used interchangeably in markuplint to mean the JSON Schema that
|
|
127
127
|
describes HTML element specs (attributes, ARIA, content models, etc) and their TypeScript types.
|
|
128
128
|
|
|
129
|
-
### Contributing
|
|
130
|
-
|
|
131
|
-
For schema editing workflows, generation commands, common recipes, dependency management, and troubleshooting, see the [Maintenance Guide](docs/maintenance.md).
|
|
132
|
-
|
|
133
|
-
For schema merging and spec extension details, see [Spec Resolution](docs/spec-resolution.md).
|
|
134
|
-
|
|
135
|
-
For content model categories and pattern format, see [HTML Algorithms](docs/html-algorithms.md).
|
|
136
|
-
|
|
137
129
|
### License
|
|
138
130
|
|
|
139
131
|
MIT
|
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
import type { AccnameElement, AccnameResolver, AccnameResult } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Resolves accessible name via `aria-labelledby` attribute.
|
|
4
|
-
*
|
|
5
3
|
* Implements AccName 1.2 §4.3.2 Step 2B:
|
|
6
4
|
* "If the current node has an `aria-labelledby` attribute that contains
|
|
7
5
|
* at least one valid IDREF, and the current node is not already part of
|
|
8
6
|
* an ongoing `aria-labelledby` traversal, process its IDREFs [...]"
|
|
9
7
|
*
|
|
10
|
-
* Control flow:
|
|
11
|
-
* 1. Read `aria-labelledby` attribute; return null if absent or empty.
|
|
12
|
-
* 2. Split the attribute value by whitespace into IDREF tokens.
|
|
13
|
-
* 3. Mark the current element's ID as visited (cycle prevention for A→B→A).
|
|
14
|
-
* 4. For each IDREF:
|
|
15
|
-
* a. Skip if already visited — **except** self-references (see below).
|
|
16
|
-
* b. Resolve the referenced element via `resolver.getElementById`.
|
|
17
|
-
* c. Recursively compute the referenced element's name with
|
|
18
|
-
* `inLabelledbyTraversal=true` (prevents Step 2B re-entry in `compute.ts`).
|
|
19
|
-
* d. Each IDREF branch gets its own copy of the visited set so that
|
|
20
|
-
* one branch's traversal does not block later branches.
|
|
21
|
-
* 5. Join all resolved parts with a space separator and flatten whitespace.
|
|
22
|
-
*
|
|
23
8
|
* **Self-reference handling** (spec-defined, not a custom extension):
|
|
24
9
|
* An element may reference its own ID in `aria-labelledby` to include its
|
|
25
10
|
* own content alongside other referenced elements. This is explicitly
|
|
@@ -29,23 +14,14 @@ import type { AccnameElement, AccnameResolver, AccnameResult } from './types.js'
|
|
|
29
14
|
* visited set — `computeFn` is called with `inLabelledbyTraversal=true`,
|
|
30
15
|
* which causes `compute.ts` to skip Step 2B on the referenced element.
|
|
31
16
|
*
|
|
32
|
-
* @param el - The element with a potential aria-labelledby attribute
|
|
33
|
-
* @param resolver - Environment-dependent resolver for element lookups
|
|
34
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
35
|
-
* @param computeFn - The recursive accessible name computation function
|
|
36
|
-
* @returns The resolved name result, or null if aria-labelledby is not present or yields no name
|
|
37
17
|
* @see https://www.w3.org/TR/accname-1.2/#comp_labelledby_traversal — AccName 1.2 §4.3.2 Step 2B
|
|
38
18
|
*/
|
|
39
19
|
export declare function resolveAriaLabelledby(el: AccnameElement, resolver: AccnameResolver, visited: ReadonlySet<string>, computeFn: (el: AccnameElement, resolver: AccnameResolver, inLabelledbyTraversal: boolean, visited: ReadonlySet<string>) => AccnameResult): AccnameResult | null;
|
|
40
20
|
/**
|
|
41
|
-
* Resolves accessible name via `aria-label` attribute.
|
|
42
|
-
*
|
|
43
21
|
* Implements AccName 1.2 §4.3.2 Step 2D:
|
|
44
22
|
* "If the current node has an `aria-label` attribute whose value is
|
|
45
23
|
* not undefined, not the empty string, and not a string of whitespace [...]"
|
|
46
24
|
*
|
|
47
|
-
* @param el - The element with a potential aria-label attribute
|
|
48
|
-
* @returns The resolved name result, or null if aria-label is not present or empty
|
|
49
25
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2D
|
|
50
26
|
*/
|
|
51
27
|
export declare function resolveAriaLabel(el: AccnameElement): AccnameResult | null;
|
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- AccnameElement wraps mutable DOM types */
|
|
2
2
|
import { flattenText, makeResult } from './helpers.js';
|
|
3
3
|
/**
|
|
4
|
-
* Resolves accessible name via `aria-labelledby` attribute.
|
|
5
|
-
*
|
|
6
4
|
* Implements AccName 1.2 §4.3.2 Step 2B:
|
|
7
5
|
* "If the current node has an `aria-labelledby` attribute that contains
|
|
8
6
|
* at least one valid IDREF, and the current node is not already part of
|
|
9
7
|
* an ongoing `aria-labelledby` traversal, process its IDREFs [...]"
|
|
10
8
|
*
|
|
11
|
-
* Control flow:
|
|
12
|
-
* 1. Read `aria-labelledby` attribute; return null if absent or empty.
|
|
13
|
-
* 2. Split the attribute value by whitespace into IDREF tokens.
|
|
14
|
-
* 3. Mark the current element's ID as visited (cycle prevention for A→B→A).
|
|
15
|
-
* 4. For each IDREF:
|
|
16
|
-
* a. Skip if already visited — **except** self-references (see below).
|
|
17
|
-
* b. Resolve the referenced element via `resolver.getElementById`.
|
|
18
|
-
* c. Recursively compute the referenced element's name with
|
|
19
|
-
* `inLabelledbyTraversal=true` (prevents Step 2B re-entry in `compute.ts`).
|
|
20
|
-
* d. Each IDREF branch gets its own copy of the visited set so that
|
|
21
|
-
* one branch's traversal does not block later branches.
|
|
22
|
-
* 5. Join all resolved parts with a space separator and flatten whitespace.
|
|
23
|
-
*
|
|
24
9
|
* **Self-reference handling** (spec-defined, not a custom extension):
|
|
25
10
|
* An element may reference its own ID in `aria-labelledby` to include its
|
|
26
11
|
* own content alongside other referenced elements. This is explicitly
|
|
@@ -30,11 +15,6 @@ import { flattenText, makeResult } from './helpers.js';
|
|
|
30
15
|
* visited set — `computeFn` is called with `inLabelledbyTraversal=true`,
|
|
31
16
|
* which causes `compute.ts` to skip Step 2B on the referenced element.
|
|
32
17
|
*
|
|
33
|
-
* @param el - The element with a potential aria-labelledby attribute
|
|
34
|
-
* @param resolver - Environment-dependent resolver for element lookups
|
|
35
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
36
|
-
* @param computeFn - The recursive accessible name computation function
|
|
37
|
-
* @returns The resolved name result, or null if aria-labelledby is not present or yields no name
|
|
38
18
|
* @see https://www.w3.org/TR/accname-1.2/#comp_labelledby_traversal — AccName 1.2 §4.3.2 Step 2B
|
|
39
19
|
*/
|
|
40
20
|
export function resolveAriaLabelledby(el, resolver, visited, computeFn) {
|
|
@@ -84,14 +64,10 @@ export function resolveAriaLabelledby(el, resolver, visited, computeFn) {
|
|
|
84
64
|
return null;
|
|
85
65
|
}
|
|
86
66
|
/**
|
|
87
|
-
* Resolves accessible name via `aria-label` attribute.
|
|
88
|
-
*
|
|
89
67
|
* Implements AccName 1.2 §4.3.2 Step 2D:
|
|
90
68
|
* "If the current node has an `aria-label` attribute whose value is
|
|
91
69
|
* not undefined, not the empty string, and not a string of whitespace [...]"
|
|
92
70
|
*
|
|
93
|
-
* @param el - The element with a potential aria-label attribute
|
|
94
|
-
* @returns The resolved name result, or null if aria-label is not present or empty
|
|
95
71
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2D
|
|
96
72
|
*/
|
|
97
73
|
export function resolveAriaLabel(el) {
|
|
@@ -33,19 +33,9 @@ import type { AccnameElement, AccnameResolver, AccnameResult } from './types.js'
|
|
|
33
33
|
*/
|
|
34
34
|
export declare function computeAccessibleName(el: AccnameElement, resolver: AccnameResolver): AccnameResult;
|
|
35
35
|
/**
|
|
36
|
-
* Internal recursive entry point for accessible name computation.
|
|
37
|
-
*
|
|
38
|
-
* This function is the recursive core called by `resolveAriaLabelledby` (Step 2B),
|
|
39
|
-
* `resolveLabelText` (Step 2E), and `resolveNameFromContent` (Step 2F) when they
|
|
40
|
-
* need to compute a child or referenced element's name.
|
|
41
|
-
*
|
|
42
|
-
* @param el - The element to compute the accessible name for
|
|
43
|
-
* @param resolver - Environment-dependent resolver for DOM traversal and role queries
|
|
44
36
|
* @param inLabelledbyTraversal - When true, Step 2B is skipped to prevent re-entry
|
|
45
37
|
* into `resolveAriaLabelledby`, and Step 2F name-from-content is enabled regardless
|
|
46
38
|
* of role. This is set by Step 2B when processing each IDREF.
|
|
47
|
-
* @param visited - Set of element IDs already visited (cycle prevention for aria-labelledby)
|
|
48
|
-
* @returns The computed name and its source
|
|
49
39
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2
|
|
50
40
|
*/
|
|
51
41
|
export declare function computeAccessibleNameInternal(el: AccnameElement, resolver: AccnameResolver, inLabelledbyTraversal: boolean, visited: ReadonlySet<string>): AccnameResult;
|
|
@@ -39,19 +39,9 @@ export function computeAccessibleName(el, resolver) {
|
|
|
39
39
|
return computeAccessibleNameInternal(el, resolver, false, new Set());
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Internal recursive entry point for accessible name computation.
|
|
43
|
-
*
|
|
44
|
-
* This function is the recursive core called by `resolveAriaLabelledby` (Step 2B),
|
|
45
|
-
* `resolveLabelText` (Step 2E), and `resolveNameFromContent` (Step 2F) when they
|
|
46
|
-
* need to compute a child or referenced element's name.
|
|
47
|
-
*
|
|
48
|
-
* @param el - The element to compute the accessible name for
|
|
49
|
-
* @param resolver - Environment-dependent resolver for DOM traversal and role queries
|
|
50
42
|
* @param inLabelledbyTraversal - When true, Step 2B is skipped to prevent re-entry
|
|
51
43
|
* into `resolveAriaLabelledby`, and Step 2F name-from-content is enabled regardless
|
|
52
44
|
* of role. This is set by Step 2B when processing each IDREF.
|
|
53
|
-
* @param visited - Set of element IDs already visited (cycle prevention for aria-labelledby)
|
|
54
|
-
* @returns The computed name and its source
|
|
55
45
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2
|
|
56
46
|
*/
|
|
57
47
|
export function computeAccessibleNameInternal(el, resolver, inLabelledbyTraversal, visited) {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { AccnameElement, AccnameResolver, AccnameResult } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Computes element-specific accessible name per HTML-AAM §4.1.
|
|
4
|
-
*
|
|
5
3
|
* Implements AccName 1.2 §4.3.2 Step 2E: for elements that have a native
|
|
6
4
|
* host language text alternative, use that alternative. The specific rules
|
|
7
5
|
* for each HTML element are defined in HTML-AAM §4.1.
|
|
@@ -9,27 +7,6 @@ import type { AccnameElement, AccnameResolver, AccnameResult } from './types.js'
|
|
|
9
7
|
* Returns null if no element-specific rule applies, letting the caller
|
|
10
8
|
* fall through to name-from-content (Step 2F) or title fallback (Step 2I).
|
|
11
9
|
*
|
|
12
|
-
* Control flow (dispatches by `localName`):
|
|
13
|
-
* - SVG elements → `handleSvgElement` (SVG-AAM: `<title>` child)
|
|
14
|
-
* - `<input>` → `handleInput` (branches by type: text-like, button, image, hidden)
|
|
15
|
-
* - `<textarea>`, `<select>`, `<meter>`, `<progress>`, `<output>` → `handleLabelableWithTitle`
|
|
16
|
-
* - `<button>` → `handleButton` (label → content → title)
|
|
17
|
-
* - `<fieldset>` → `handleFieldset` (legend → title)
|
|
18
|
-
* - `<table>` → `handleTable` (caption → title)
|
|
19
|
-
* - `<img>` → `handleImg` (alt → title)
|
|
20
|
-
* - `<area>` → `handleArea` (alt → title)
|
|
21
|
-
* - `<figure>` → `handleFigure` (title only)
|
|
22
|
-
* - `<summary>` → `handleSummary` (content → title)
|
|
23
|
-
* - `<a href>` → `handleAnchor` (content → title)
|
|
24
|
-
* - `<iframe>` → `handleTitleOnly` (title only)
|
|
25
|
-
* - All others → null (rely on caller's generic Steps 2F/2I)
|
|
26
|
-
*
|
|
27
|
-
* @param el - The element to compute the name for
|
|
28
|
-
* @param resolver - Environment-dependent resolver for DOM traversal and role queries
|
|
29
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
30
|
-
* @param computeFn - The recursive accessible name computation function
|
|
31
|
-
* @param inLabelledbyTraversal - Whether this computation is part of an aria-labelledby traversal
|
|
32
|
-
* @returns The computed name result, or null if no element-specific rule applies
|
|
33
10
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2E
|
|
34
11
|
* @see https://www.w3.org/TR/html-aam-1.0/#accessible-name-and-description-computation — HTML-AAM §4.1
|
|
35
12
|
*/
|
|
@@ -3,8 +3,6 @@ import { DEFAULT_IMAGE_LABEL, DEFAULT_RESET_LABEL, DEFAULT_SUBMIT_LABEL, TEXT_IN
|
|
|
3
3
|
import { findChildByLocalName, getInputType, isSvgElement, makeResult, resolveNameFromContent } from './helpers.js';
|
|
4
4
|
import { resolveLabelText } from './label-steps.js';
|
|
5
5
|
/**
|
|
6
|
-
* Computes element-specific accessible name per HTML-AAM §4.1.
|
|
7
|
-
*
|
|
8
6
|
* Implements AccName 1.2 §4.3.2 Step 2E: for elements that have a native
|
|
9
7
|
* host language text alternative, use that alternative. The specific rules
|
|
10
8
|
* for each HTML element are defined in HTML-AAM §4.1.
|
|
@@ -12,27 +10,6 @@ import { resolveLabelText } from './label-steps.js';
|
|
|
12
10
|
* Returns null if no element-specific rule applies, letting the caller
|
|
13
11
|
* fall through to name-from-content (Step 2F) or title fallback (Step 2I).
|
|
14
12
|
*
|
|
15
|
-
* Control flow (dispatches by `localName`):
|
|
16
|
-
* - SVG elements → `handleSvgElement` (SVG-AAM: `<title>` child)
|
|
17
|
-
* - `<input>` → `handleInput` (branches by type: text-like, button, image, hidden)
|
|
18
|
-
* - `<textarea>`, `<select>`, `<meter>`, `<progress>`, `<output>` → `handleLabelableWithTitle`
|
|
19
|
-
* - `<button>` → `handleButton` (label → content → title)
|
|
20
|
-
* - `<fieldset>` → `handleFieldset` (legend → title)
|
|
21
|
-
* - `<table>` → `handleTable` (caption → title)
|
|
22
|
-
* - `<img>` → `handleImg` (alt → title)
|
|
23
|
-
* - `<area>` → `handleArea` (alt → title)
|
|
24
|
-
* - `<figure>` → `handleFigure` (title only)
|
|
25
|
-
* - `<summary>` → `handleSummary` (content → title)
|
|
26
|
-
* - `<a href>` → `handleAnchor` (content → title)
|
|
27
|
-
* - `<iframe>` → `handleTitleOnly` (title only)
|
|
28
|
-
* - All others → null (rely on caller's generic Steps 2F/2I)
|
|
29
|
-
*
|
|
30
|
-
* @param el - The element to compute the name for
|
|
31
|
-
* @param resolver - Environment-dependent resolver for DOM traversal and role queries
|
|
32
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
33
|
-
* @param computeFn - The recursive accessible name computation function
|
|
34
|
-
* @param inLabelledbyTraversal - Whether this computation is part of an aria-labelledby traversal
|
|
35
|
-
* @returns The computed name result, or null if no element-specific rule applies
|
|
36
13
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2E
|
|
37
14
|
* @see https://www.w3.org/TR/html-aam-1.0/#accessible-name-and-description-computation — HTML-AAM §4.1
|
|
38
15
|
*/
|
|
@@ -1,40 +1,16 @@
|
|
|
1
1
|
import type { AccnameElement, AccnameResolver, AccnameResult, AccnameSource } from './types.js';
|
|
2
2
|
export { EMBEDDED_CONTROL_ROLES } from '../../../const/index.js';
|
|
3
|
-
/**
|
|
4
|
-
* Creates an AccnameResult with a trimmed, whitespace-collapsed name.
|
|
5
|
-
*
|
|
6
|
-
* @param name - The raw name string to normalize
|
|
7
|
-
* @param source - The source that provided the name, or null if the name is empty
|
|
8
|
-
* @returns A result with the collapsed name and its source (source is null when name is empty)
|
|
9
|
-
*/
|
|
10
3
|
export declare function makeResult(name: string, source: AccnameSource | null): AccnameResult;
|
|
11
|
-
/**
|
|
12
|
-
* Collapses internal whitespace and trims a string.
|
|
13
|
-
*
|
|
14
|
-
* @param text - The string to normalize
|
|
15
|
-
* @returns The string with collapsed whitespace and trimmed
|
|
16
|
-
*/
|
|
17
4
|
export declare function flattenText(text: string): string;
|
|
18
5
|
/**
|
|
19
|
-
* Collects text content from child nodes recursively for name-from-content computation.
|
|
20
|
-
*
|
|
21
6
|
* Implements AccName 1.2 §4.3.2 Steps 2F and 2C:
|
|
22
7
|
*
|
|
23
8
|
* - **Step 2F**: "If the current node's role allows name from content [...],
|
|
24
9
|
* return the accumulated text of the current node's descendant nodes."
|
|
25
10
|
* - **Step 2C (Embedded Controls)**: "If the current node is a descendant of
|
|
26
11
|
* an `aria-labelledby` or `aria-label` reference AND the current node is
|
|
27
|
-
* an embedded control, return the embedded control's value."
|
|
28
|
-
*
|
|
29
|
-
* Control flow for each child node:
|
|
30
|
-
* 1. **Text node** → contribute text directly.
|
|
31
|
-
* 2. **Embedded control** (textbox, combobox, listbox, slider, spinbutton, searchbox)
|
|
32
|
-
* → use value via `getEmbeddedControlValue`. Per spec, `aria-label` is ignored
|
|
33
|
-
* for embedded controls during name-from-content traversal.
|
|
34
|
-
* 3. **Other element** → first try its full accessible name (Steps 2B–2I via `computeFn`).
|
|
35
|
-
* If no name, fall through to `collectTextContent` for transparent traversal.
|
|
36
|
-
*
|
|
37
|
-
* Parts are joined with a space separator per AccName 1.2 §4.3.2 Step 2C.
|
|
12
|
+
* an embedded control, return the embedded control's value." Per spec,
|
|
13
|
+
* `aria-label` is ignored for embedded controls during this traversal.
|
|
38
14
|
*
|
|
39
15
|
* **Limitation:** CSS-generated content (`::before`/`::after` with the `content`
|
|
40
16
|
* property) is not included. AccName 1.2 §4.3.2 Step 2G specifies that CSS
|
|
@@ -42,52 +18,14 @@ export declare function flattenText(text: string): string;
|
|
|
42
18
|
* performs static HTML analysis without CSS processing, so this content is
|
|
43
19
|
* unavailable at lint time.
|
|
44
20
|
*
|
|
45
|
-
* @param el - The element whose child nodes to collect text from
|
|
46
|
-
* @param resolver - Environment-dependent resolver for DOM traversal and role queries
|
|
47
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
48
|
-
* @param computeFn - The recursive accessible name computation function
|
|
49
|
-
* @param inLabelledbyTraversal - Whether this computation is part of an aria-labelledby traversal
|
|
50
|
-
* @returns The concatenated text content from child nodes
|
|
51
21
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2F
|
|
52
22
|
* @see https://www.w3.org/TR/accname-1.2/#comp_embedded_control — AccName 1.2 §4.3.2 Step 2C
|
|
53
23
|
*/
|
|
54
24
|
export declare function resolveNameFromContent(el: AccnameElement, resolver: AccnameResolver, visited: ReadonlySet<string>, computeFn: (el: AccnameElement, resolver: AccnameResolver, inLabelledbyTraversal: boolean, visited: ReadonlySet<string>) => AccnameResult, inLabelledbyTraversal: boolean): string;
|
|
55
|
-
/**
|
|
56
|
-
* Finds the first child element with a matching localName.
|
|
57
|
-
*
|
|
58
|
-
* @param el - The parent element to search within
|
|
59
|
-
* @param localName - The local tag name to match
|
|
60
|
-
* @returns The first matching child element, or null if none found
|
|
61
|
-
*/
|
|
62
25
|
export declare function findChildByLocalName(el: AccnameElement, localName: string): AccnameElement | null;
|
|
63
|
-
/**
|
|
64
|
-
* Finds the nearest ancestor label element (implicit label association).
|
|
65
|
-
*
|
|
66
|
-
* @param el - The element to search from
|
|
67
|
-
* @returns The nearest ancestor label element, or null if none found
|
|
68
|
-
*/
|
|
69
26
|
export declare function findAncestorLabel(el: AccnameElement): AccnameElement | null;
|
|
70
|
-
/**
|
|
71
|
-
* Resolves a label for an element via explicit (for=id) or implicit (ancestor) association.
|
|
72
|
-
*
|
|
73
|
-
* @param el - The element to find labels for
|
|
74
|
-
* @param resolver - Environment-dependent resolver for label lookups
|
|
75
|
-
* @returns An array of label elements associated with the element
|
|
76
|
-
*/
|
|
77
27
|
export declare function resolveLabel(el: AccnameElement, resolver: AccnameResolver): readonly AccnameElement[];
|
|
78
|
-
/**
|
|
79
|
-
* Gets the input type, defaulting to 'text' for inputs without a type attribute.
|
|
80
|
-
*
|
|
81
|
-
* @param el - The element to get the input type for
|
|
82
|
-
* @returns The lowercase input type, or an empty string if not an input element
|
|
83
|
-
*/
|
|
84
28
|
export declare function getInputType(el: AccnameElement): string;
|
|
85
|
-
/**
|
|
86
|
-
* Checks if an element is in the SVG namespace.
|
|
87
|
-
*
|
|
88
|
-
* @param el - The element to check
|
|
89
|
-
* @returns True if the element is in the SVG namespace
|
|
90
|
-
*/
|
|
91
29
|
export declare function isSvgElement(el: AccnameElement): boolean;
|
|
92
30
|
/**
|
|
93
31
|
* Escapes a string for safe use inside a CSS selector.
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/prefer-readonly-parameter-types -- AccnameElement wraps mutable DOM types */
|
|
2
2
|
import { ELEMENT_NODE, SVG_NAMESPACE, TEXT_NODE, TEXT_LIKE_INPUT_TYPES } from '../../../const/index.js';
|
|
3
3
|
export { EMBEDDED_CONTROL_ROLES } from '../../../const/index.js';
|
|
4
|
-
/**
|
|
5
|
-
* Creates an AccnameResult with a trimmed, whitespace-collapsed name.
|
|
6
|
-
*
|
|
7
|
-
* @param name - The raw name string to normalize
|
|
8
|
-
* @param source - The source that provided the name, or null if the name is empty
|
|
9
|
-
* @returns A result with the collapsed name and its source (source is null when name is empty)
|
|
10
|
-
*/
|
|
11
4
|
export function makeResult(name, source) {
|
|
12
5
|
const trimmed = flattenText(name);
|
|
13
6
|
return {
|
|
@@ -15,35 +8,18 @@ export function makeResult(name, source) {
|
|
|
15
8
|
source: trimmed ? source : null,
|
|
16
9
|
};
|
|
17
10
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Collapses internal whitespace and trims a string.
|
|
20
|
-
*
|
|
21
|
-
* @param text - The string to normalize
|
|
22
|
-
* @returns The string with collapsed whitespace and trimmed
|
|
23
|
-
*/
|
|
24
11
|
export function flattenText(text) {
|
|
25
12
|
return text.replaceAll(/\s+/g, ' ').trim();
|
|
26
13
|
}
|
|
27
14
|
/**
|
|
28
|
-
* Collects text content from child nodes recursively for name-from-content computation.
|
|
29
|
-
*
|
|
30
15
|
* Implements AccName 1.2 §4.3.2 Steps 2F and 2C:
|
|
31
16
|
*
|
|
32
17
|
* - **Step 2F**: "If the current node's role allows name from content [...],
|
|
33
18
|
* return the accumulated text of the current node's descendant nodes."
|
|
34
19
|
* - **Step 2C (Embedded Controls)**: "If the current node is a descendant of
|
|
35
20
|
* an `aria-labelledby` or `aria-label` reference AND the current node is
|
|
36
|
-
* an embedded control, return the embedded control's value."
|
|
37
|
-
*
|
|
38
|
-
* Control flow for each child node:
|
|
39
|
-
* 1. **Text node** → contribute text directly.
|
|
40
|
-
* 2. **Embedded control** (textbox, combobox, listbox, slider, spinbutton, searchbox)
|
|
41
|
-
* → use value via `getEmbeddedControlValue`. Per spec, `aria-label` is ignored
|
|
42
|
-
* for embedded controls during name-from-content traversal.
|
|
43
|
-
* 3. **Other element** → first try its full accessible name (Steps 2B–2I via `computeFn`).
|
|
44
|
-
* If no name, fall through to `collectTextContent` for transparent traversal.
|
|
45
|
-
*
|
|
46
|
-
* Parts are joined with a space separator per AccName 1.2 §4.3.2 Step 2C.
|
|
21
|
+
* an embedded control, return the embedded control's value." Per spec,
|
|
22
|
+
* `aria-label` is ignored for embedded controls during this traversal.
|
|
47
23
|
*
|
|
48
24
|
* **Limitation:** CSS-generated content (`::before`/`::after` with the `content`
|
|
49
25
|
* property) is not included. AccName 1.2 §4.3.2 Step 2G specifies that CSS
|
|
@@ -51,12 +27,6 @@ export function flattenText(text) {
|
|
|
51
27
|
* performs static HTML analysis without CSS processing, so this content is
|
|
52
28
|
* unavailable at lint time.
|
|
53
29
|
*
|
|
54
|
-
* @param el - The element whose child nodes to collect text from
|
|
55
|
-
* @param resolver - Environment-dependent resolver for DOM traversal and role queries
|
|
56
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
57
|
-
* @param computeFn - The recursive accessible name computation function
|
|
58
|
-
* @param inLabelledbyTraversal - Whether this computation is part of an aria-labelledby traversal
|
|
59
|
-
* @returns The concatenated text content from child nodes
|
|
60
30
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2F
|
|
61
31
|
* @see https://www.w3.org/TR/accname-1.2/#comp_embedded_control — AccName 1.2 §4.3.2 Step 2C
|
|
62
32
|
*/
|
|
@@ -131,8 +101,6 @@ function collectTextContent(el, resolver, visited, computeFn, inLabelledbyTraver
|
|
|
131
101
|
return parts.join(' ');
|
|
132
102
|
}
|
|
133
103
|
/**
|
|
134
|
-
* Gets the value of an embedded control for name-from-content computation.
|
|
135
|
-
*
|
|
136
104
|
* Implements AccName 1.2 §4.3.2 Step 2C embedded control value resolution:
|
|
137
105
|
* - **Range controls** (slider/spinbutton/input[range]):
|
|
138
106
|
* aria-valuetext → aria-valuenow → value attr → textContent
|
|
@@ -214,9 +182,6 @@ function getSelectedOptionText(el) {
|
|
|
214
182
|
return first?.textContent?.trim() ?? '';
|
|
215
183
|
}
|
|
216
184
|
/**
|
|
217
|
-
* Collects all `<option>` descendant elements from a `<select>`,
|
|
218
|
-
* traversing through `<optgroup>` containers.
|
|
219
|
-
*
|
|
220
185
|
* **Note (#2069):** Customizable `<select>` allows non-option children
|
|
221
186
|
* (e.g., `<button>`, `<datalist>`, `<div>`). This function currently only
|
|
222
187
|
* recognizes `<option>` and `<optgroup>` — it will need updating when the
|
|
@@ -240,19 +205,9 @@ function collectOptions(el) {
|
|
|
240
205
|
}
|
|
241
206
|
return result;
|
|
242
207
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Checks if an input element is a text-like type (text, search, tel, url, email, etc.).
|
|
245
|
-
*/
|
|
246
208
|
function isTextLikeInput(el) {
|
|
247
209
|
return TEXT_LIKE_INPUT_TYPES.has(getInputType(el));
|
|
248
210
|
}
|
|
249
|
-
/**
|
|
250
|
-
* Finds the first child element with a matching localName.
|
|
251
|
-
*
|
|
252
|
-
* @param el - The parent element to search within
|
|
253
|
-
* @param localName - The local tag name to match
|
|
254
|
-
* @returns The first matching child element, or null if none found
|
|
255
|
-
*/
|
|
256
211
|
export function findChildByLocalName(el, localName) {
|
|
257
212
|
for (const child of el.children) {
|
|
258
213
|
if (child.localName === localName) {
|
|
@@ -261,12 +216,6 @@ export function findChildByLocalName(el, localName) {
|
|
|
261
216
|
}
|
|
262
217
|
return null;
|
|
263
218
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Finds the nearest ancestor label element (implicit label association).
|
|
266
|
-
*
|
|
267
|
-
* @param el - The element to search from
|
|
268
|
-
* @returns The nearest ancestor label element, or null if none found
|
|
269
|
-
*/
|
|
270
219
|
export function findAncestorLabel(el) {
|
|
271
220
|
let current = el.parentElement;
|
|
272
221
|
while (current) {
|
|
@@ -277,13 +226,6 @@ export function findAncestorLabel(el) {
|
|
|
277
226
|
}
|
|
278
227
|
return null;
|
|
279
228
|
}
|
|
280
|
-
/**
|
|
281
|
-
* Resolves a label for an element via explicit (for=id) or implicit (ancestor) association.
|
|
282
|
-
*
|
|
283
|
-
* @param el - The element to find labels for
|
|
284
|
-
* @param resolver - Environment-dependent resolver for label lookups
|
|
285
|
-
* @returns An array of label elements associated with the element
|
|
286
|
-
*/
|
|
287
229
|
export function resolveLabel(el, resolver) {
|
|
288
230
|
const id = el.id;
|
|
289
231
|
if (id) {
|
|
@@ -298,24 +240,12 @@ export function resolveLabel(el, resolver) {
|
|
|
298
240
|
}
|
|
299
241
|
return [];
|
|
300
242
|
}
|
|
301
|
-
/**
|
|
302
|
-
* Gets the input type, defaulting to 'text' for inputs without a type attribute.
|
|
303
|
-
*
|
|
304
|
-
* @param el - The element to get the input type for
|
|
305
|
-
* @returns The lowercase input type, or an empty string if not an input element
|
|
306
|
-
*/
|
|
307
243
|
export function getInputType(el) {
|
|
308
244
|
if (el.localName !== 'input') {
|
|
309
245
|
return '';
|
|
310
246
|
}
|
|
311
247
|
return (el.getAttribute('type') ?? 'text').toLowerCase();
|
|
312
248
|
}
|
|
313
|
-
/**
|
|
314
|
-
* Checks if an element is in the SVG namespace.
|
|
315
|
-
*
|
|
316
|
-
* @param el - The element to check
|
|
317
|
-
* @returns True if the element is in the SVG namespace
|
|
318
|
-
*/
|
|
319
249
|
export function isSvgElement(el) {
|
|
320
250
|
return el.namespaceURI === SVG_NAMESPACE;
|
|
321
251
|
}
|
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
import type { AccnameElement, AccnameResolver, AccnameResult } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Part of AccName 1.2 §4.3.2 Step 2E — for labelable elements, the HTML label
|
|
4
|
+
* association is checked before other element-specific rules.
|
|
4
5
|
*
|
|
5
|
-
* Part of AccName 1.2 §4.3.2 Step 2E — for labelable elements, checks
|
|
6
|
-
* the HTML label association before other element-specific rules.
|
|
7
|
-
*
|
|
8
|
-
* Control flow:
|
|
9
|
-
* 1. Find labels via `resolveLabel`: explicit `<label for="id">` first,
|
|
10
|
-
* then implicit ancestor `<label>`.
|
|
11
|
-
* 2. For each label, collect its text content via `collectLabelText`,
|
|
12
|
-
* which walks the label's children but **excludes** the labeled
|
|
13
|
-
* element itself (prevents circular inclusion).
|
|
14
|
-
* 3. Join all label texts with a space separator and flatten whitespace.
|
|
15
|
-
*
|
|
16
|
-
* @param el - The labelable element to resolve label text for
|
|
17
|
-
* @param resolver - Environment-dependent resolver for label lookups
|
|
18
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
19
|
-
* @param computeFn - The recursive accessible name computation function
|
|
20
|
-
* @param inLabelledbyTraversal - Whether this computation is part of an aria-labelledby traversal
|
|
21
|
-
* @returns The resolved name result, or null if no label provides a name
|
|
22
6
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2E
|
|
23
7
|
* @see https://www.w3.org/TR/html-aam-1.0/#el-input-text — HTML-AAM label association
|
|
24
8
|
*/
|
|
@@ -2,25 +2,9 @@
|
|
|
2
2
|
import { ELEMENT_NODE, TEXT_NODE } from '../../../const/index.js';
|
|
3
3
|
import { flattenText, makeResult, resolveLabel } from './helpers.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Part of AccName 1.2 §4.3.2 Step 2E — for labelable elements, the HTML label
|
|
6
|
+
* association is checked before other element-specific rules.
|
|
6
7
|
*
|
|
7
|
-
* Part of AccName 1.2 §4.3.2 Step 2E — for labelable elements, checks
|
|
8
|
-
* the HTML label association before other element-specific rules.
|
|
9
|
-
*
|
|
10
|
-
* Control flow:
|
|
11
|
-
* 1. Find labels via `resolveLabel`: explicit `<label for="id">` first,
|
|
12
|
-
* then implicit ancestor `<label>`.
|
|
13
|
-
* 2. For each label, collect its text content via `collectLabelText`,
|
|
14
|
-
* which walks the label's children but **excludes** the labeled
|
|
15
|
-
* element itself (prevents circular inclusion).
|
|
16
|
-
* 3. Join all label texts with a space separator and flatten whitespace.
|
|
17
|
-
*
|
|
18
|
-
* @param el - The labelable element to resolve label text for
|
|
19
|
-
* @param resolver - Environment-dependent resolver for label lookups
|
|
20
|
-
* @param visited - Set of element IDs already visited (cycle prevention)
|
|
21
|
-
* @param computeFn - The recursive accessible name computation function
|
|
22
|
-
* @param inLabelledbyTraversal - Whether this computation is part of an aria-labelledby traversal
|
|
23
|
-
* @returns The resolved name result, or null if no label provides a name
|
|
24
8
|
* @see https://www.w3.org/TR/accname-1.2/#computation-steps — AccName 1.2 §4.3.2 Step 2E
|
|
25
9
|
* @see https://www.w3.org/TR/html-aam-1.0/#el-input-text — HTML-AAM label association
|
|
26
10
|
*/
|
|
@@ -43,9 +27,9 @@ export function resolveLabelText(el, resolver, visited, computeFn, inLabelledbyT
|
|
|
43
27
|
return null;
|
|
44
28
|
}
|
|
45
29
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
30
|
+
* Excludes the labeled element itself to prevent circular inclusion. Parts are
|
|
31
|
+
* joined with a space separator, matching the AccName 1.2 concatenation
|
|
32
|
+
* behavior for label text.
|
|
49
33
|
*/
|
|
50
34
|
function collectLabelText(label, labeledElement, resolver, visited, computeFn, inLabelledbyTraversal) {
|
|
51
35
|
const parts = [];
|
|
@@ -61,9 +61,6 @@ export interface AccnameResult {
|
|
|
61
61
|
readonly name: string;
|
|
62
62
|
readonly source: AccnameSource | null;
|
|
63
63
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Internal options passed through recursive calls.
|
|
66
|
-
*/
|
|
67
64
|
export interface AccnameOptions {
|
|
68
65
|
readonly inLabelledbyTraversal?: boolean;
|
|
69
66
|
readonly visited?: ReadonlySet<string>;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { ARIAVersion, ComputedRole, MLMLSpec } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* WAI-ARIA "Handling Author Errors" algorithm. Iterates through whitespace-separated
|
|
5
|
-
* role tokens and returns the first one that passes all validation checks
|
|
6
|
-
* (exists, non-abstract, permitted, valid landmark).
|
|
3
|
+
* Implements the WAI-ARIA "Handling Author Errors" algorithm.
|
|
7
4
|
*
|
|
8
5
|
* @see https://w3c.github.io/aria/#document-handling_author-errors
|
|
9
|
-
* @param specs - The full markup language specification
|
|
10
|
-
* @param el - The DOM element to resolve the explicit role for
|
|
11
|
-
* @param version - The ARIA specification version to use
|
|
12
|
-
* @returns The first valid role found as a `ComputedRole`, or `role: null` with the last encountered error type
|
|
13
6
|
*/
|
|
14
7
|
export declare function getExplicitRole(specs: MLMLSpec, el: Element, version: ARIAVersion): ComputedRole;
|
|
@@ -2,16 +2,9 @@ import { getRoleSpec } from './get-role-spec.js';
|
|
|
2
2
|
import { resolveNamespace } from '../../utils/resolve-namespace.js';
|
|
3
3
|
import { getPermittedRoles } from './get-permitted-roles.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* WAI-ARIA "Handling Author Errors" algorithm. Iterates through whitespace-separated
|
|
7
|
-
* role tokens and returns the first one that passes all validation checks
|
|
8
|
-
* (exists, non-abstract, permitted, valid landmark).
|
|
5
|
+
* Implements the WAI-ARIA "Handling Author Errors" algorithm.
|
|
9
6
|
*
|
|
10
7
|
* @see https://w3c.github.io/aria/#document-handling_author-errors
|
|
11
|
-
* @param specs - The full markup language specification
|
|
12
|
-
* @param el - The DOM element to resolve the explicit role for
|
|
13
|
-
* @param version - The ARIA specification version to use
|
|
14
|
-
* @returns The first valid role found as a `ComputedRole`, or `role: null` with the last encountered error type
|
|
15
8
|
*/
|
|
16
9
|
export function getExplicitRole(specs,
|
|
17
10
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import type { ARIAVersion, MLMLSpec } from '../../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* ownership traversal (via `isTransparentForOwnership`).
|
|
6
|
-
*
|
|
7
|
-
* In ARIA 1.3, `generic` role elements are additionally transparent.
|
|
8
|
-
*
|
|
9
|
-
* @param el - The DOM element whose ancestors to traverse
|
|
10
|
-
* @param specs - The full markup language specification
|
|
11
|
-
* @param version - The ARIA specification version to use
|
|
12
|
-
* @returns The nearest non-presentational ancestor's `ComputedRole`, or `{ el: null, role: null }` if none exists
|
|
3
|
+
* In ARIA 1.3, `generic` role elements are additionally transparent for
|
|
4
|
+
* ownership traversal.
|
|
13
5
|
*/
|
|
14
6
|
export declare function getNonPresentationalAncestor(el: Element, specs: MLMLSpec, version: ARIAVersion): import("../../types/index.js").ComputedRole | {
|
|
15
7
|
el: null;
|