@markuplint/ml-spec 4.10.2 → 5.0.0-alpha.1

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.
Files changed (78) hide show
  1. package/ARCHITECTURE.ja.md +24 -10
  2. package/ARCHITECTURE.md +24 -10
  3. package/CHANGELOG.md +34 -0
  4. package/README.md +2 -2
  5. package/docs/aria-algorithms.ja.md +180 -33
  6. package/docs/aria-algorithms.md +183 -34
  7. package/docs/html-algorithms.ja.md +2 -2
  8. package/docs/html-algorithms.md +2 -2
  9. package/docs/maintenance.ja.md +13 -22
  10. package/docs/maintenance.md +13 -22
  11. package/docs/spec-resolution.ja.md +44 -9
  12. package/docs/spec-resolution.md +53 -16
  13. package/docs/type-definitions.ja.md +8 -4
  14. package/docs/type-definitions.md +8 -4
  15. package/lib/algorithm/aria/accname/__tests__/test-helpers.d.ts +45 -0
  16. package/lib/algorithm/aria/accname/__tests__/test-helpers.js +120 -0
  17. package/lib/algorithm/aria/accname/aria-steps.d.ts +51 -0
  18. package/lib/algorithm/aria/accname/aria-steps.js +104 -0
  19. package/lib/algorithm/aria/accname/compute.d.ts +51 -0
  20. package/lib/algorithm/aria/accname/compute.js +101 -0
  21. package/lib/algorithm/aria/accname/element-names.d.ts +36 -0
  22. package/lib/algorithm/aria/accname/element-names.js +342 -0
  23. package/lib/algorithm/aria/accname/helpers.d.ts +98 -0
  24. package/lib/algorithm/aria/accname/helpers.js +330 -0
  25. package/lib/algorithm/aria/accname/index.d.ts +4 -0
  26. package/lib/algorithm/aria/accname/index.js +3 -0
  27. package/lib/algorithm/aria/accname/label-steps.d.ts +25 -0
  28. package/lib/algorithm/aria/accname/label-steps.js +74 -0
  29. package/lib/algorithm/aria/accname/svg-helpers.d.ts +17 -0
  30. package/lib/algorithm/aria/accname/svg-helpers.js +30 -0
  31. package/lib/algorithm/aria/accname/types.d.ts +70 -0
  32. package/lib/algorithm/aria/accname/types.js +2 -0
  33. package/lib/algorithm/aria/accname-computation.d.ts +14 -3
  34. package/lib/algorithm/aria/accname-computation.js +131 -8
  35. package/lib/algorithm/aria/aria-specs.d.ts +1 -0
  36. package/lib/algorithm/aria/get-aria.js +30 -4
  37. package/lib/algorithm/aria/get-computed-role.js +106 -26
  38. package/lib/algorithm/aria/get-explicit-role.d.ts +12 -0
  39. package/lib/algorithm/aria/get-explicit-role.js +12 -0
  40. package/lib/algorithm/aria/get-non-presentational-ancestor.d.ts +12 -0
  41. package/lib/algorithm/aria/get-non-presentational-ancestor.js +14 -2
  42. package/lib/algorithm/aria/get-permitted-roles-spec.d.ts +8 -4
  43. package/lib/algorithm/aria/get-permitted-roles-spec.js +19 -6
  44. package/lib/algorithm/aria/get-role-spec.js +10 -3
  45. package/lib/algorithm/aria/has-required-owned-elements.d.ts +2 -1
  46. package/lib/algorithm/aria/has-required-owned-elements.js +18 -15
  47. package/lib/algorithm/aria/is-presentational.d.ts +24 -0
  48. package/lib/algorithm/aria/is-presentational.js +31 -0
  49. package/lib/algorithm/aria/matches-context-role.d.ts +12 -4
  50. package/lib/algorithm/aria/matches-context-role.js +39 -6
  51. package/lib/algorithm/html/content-model-category-to-tag-names.js +1 -1
  52. package/lib/algorithm/html/get-content-model.d.ts +4 -2
  53. package/lib/algorithm/html/get-content-model.js +6 -7
  54. package/lib/const/accname.d.ts +29 -0
  55. package/lib/const/accname.js +76 -0
  56. package/lib/const/dom.d.ts +8 -0
  57. package/lib/const/dom.js +8 -0
  58. package/lib/const/index.d.ts +2 -0
  59. package/lib/const/index.js +2 -0
  60. package/lib/index.d.ts +3 -0
  61. package/lib/index.js +4 -0
  62. package/lib/types/aria.d.ts +3 -3
  63. package/lib/types/index.d.ts +65 -0
  64. package/lib/utils/aria-version.d.ts +1 -1
  65. package/lib/utils/aria-version.js +1 -1
  66. package/lib/utils/directive-resolver.d.ts +23 -0
  67. package/lib/utils/directive-resolver.js +50 -0
  68. package/lib/utils/get-attr-specs-spec.js +4 -3
  69. package/lib/utils/get-ns.d.ts +7 -0
  70. package/lib/utils/get-ns.js +7 -0
  71. package/lib/utils/get-spec-by-tag-name.d.ts +1 -1
  72. package/lib/utils/merge-array.d.ts +10 -0
  73. package/lib/utils/merge-array.js +10 -0
  74. package/lib/utils/resolve-version.d.ts +11 -0
  75. package/lib/utils/resolve-version.js +13 -1
  76. package/lib/utils/schema-to-spec.d.ts +2 -0
  77. package/lib/utils/schema-to-spec.js +15 -4
  78. package/package.json +9 -7
@@ -9,7 +9,7 @@ The implementation covers algorithms from the following specifications:
9
9
  - **WAI-ARIA 1.1 / 1.2 / 1.3** -- Role definitions, states, and properties
10
10
  - **HTML-AAM** (HTML Accessibility API Mappings) -- Implicit role mappings for HTML elements
11
11
  - **SVG-AAM** (SVG Accessibility API Mappings) -- Accessibility tree inclusion rules for SVG
12
- - **AccName 1.1** (Accessible Name and Description Computation) -- Accessible name computation
12
+ - **AccName 1.2** (Accessible Name and Description Computation) -- Accessible name computation
13
13
  - **ARIA in HTML** -- Permitted roles and ARIA attribute constraints per element
14
14
 
15
15
  ### Design Principles
@@ -19,7 +19,7 @@ All ARIA algorithm functions share a consistent design:
19
19
  - They operate on the standard DOM `Element` interface, requiring no markuplint-specific node types.
20
20
  - They accept an `MLMLSpec` parameter containing the full markup language specification data.
21
21
  - They accept an `ARIAVersion` parameter (`'1.1'`, `'1.2'`, or `'1.3'`) to select version-specific behavior.
22
- - They are pure functions with no side effects (aside from internal caching in `getARIA`).
22
+ - They are pure functions with no side effects (aside from internal caching in `getARIA` and a reentrant guard in `getAccname`).
23
23
 
24
24
  ## Role Computation Pipeline
25
25
 
@@ -39,7 +39,7 @@ flowchart TB
39
39
  GCR --> CR{Computed Role}
40
40
  CR -->|presentational| PCR["Presentational Roles<br/>Conflict Resolution"]
41
41
 
42
- PCR --> Check1["1. Required Context Role<br/>validation"]
42
+ PCR --> Check1["1. Required Accessibility<br/>Parent Role validation"]
43
43
  PCR --> Check2["2. SVG accessibility<br/>tree inclusion"]
44
44
  PCR --> Check3["3. Interactive element<br/>protection"]
45
45
  PCR --> Check4["4. Required owned element<br/>check"]
@@ -86,13 +86,13 @@ The core function of the ARIA algorithm suite. It computes the final ARIA role f
86
86
 
87
87
  **Conflict Resolution checks (in order):**
88
88
 
89
- 1. **Required context role validation** -- If the role has `requiredContextRole` entries, the function checks the parent hierarchy. If no parent element exists, returns `NO_OWNER`. If the parent hierarchy does not satisfy the context role conditions (via `matchesContextRole()`), returns `INVALID_REQUIRED_CONTEXT_ROLE`. Presentational ancestors are traversed transparently via `getNonPresentationalAncestor()`.
89
+ 1. **Required context role validation** -- If the role has `requiredAccessibilityParentRole` entries (called `requiredContextRole` in ARIA 1.2), the function checks the parent hierarchy. If no parent element exists, returns `NO_OWNER`. If the parent hierarchy does not satisfy the context role conditions (via `matchesContextRole()`), returns `INVALID_REQUIRED_CONTEXT_ROLE`. Presentational ancestors are traversed transparently via `getNonPresentationalAncestor()`.
90
90
 
91
- 2. **SVG accessibility tree inclusion** -- For SVG namespace elements without a valid explicit role, the function checks whether the element has an accessible name (via `getAccname()`) or a `<title>`/`<desc>` child element. If neither exists, the SVG element is excluded from the accessibility tree (returns `role: null`). This implements the SVG-AAM rules for including normally-omitted SVG elements.
91
+ 2. **SVG accessibility tree inclusion** -- For SVG namespace elements without a valid explicit role, the function checks whether the element has an accessible name source (via `hasSvgAccessibleNameSource()`) — i.e., `aria-label`, `aria-labelledby`, or a `<title>`/`<desc>` child element. If none exists, the SVG element is excluded from the accessibility tree (returns `role: null`). This implements the SVG-AAM rules for including normally-omitted SVG elements.
92
92
 
93
93
  3. **Interactive element protection** -- Focusable elements cannot be presentational. The function checks `mayBeFocusable()` and ensures the element is not `disabled`, `inert`, or `hidden` (traversing ancestors for each attribute). If the element is interactive and not disabled/inert/hidden, the presentational role is overridden with the implicit role and `INTERACTIVE_ELEMENT_MUST_NOT_BE_PRESENTATIONAL` error.
94
94
 
95
- 4. **Required owned element check** -- If a non-presentational ancestor has `requiredOwnedElements` and the current element's implicit role matches one of those required owned elements, the presentational role is overridden. Returns `REQUIRED_OWNED_ELEMENT_MUST_NOT_BE_PRESENTATIONAL`.
95
+ 4. **Required owned element check** -- If a non-presentational ancestor has `allowedAccessibilityChildRoles` (called `requiredOwnedElements` in ARIA 1.2) and the current element's implicit role matches one of those required owned elements, the presentational role is overridden. Returns `REQUIRED_OWNED_ELEMENT_MUST_NOT_BE_PRESENTATIONAL`.
96
96
 
97
97
  5. **Global ARIA property check** -- If the element has any global ARIA properties (e.g., `aria-label`, `aria-describedby`), the presentational role is overridden with the implicit role. Returns `GLOBAL_PROP_MUST_NOT_BE_PRESENTATIONAL`.
98
98
 
@@ -242,12 +242,12 @@ The spec-level implementation for computing permitted ARIA roles. Operates on ta
242
242
 
243
243
  | `permittedRoles` value | Behavior |
244
244
  | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
245
- | `true` | All non-abstract roles from the ARIA spec are permitted. |
245
+ | `true` | All non-abstract roles from the ARIA spec (including DPub roles) are permitted. |
246
246
  | `PermittedARIAAAMInfo` object | If `core-aam` is `true`, adds all non-abstract roles. If `graphics-aam` is `true`, adds all non-abstract graphics roles. |
247
247
  | Array of strings/objects | The specific listed roles are permitted. |
248
248
  | `false` | No roles are permitted (empty list before implicit role). |
249
249
 
250
- 4. Always includes the implicit role in the result. If the implicit role is `"presentation"` or `"none"`, both equivalents are included.
250
+ 4. Always includes the implicit role in the result. If the implicit role is `"presentation"` or `"none"`, both equivalents are included. In ARIA 1.3, if the implicit role is `"img"` or `"image"`, both synonyms are included.
251
251
  5. Returns the merged, deduplicated list.
252
252
 
253
253
  ---
@@ -273,9 +273,10 @@ Retrieves the full ARIA role specification for a given role name, including the
273
273
 
274
274
  1. Searches for the role by name in the ARIA roles list for the given version.
275
275
  2. For SVG namespace (`http://www.w3.org/2000/svg`), also searches `graphicsRoles` if not found in core roles.
276
- 3. Recursively traverses super-class roles via the `generalization` property, building the complete inheritance chain.
277
- 4. Normalizes all optional fields to non-undefined defaults (e.g., `!!role.isAbstract`, `role.requiredContextRole ?? []`).
278
- 5. Returns `null` if the role name does not exist in the spec.
276
+ 3. If still not found, searches `dpubRoles` (DPub ARIA roles are accepted for all elements regardless of namespace).
277
+ 4. Recursively traverses super-class roles via the `generalization` property, building the complete inheritance chain.
278
+ 5. Normalizes all optional fields to non-undefined defaults (e.g., `!!role.isAbstract`). Resolves `requiredAccessibilityParentRole` from the schema's `requiredContextRole` (ARIA 1.2 name) or `requiredAccessibilityParentRole` (ARIA 1.3 name), and similarly `allowedAccessibilityChildRoles` from `requiredOwnedElements` or `allowedAccessibilityChildRoles`. Both the new and deprecated property names are populated with the same values.
279
+ 6. Returns `null` if the role name does not exist in the spec.
279
280
 
280
281
  **Normalized fields in the return value:**
281
282
 
@@ -284,8 +285,10 @@ Retrieves the full ARIA role specification for a given role name, including the
284
285
  name: string;
285
286
  isAbstract: boolean; // default: false
286
287
  deprecated: boolean; // default: false
287
- requiredContextRole: string[]; // default: []
288
- requiredOwnedElements: string[]; // default: []
288
+ requiredAccessibilityParentRole: string[]; // default: []
289
+ allowedAccessibilityChildRoles: string[]; // default: []
290
+ requiredContextRole: string[]; // @deprecated alias — same as requiredAccessibilityParentRole
291
+ requiredOwnedElements: string[]; // @deprecated alias — same as allowedAccessibilityChildRoles
289
292
  accessibleNameRequired: boolean; // default: false
290
293
  accessibleNameFromAuthor: boolean; // default: false
291
294
  accessibleNameFromContent: boolean;// default: false
@@ -322,7 +325,7 @@ Gets the version-resolved ARIA specification for an element, evaluating conditio
322
325
  1. Calls `getVersionResolvedARIA()` which:
323
326
  - Looks up the element spec by tag name and namespace.
324
327
  - Applies `resolveVersion()` to merge version-specific overrides on top of the base ARIA spec.
325
- - Optimizes permitted roles: if `"presentation"` is in the permitted roles array, `"none"` is added, and vice versa (per WAI-ARIA 1.2 note on the `none` role).
328
+ - Optimizes permitted roles: if `"presentation"` is in the permitted roles array, `"none"` is added, and vice versa (per WAI-ARIA 1.2 note on the `none` role). In ARIA 1.3, if `"image"` is present, `"img"` is added, and vice versa (per the ARIA 1.3 `image`/`img` synonym).
326
329
  - Caches results by `localName + namespace + version`.
327
330
 
328
331
  2. Evaluates conditional overrides (the `conditions` block in the ARIA spec):
@@ -386,25 +389,140 @@ Returns an empty record if the element has no computed role.
386
389
 
387
390
  ---
388
391
 
389
- ### 10. `getAccname(el): string`
392
+ ### 10. `getAccname(el, specs, version): string`
390
393
 
391
394
  **Source:** `src/algorithm/aria/accname-computation.ts`
392
395
 
393
- Computes the accessible name for an element using the WAI-ARIA Accessible Name and Description Computation algorithm.
396
+ Computes the accessible name for a DOM element using the HTML-AAM §4.1 algorithm. This is the public facade that creates a DOM-based resolver and delegates to the pure `computeAccessibleName()` function.
394
397
 
395
398
  **Parameters:**
396
399
 
397
- | Parameter | Type | Description |
398
- | --------- | --------- | --------------- |
399
- | `el` | `Element` | The DOM element |
400
+ | Parameter | Type | Description |
401
+ | --------- | ------------- | ----------------------------------------------- |
402
+ | `el` | `Element` | The DOM element |
403
+ | `specs` | `MLMLSpec` | The full markup language specification |
404
+ | `version` | `ARIAVersion` | The ARIA specification version for role queries |
400
405
 
401
406
  **Returns:** `string` -- the computed accessible name, or an empty string if none is found.
402
407
 
403
- **Algorithm:**
408
+ **Architecture:**
409
+
410
+ The implementation is split into two layers:
411
+
412
+ - **`accname-computation.ts`** (facade): Creates a DOM-based `AccnameResolver`, handles reentrant guard for `:aria(has name)` selector cycles, and delegates to the pure algorithm.
413
+ - **`accname/compute.ts`** (pure algorithm): Implements Steps 2A–2I of AccName 1.2 §4.3.2 using only the `AccnameElement` and `AccnameResolver` interfaces, with no dependency on the DOM or markuplint types.
414
+
415
+ **Algorithm (AccName 1.2 §4.3.2):**
416
+
417
+ 1. **Step 2A — Hidden check:** If the element is hidden and not referenced by `aria-labelledby`, return empty.
418
+ 2. **Step 2B – `aria-labelledby`:** Resolve referenced elements, compute their names recursively (with cycle prevention via visited set).
419
+ 3. **Step 2D – `aria-label`:** Use the `aria-label` attribute value if non-empty.
420
+ 4. **Step 2E – Element-specific name:** Apply HTML-AAM §4.1 rules (label association, `<img alt>`, `<input value>`, `<fieldset>` legend, `<table>` caption, SVG `<title>`, etc.).
421
+ 5. **Step 2F – Name from content:** If the element's role allows name-from-content, or the element is referenced by `aria-labelledby`, recursively collect text from child nodes (with embedded control value extraction per Step 2C).
422
+ 6. **Step 2I – Title fallback:** Use the `title` attribute value.
423
+
424
+ **Reentrant guard:** `getAccname` uses a `WeakSet<Element>` to prevent infinite recursion when `getComputedRole` evaluates `:aria(has name)` selectors that call back into `getAccname`.
425
+
426
+ #### AccName Algorithm Control Flow
427
+
428
+ The following diagram shows the full control flow of the accessible name computation algorithm, mapping each step to its implementation file.
429
+
430
+ ```mermaid
431
+ flowchart TB
432
+ Start([Element]) --> Facade["getAccname()\n<i>accname-computation.ts</i>"]
433
+ Facade --> Guard{"Reentrant\nguard?"}
434
+ Guard -->|"Already computing"| Empty(["Return ''"])
435
+ Guard -->|"First call"| Resolver["createDomResolver()\n<i>accname-computation.ts</i>"]
436
+ Resolver --> Compute["computeAccessibleName()\n<i>compute.ts</i>"]
437
+
438
+ Compute --> Hidden{"Hidden &\nnot in\nlabelledby?"}
439
+ Hidden -->|Yes| EmptyResult(["Return empty"])
440
+ Hidden -->|No| Precomp{"getPrecomputedName?\n<i>[Implementation-specific]</i>"}
441
+
442
+ Precomp -->|"Has value"| PrecompResult(["Return precomputed name"])
443
+ Precomp -->|"No"| Step2B
444
+
445
+ subgraph step2b ["Step 2B — aria-labelledby (aria-steps.ts)"]
446
+ Step2B{"aria-labelledby\npresent &\nnot in labelledby\ntraversal?"}
447
+ Step2B -->|Yes| SplitIDs["Split IDREFs by whitespace"]
448
+ SplitIDs --> ForEachID["For each IDREF:\n• Skip visited (allow self-ref)\n• getElementById()\n• Recurse with inLabelledby=true"]
449
+ ForEachID --> JoinParts["Join parts with space"]
450
+ end
451
+
452
+ Step2B -->|"No / empty"| Step2D
453
+ JoinParts -->|"Has name"| LabelledbyResult(["Return name\nsource: aria-labelledby"])
454
+
455
+ subgraph step2d ["Step 2D — aria-label (aria-steps.ts)"]
456
+ Step2D{"aria-label\nnon-empty?"}
457
+ end
458
+
459
+ Step2D -->|Yes| AriaLabelResult(["Return name\nsource: aria-label"])
460
+ Step2D -->|No| Step2E
461
+
462
+ subgraph step2e ["Step 2E — Element-specific (element-names.ts)"]
463
+ Step2E["getElementSpecificName()"]
464
+ Step2E --> Dispatch{"Element\ntype?"}
465
+ Dispatch -->|"SVG"| SVGTitle["SVG: title child\n<i>SVG-AAM §8.1</i>"]
466
+ Dispatch -->|"input"| InputType["Input: branch by type\n<i>HTML-AAM §4.1</i>"]
467
+ Dispatch -->|"button"| BtnLabel["Label → content\n<i>HTML-AAM §4.1</i>"]
468
+ Dispatch -->|"fieldset"| Legend["Legend content\n<i>HTML-AAM §4.1</i>"]
469
+ Dispatch -->|"table"| Caption["Caption content\n<i>HTML-AAM §4.1</i>"]
470
+ Dispatch -->|"img"| ImgAlt["alt attr\n<i>HTML-AAM §4.1</i>"]
471
+ Dispatch -->|"Other"| LabelAssoc["label-steps.ts:\nLabel association\n<i>for= / ancestor</i>"]
472
+ end
473
+
474
+ Step2E -->|"Has name"| ElementResult(["Return name\nsource: element-specific"])
475
+ Step2E -->|"null"| Step2F
476
+
477
+ subgraph step2f ["Step 2F/2C — Name from content (helpers.ts)"]
478
+ Step2F{"Role allows\nnameFrom: content\nOR in labelledby\ntraversal?"}
479
+ Step2F -->|Yes| WalkChildren["resolveNameFromContent():\nFor each child node:"]
480
+ WalkChildren --> ChildType{"Node\ntype?"}
481
+ ChildType -->|"Text"| TextContent["Use textContent"]
482
+ ChildType -->|"Embedded\ncontrol"| EmbedValue["getEmbeddedControlValue()\n<i>AccName §4.3.2 Step 2C</i>"]
483
+ ChildType -->|"Element"| RecurseChild["Recurse computeFn();\nif no name → collectTextContent()\n<i>[Implementation-specific]</i>"]
484
+ TextContent --> JoinSpaces["Join with space"]
485
+ EmbedValue --> JoinSpaces
486
+ RecurseChild --> JoinSpaces
487
+ end
488
+
489
+ Step2F -->|"No"| Step2I
490
+ JoinSpaces -->|"Has name"| ContentResult(["Return name\nsource: content"])
491
+ JoinSpaces -->|"Empty"| Step2I
492
+
493
+ subgraph step2i ["Step 2I — Title fallback"]
494
+ Step2I{"title attr\nnon-empty?"}
495
+ end
496
+
497
+ Step2I -->|Yes| TitleResult(["Return name\nsource: title"])
498
+ Step2I -->|No| FinalEmpty(["Return empty"])
499
+ ```
500
+
501
+ #### W3C Specification References
502
+
503
+ | Spec | Section | Description |
504
+ | --------------------------------------------------- | ------------------ | ------------------------------------------------- |
505
+ | [AccName 1.2](https://www.w3.org/TR/accname-1.2/) | §4.3.2 | Computation Steps (Steps 2A–2I) |
506
+ | [HTML-AAM 1.0](https://www.w3.org/TR/html-aam-1.0/) | §4.1 | Accessible Name and Description Computation |
507
+ | [HTML-AAM 1.0](https://www.w3.org/TR/html-aam-1.0/) | §4.1 (per element) | Element-specific name computation rules |
508
+ | [SVG-AAM 1.0](https://www.w3.org/TR/svg-aam-1.0/) | §5.1.1, §8.1 | SVG accessibility tree inclusion and name mapping |
509
+
510
+ #### Implementation-Specific Extensions
511
+
512
+ The following behaviors extend beyond the strict AccName specification:
404
513
 
405
- 1. Delegates to `computeAccessibleName()` from the `dom-accessibility-api` library, which implements the full AccName 1.1 algorithm.
406
- 2. **Fallback for `<input>` elements:** If the computed name is empty (after trimming), returns the `placeholder` attribute value (trimmed).
407
- 3. Returns an empty string if no name is found through any method.
514
+ | Extension | Location | Rationale |
515
+ | ---------------------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
516
+ | Pre-computed name (`getPrecomputedName`) | `compute.ts` | Supports ml-core's Pretender integration for framework components |
517
+ | Transparent text collection (`collectTextContent`) | `helpers.ts` | Collects text from intermediate elements (e.g., `<span>` inside `<button>`) whose roles do not include `nameFrom: ["content"]` |
518
+ | `<select>` selected option (`getSelectedOptionText`) | `helpers.ts` | Static approximation of selected option text via `selected` attribute. Customizable `<select>` ([#2069](https://github.com/markuplint/markuplint/issues/2069)) will require: (1) `collectOptions` to skip new child types (`<button>`, `<datalist>`, `<selectedcontent>`), (2) evaluate whether `<selectedcontent>` affects name computation |
519
+ | Reentrant guard (`computingElements` WeakSet) | `accname-computation.ts` | Prevents infinite recursion from `:aria(has name)` pseudo-class in `getComputedRole` → `getARIA` → `matches` chain |
520
+
521
+ #### Known Limitations
522
+
523
+ | Limitation | AccName Reference | Description |
524
+ | -------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
525
+ | CSS-generated content (`::before`/`::after`) | §4.3.2 Step 2G | The AccName spec requires CSS-generated textual content (via the `content` property on `::before`/`::after` pseudo-elements) to be included in the accumulated text. Markuplint performs static HTML analysis without CSS processing, so this content is unavailable at lint time. Elements relying solely on CSS-generated content for their accessible name will not be detected. |
408
526
 
409
527
  ---
410
528
 
@@ -448,19 +566,19 @@ Determines whether an element is included in (exposed to) the Accessibility Tree
448
566
 
449
567
  **Source:** `src/algorithm/aria/has-required-owned-elements.ts`
450
568
 
451
- Two related functions for validating the "Required Owned Elements" constraint.
569
+ Two related functions for validating the "Allowed Accessibility Child Roles" constraint (called "Required Owned Elements" in ARIA 1.2).
452
570
 
453
571
  #### `hasRequiredOwnedElement`
454
572
 
455
- Checks whether an element satisfies the required owned elements constraint defined by its computed role.
573
+ Checks whether an element satisfies the "Allowed Accessibility Child Roles" constraint defined by its computed role.
456
574
 
457
575
  **Algorithm:**
458
576
 
459
577
  1. If the element has an `aria-owns` attribute, returns `true` (partial support -- the referenced elements are not validated).
460
578
  2. Computes the element's role via `getComputedRole()`.
461
- 3. If the role has no `requiredOwnedElements`, returns `true`.
462
- 4. Traverses the element's closest non-presentational descendants (children, transparently passing through presentational elements).
463
- 5. For each required owned element pattern, checks if any descendant matches via `isRequiredOwnedElement()`.
579
+ 3. If the role has no `allowedAccessibilityChildRoles`, returns `true`.
580
+ 4. Traverses the element's closest non-presentational descendants (children, transparently passing through elements with ownership-transparent roles via `isTransparentForOwnership()`). In ARIA 1.3, `generic` elements are additionally transparent.
581
+ 5. For each allowed accessibility child role pattern, checks if any descendant matches via `isRequiredOwnedElement()`.
464
582
 
465
583
  #### `isRequiredOwnedElement`
466
584
 
@@ -478,7 +596,7 @@ Determines whether an element matches a required owned element query.
478
596
 
479
597
  **Query syntax:** Supports chains with `>` notation. For example, `"group > listitem"` means the element must have role `"group"` and contain a descendant with role `"listitem"`.
480
598
 
481
- **Spec issue:** The WAI-ARIA specification has not decided whether "owned" means child or descendant. This implementation interprets "owned" as **child** (not descendant), to align with HTML semantics. Presentational children are traversed transparently.
599
+ **Ownership traversal:** In ARIA 1.1/1.2, the spec had not decided whether "owned" means child or descendant. This implementation interprets "owned" as **child** (not descendant), to align with HTML semantics. `presentation`/`none` children are traversed transparently. ARIA 1.3 formally resolves this with the definitions of "accessibility child" and "accessibility parent", and additionally makes `generic` elements transparent (via `isTransparentForOwnership()`).
482
600
 
483
601
  ---
484
602
 
@@ -507,6 +625,8 @@ Validates whether an element's parent hierarchy satisfies at least one of the re
507
625
  4. For each level, calls `getComputedRole()` with `assumeSingleNode = true` to get the parent's role independently.
508
626
  5. Returns `true` if any condition string fully matches the ancestor chain.
509
627
 
628
+ **ARIA 1.3 transparency:** In ARIA 1.3+, parent elements with `generic` or `none` roles are transparently skipped during matching (via `isTransparentForOwnership()`). In ARIA 1.1/1.2, parents are matched strictly without skipping.
629
+
510
630
  **Example:** For a `listitem` role with `requiredContextRole: ["list", "list > group"]`:
511
631
 
512
632
  - `"list"` matches if the parent has the `list` role.
@@ -557,13 +677,39 @@ Traverses the parent element chain to find the nearest ancestor with a non-prese
557
677
 
558
678
  1. Starts from `el.parentElement`.
559
679
  2. For each ancestor, computes the role via `getComputedRole()`.
560
- 3. If the ancestor's role is not presentational, returns that ancestor's computed role.
680
+ 3. If the ancestor's role is not transparent for ownership traversal (via `isTransparentForOwnership()`), returns that ancestor's computed role.
561
681
  4. Otherwise, continues to the next parent.
562
682
  5. If no non-presentational ancestor is found, returns `{ el: null, role: null }`.
563
683
 
684
+ **ARIA 1.3 transparency:** In ARIA 1.3, `generic` role elements are additionally skipped (alongside `presentation`/`none`), per the WAI-ARIA definitions of "accessibility child" and "accessibility parent".
685
+
686
+ ---
687
+
688
+ ### 16. `isTransparentForOwnership(roleName, version): boolean`
689
+
690
+ **Source:** `src/algorithm/aria/is-presentational.ts`
691
+
692
+ Determines whether a given role is transparent for ownership traversal. Used by `getNonPresentationalAncestor`, `getClosestNonPresentationalDescendants`, and `matchesContextRole` to decide which elements to skip.
693
+
694
+ **Parameters:**
695
+
696
+ | Parameter | Type | Description |
697
+ | ---------- | --------------------- | ------------------------------ |
698
+ | `roleName` | `string \| undefined` | The ARIA role name to check |
699
+ | `version` | `ARIAVersion` | The ARIA specification version |
700
+
701
+ **Returns:** `true` if the role should be skipped during ownership traversal.
702
+
703
+ **Version behavior:**
704
+
705
+ | ARIA Version | Transparent roles |
706
+ | ---------------- | --------------------------------- |
707
+ | `'1.1'`, `'1.2'` | `presentation`, `none` |
708
+ | `'1.3'` | `presentation`, `none`, `generic` |
709
+
564
710
  ---
565
711
 
566
- ### 16. `ariaSpecs(specs, version)`
712
+ ### 17. `ariaSpecs(specs, version)`
567
713
 
568
714
  **Source:** `src/algorithm/aria/aria-specs.ts`
569
715
 
@@ -576,9 +722,9 @@ A simple accessor function that retrieves the ARIA specification data for a spec
576
722
  | `specs` | `MLMLSpec` | The full markup language specification |
577
723
  | `version` | `ARIAVersion` | The ARIA specification version |
578
724
 
579
- **Returns:** `{ roles: ARIARoleInSchema[], graphicsRoles: ARIARoleInSchema[], props: ARIAProperty[] }`
725
+ **Returns:** `{ roles: ARIARoleInSchema[], graphicsRoles: ARIARoleInSchema[], dpubRoles: ARIARoleInSchema[], props: ARIAProperty[] }`
580
726
 
581
- **Implementation:** Returns `specs.def['#aria'][version]`, providing direct access to the roles, graphics roles, and properties defined for the requested ARIA version.
727
+ **Implementation:** Returns `specs.def['#aria'][version]`, providing direct access to the roles, graphics roles, DPub roles, and properties defined for the requested ARIA version.
582
728
 
583
729
  ## RoleComputationError Reference
584
730
 
@@ -636,6 +782,8 @@ This design allows the schema to define a base ARIA spec that works across versi
636
782
  ### Version Impact on Behavior
637
783
 
638
784
  - **`getNonPresentationalAncestor`**: In ARIA 1.1/1.2, ancestor role computation uses full context (`assumeSingleNode = false`). In ARIA 1.3+, each ancestor is computed independently (`assumeSingleNode = true`).
785
+ - **`isTransparentForOwnership`**: In ARIA 1.1/1.2, only `presentation`/`none` are transparent. In ARIA 1.3, `generic` is additionally transparent per the WAI-ARIA definitions of "accessibility child" and "accessibility parent".
786
+ - **`matchesContextRole`**: In ARIA 1.1/1.2, parent elements are matched strictly. In ARIA 1.3+, `generic`/`none` parents are transparently skipped.
639
787
  - **`namingProhibited`**: Only applicable in ARIA 1.2 and later. Version 1.1 always uses the base value.
640
788
  - **Role and property definitions**: The available roles and their properties may differ across versions (e.g., new roles added in 1.2 or 1.3).
641
789
 
@@ -646,6 +794,7 @@ The ARIA algorithms implement behavior defined in the following W3C specificatio
646
794
  - [WAI-ARIA 1.2](https://www.w3.org/TR/wai-aria-1.2/) -- Accessible Rich Internet Applications, primary reference
647
795
  - [WAI-ARIA 1.1](https://www.w3.org/TR/wai-aria-1.1/) -- Previous version, still supported
648
796
  - [HTML-AAM 1.0](https://www.w3.org/TR/html-aam-1.0/) -- HTML Accessibility API Mappings (implicit role mappings)
649
- - [AccName 1.1](https://www.w3.org/TR/accname-1.1/) -- Accessible Name and Description Computation
797
+ - [AccName 1.2](https://www.w3.org/TR/accname-1.2/) -- Accessible Name and Description Computation
650
798
  - [SVG-AAM 1.0](https://www.w3.org/TR/svg-aam-1.0/) -- SVG Accessibility API Mappings
799
+ - [DPub-ARIA 1.1](https://w3c.github.io/dpub-aria/) -- Digital Publishing WAI-ARIA Module (DPub roles)
651
800
  - [ARIA in HTML](https://www.w3.org/TR/html-aria/) -- Permitted roles and ARIA attribute constraints per HTML element
@@ -79,7 +79,7 @@ function getContentModel(
79
79
 
80
80
  **動作:**
81
81
 
82
- 1. ネストされた `Map<Specs, Map<Element, result>>` キャッシュを確認します。指定された specs 参照と要素インスタンスに対するキャッシュ結果が存在すれば、即座に返します。
82
+ 1. `WeakMap<Element, result>` キャッシュを確認します。指定された要素インスタンスに対するキャッシュ結果が存在すれば、即座に返します。
83
83
  2. `getSpec()` を使用して要素の仕様を検索します。見つからない場合、`null` をキャッシュして返します。
84
84
  3. `contentModel.conditional[]`(存在する場合)を走査します。各条件に対して `el.matches(cond.condition)` を呼び出します。
85
85
  4. 最初にマッチした条件の `contents` を返します。どの条件にもマッチしない場合、デフォルトの `contentModel.contents` を返します。
@@ -87,7 +87,7 @@ function getContentModel(
87
87
 
88
88
  **キャッシュ戦略:**
89
89
 
90
- キャッシュは 2 階層の `Map` です。外側のマップは `specs` 配列の参照をキーとし、内側のマップは `Element` インスタンスをキーとします。これにより、specs が変更された場合に結果が正しく無効化される一方、同じ要素に対する冗長な計算を回避できます。
90
+ キャッシュは `Element` インスタンスをキーとする `WeakMap` です。同じ要素に対する冗長な計算を回避しつつ、要素が参照されなくなった場合(例: 再パース後)にキャッシュエントリが自動的にガベージコレクションされます。
91
91
 
92
92
  ---
93
93
 
@@ -79,7 +79,7 @@ function getContentModel(
79
79
 
80
80
  **Behavior:**
81
81
 
82
- 1. Checks the nested `Map<Specs, Map<Element, result>>` cache. If a cached result exists for the given specs reference and element instance, returns it immediately.
82
+ 1. Checks the `WeakMap<Element, result>` cache. If a cached result exists for the given element instance, returns it immediately.
83
83
  2. Looks up the element's spec using `getSpec()`. If not found, caches and returns `null`.
84
84
  3. Iterates over `contentModel.conditional[]` (if present). For each condition, calls `el.matches(cond.condition)`.
85
85
  4. Returns the first matching condition's `contents`. If no condition matches, returns the default `contentModel.contents`.
@@ -87,7 +87,7 @@ function getContentModel(
87
87
 
88
88
  **Caching strategy:**
89
89
 
90
- The cache is a two-level `Map`: the outer map is keyed by the `specs` array reference, and the inner map is keyed by the `Element` instance. This ensures results are correctly invalidated when specs change while avoiding redundant computation for the same element.
90
+ The cache is a `WeakMap` keyed by the `Element` instance. This avoids redundant computation for the same element while ensuring cache entries are automatically garbage-collected when elements are no longer referenced (e.g., after a re-parse).
91
91
 
92
92
  ---
93
93
 
@@ -128,12 +128,13 @@ schema:json → schema:content-models → schema:attributes → schema:aria →
128
128
 
129
129
  ### テストファイル
130
130
 
131
- パッケージには vitest を使用する 15 のテストファイルがあります:
131
+ パッケージには vitest を使用する 18 のテストファイルがあります:
132
132
 
133
- | ディレクトリ | テストファイル数 | カバレッジ |
134
- | --------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
- | `src/algorithm/aria/` | 11 | `accname-computation`, `get-computed-aria-props`, `get-computed-role`, `get-implicit-role-spec`, `get-implicit-role`, `get-permitted-roles-spec`, `get-role-spec`, `has-required-owned-elements`, `is-exposed`, `matches-context-role` |
136
- | `src/utils/` | 4 | `get-attr-specs-spec`, `get-spec-by-tag-name`, `resolve-namespace`, `resolve-version`, `schema-to-spec` |
133
+ | ディレクトリ | テストファイル数 | カバレッジ |
134
+ | ----------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
+ | `src/algorithm/aria/` | 11 | `accname-computation`, `get-computed-aria-props`, `get-computed-role`, `get-implicit-role-spec`, `get-implicit-role`, `get-permitted-roles-spec`, `get-role-spec`, `has-required-owned-elements`, `is-exposed`, `matches-context-role` |
136
+ | `src/algorithm/aria/accname/` | 3 | `aria-steps`, `compute`, `element-names`, `label-steps` |
137
+ | `src/utils/` | 4 | `get-attr-specs-spec`, `get-spec-by-tag-name`, `resolve-namespace`, `resolve-version`, `schema-to-spec` |
137
138
 
138
139
  ### テストの実行
139
140
 
@@ -154,13 +155,12 @@ yarn test packages/@markuplint/ml-spec/src/algorithm/aria/get-computed-role.spec
154
155
 
155
156
  ### ランタイム依存
156
157
 
157
- | パッケージ | バージョン | 目的 | 更新リスク |
158
- | ----------------------- | ---------- | ---------------------------------- | ------------------ |
159
- | `@markuplint/ml-ast` | 4.4.10 | `NamespaceURI` 型 | 低(内部) |
160
- | `@markuplint/types` | 4.8.1 | 属性値型の `Type` ユニオン | 中(スキーマ参照) |
161
- | `dom-accessibility-api` | 0.7.1 | AccName 計算 | 中(仕様準拠) |
162
- | `is-plain-object` | 5.0.0 | AAM 情報のプレーンオブジェクト検出 | 低(安定 API) |
163
- | `type-fest` | 4.41.0 | `ReadonlyDeep` ユーティリティ型 | 低(型のみ) |
158
+ | パッケージ | バージョン | 目的 | 更新リスク |
159
+ | -------------------- | ---------- | ---------------------------------- | ------------------ |
160
+ | `@markuplint/ml-ast` | 4.4.10 | `NamespaceURI` 型 | 低(内部) |
161
+ | `@markuplint/types` | 4.8.1 | 属性値型の `Type` ユニオン | 中(スキーマ参照) |
162
+ | `is-plain-object` | 5.0.0 | AAM 情報のプレーンオブジェクト検出 | 低(安定 API) |
163
+ | `type-fest` | 4.41.0 | `ReadonlyDeep` ユーティリティ型 | 低(型のみ) |
164
164
 
165
165
  ### 開発依存
166
166
 
@@ -171,7 +171,6 @@ yarn test packages/@markuplint/ml-spec/src/algorithm/aria/get-computed-role.spec
171
171
 
172
172
  ### 依存関係の更新
173
173
 
174
- - **`dom-accessibility-api`**: 更新により AccName 計算の動作が変わる場合があります。更新後は `accname-computation.spec.ts` テストを実行してください。
175
174
  - **`json-schema-to-typescript`**: メジャーバージョン更新により、生成される型の出力が変わる場合があります(フォーマット、optional の扱い)。更新後は `yarn workspace @markuplint/ml-spec run schema` を実行し、`src/types/*.ts` の差分を確認してください。
176
175
  - **`@markuplint/types`**: 更新後は必ず `yarn up:schema` を実行して、スキーマ参照の一貫性を確保してください。
177
176
  - **`type-fest`**: 型のみの依存です。自由に更新できますが、ビルドが成功することを確認してください(`yarn build --scope @markuplint/ml-spec`)。
@@ -278,7 +277,7 @@ W3C 仕様が更新された場合(例: WAI-ARIA 1.3 が勧告になった場
278
277
  | --------------------------------------------- | ----------------------------------------------- | -------------------------- |
279
278
  | `getARIA()` 内部キャッシュ | `Map`(`localName + namespace + version` キー) | プロセス再起動時のみクリア |
280
279
  | `getSpecByTagName()` キャッシュ | `Map`(`namespace:localName` キー) | specs インスタンスごと |
281
- | `getContentModel()` キャッシュ | `Map<Specs, Map<Element, ...>>` | ネスト、specs + 要素ごと |
280
+ | `getContentModel()` キャッシュ | `WeakMap<Element, ...>` | 要素ごと、GC セーフ |
282
281
  | `contentModelCategoryToTagNames()` キャッシュ | モジュールレベル `Map<Category, string[]>` | グローバル、無効化なし |
283
282
  | `getAttrSpecs()` キャッシュ | `WeakSet` + `Map`(スキーマごと) | 新しいスキーマでリセット |
284
283
  | `resolveNamespace()` キャッシュ | モジュールレベル `Map` | グローバル、無効化なし |
@@ -310,14 +309,6 @@ W3C 仕様が更新された場合(例: WAI-ARIA 1.3 が勧告になった場
310
309
 
311
310
  **対処:** 差分を注意深く確認してください。型が意味的に同等であれば、変更をコミットしてください。動作が変わった場合(例: 以前 optional だったフィールドが required になった)、`json-schema-to-typescript` のチェンジログを調査してください。
312
311
 
313
- ### `dom-accessibility-api` 更新後のテスト失敗
314
-
315
- **症状:** `dom-accessibility-api` の更新後、`accname-computation.spec.ts` が失敗する。
316
-
317
- **原因:** ライブラリが AccName アルゴリズムの実装を更新し、計算されるアクセシブル名が変わった。
318
-
319
- **対処:** [AccName 1.1 仕様](https://www.w3.org/TR/accname-1.1/)に照らして新しい動作を確認してください。ライブラリがより仕様準拠になった場合は、テストの期待値を更新してください。
320
-
321
312
  ### ビルドエラー: 生成型の不一致
322
313
 
323
314
  **症状:** `src/types/aria.ts`、`attributes.ts`、`permitted-structures.ts` の型を参照する TypeScript ビルドエラー。
@@ -128,12 +128,13 @@ These files carry "DO NOT MODIFY" headers and are overwritten by the generation
128
128
 
129
129
  ### Test files
130
130
 
131
- The package has 15 test files using vitest:
131
+ The package has 18 test files using vitest:
132
132
 
133
- | Directory | Test files | Coverage |
134
- | --------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
- | `src/algorithm/aria/` | 11 | `accname-computation`, `get-computed-aria-props`, `get-computed-role`, `get-implicit-role-spec`, `get-implicit-role`, `get-permitted-roles-spec`, `get-role-spec`, `has-required-owned-elements`, `is-exposed`, `matches-context-role` |
136
- | `src/utils/` | 4 | `get-attr-specs-spec`, `get-spec-by-tag-name`, `resolve-namespace`, `resolve-version`, `schema-to-spec` |
133
+ | Directory | Test files | Coverage |
134
+ | ----------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
+ | `src/algorithm/aria/` | 11 | `accname-computation`, `get-computed-aria-props`, `get-computed-role`, `get-implicit-role-spec`, `get-implicit-role`, `get-permitted-roles-spec`, `get-role-spec`, `has-required-owned-elements`, `is-exposed`, `matches-context-role` |
136
+ | `src/algorithm/aria/accname/` | 3 | `aria-steps`, `compute`, `element-names`, `label-steps` |
137
+ | `src/utils/` | 4 | `get-attr-specs-spec`, `get-spec-by-tag-name`, `resolve-namespace`, `resolve-version`, `schema-to-spec` |
137
138
 
138
139
  ### Running tests
139
140
 
@@ -154,13 +155,12 @@ Tests depend on `@markuplint/test-tools` (devDependency) which provides HTML par
154
155
 
155
156
  ### Runtime dependencies
156
157
 
157
- | Package | Version | Purpose | Update risk |
158
- | ----------------------- | ------- | -------------------------------------- | ------------------------- |
159
- | `@markuplint/ml-ast` | 4.4.10 | `NamespaceURI` type | Low (internal) |
160
- | `@markuplint/types` | 4.8.1 | `Type` union for attribute value types | Medium (schema reference) |
161
- | `dom-accessibility-api` | 0.7.1 | AccName computation | Medium (spec compliance) |
162
- | `is-plain-object` | 5.0.0 | Plain object detection for AAM info | Low (stable API) |
163
- | `type-fest` | 4.41.0 | `ReadonlyDeep` utility type | Low (types only) |
158
+ | Package | Version | Purpose | Update risk |
159
+ | -------------------- | ------- | -------------------------------------- | ------------------------- |
160
+ | `@markuplint/ml-ast` | 4.4.10 | `NamespaceURI` type | Low (internal) |
161
+ | `@markuplint/types` | 4.8.1 | `Type` union for attribute value types | Medium (schema reference) |
162
+ | `is-plain-object` | 5.0.0 | Plain object detection for AAM info | Low (stable API) |
163
+ | `type-fest` | 4.41.0 | `ReadonlyDeep` utility type | Low (types only) |
164
164
 
165
165
  ### Dev dependencies
166
166
 
@@ -171,7 +171,6 @@ Tests depend on `@markuplint/test-tools` (devDependency) which provides HTML par
171
171
 
172
172
  ### Updating dependencies
173
173
 
174
- - **`dom-accessibility-api`**: Updates may change AccName computation behavior. Run `accname-computation.spec.ts` tests after updating.
175
174
  - **`json-schema-to-typescript`**: Major version updates may change generated type output (formatting, optional handling). After updating, run `yarn workspace @markuplint/ml-spec run schema` and review diffs in `src/types/*.ts`.
176
175
  - **`@markuplint/types`**: Always run `yarn up:schema` after updating to ensure schema references stay consistent.
177
176
  - **`type-fest`**: Type-only dependency. Update freely, but verify the build succeeds (`yarn build --scope @markuplint/ml-spec`).
@@ -278,7 +277,7 @@ The package uses several runtime caches that are **never invalidated during a pr
278
277
  | ---------------------------------------- | ------------------------------------------------ | ------------------------------- |
279
278
  | `getARIA()` internal cache | `Map` keyed by `localName + namespace + version` | Cleared only on process restart |
280
279
  | `getSpecByTagName()` cache | `Map` keyed by `namespace:localName` | Per-specs instance |
281
- | `getContentModel()` cache | `Map<Specs, Map<Element, ...>>` | Nested, per-specs + per-element |
280
+ | `getContentModel()` cache | `WeakMap<Element, ...>` | Per-element, GC-safe |
282
281
  | `contentModelCategoryToTagNames()` cache | Module-level `Map<Category, string[]>` | Global, never invalidated |
283
282
  | `getAttrSpecs()` cache | `WeakSet` + `Map` per schema | New schema resets cache |
284
283
  | `resolveNamespace()` cache | Module-level `Map` | Global, never invalidated |
@@ -310,14 +309,6 @@ If you add a new algorithm function that computes expensive results, consider ad
310
309
 
311
310
  **Fix:** Review the diff carefully. If the types are semantically equivalent, commit the changes. If behavior changed (e.g., previously optional fields became required), investigate the `json-schema-to-typescript` changelog.
312
311
 
313
- ### Test failures after `dom-accessibility-api` update
314
-
315
- **Symptom:** `accname-computation.spec.ts` fails after updating `dom-accessibility-api`.
316
-
317
- **Cause:** The library updated its AccName algorithm implementation, changing computed accessible names.
318
-
319
- **Fix:** Verify the new behavior against the [AccName 1.1 specification](https://www.w3.org/TR/accname-1.1/). If the library is now more spec-compliant, update the test expectations.
320
-
321
312
  ### Build error: generated type mismatch
322
313
 
323
314
  **Symptom:** TypeScript build errors referencing types in `src/types/aria.ts`, `attributes.ts`, or `permitted-structures.ts`.