Skip to content

[A11Y-12317] [A11Y-12318] Add SIA-R121 and SIA-R122. - #2174

Open
pooya-si wants to merge 10 commits into
mainfrom
A11Y-12317
Open

pooya-si wants to merge 10 commits into
mainfrom
A11Y-12317

Conversation

@pooya-si

@pooya-si pooya-si commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Description

  • Add a new rule to verify whether <ul>, <ol>, and <dl> only contain the child element types the HTML spec allows.
  • Add R120 to identify whether a document includes elements with duplicate access keys.

Jira Ticket

A11Y-12317
A11Y-12318

Check that <ul>, <ol> and <dl> elements only contain the children the HTML
content model allows, following the specification rather than a relaxed
reading of it: a <dl> wraps every name-value group or none of them, a <div>
inside a <dl> holds exactly one group, every group must be well formed, and
children are matched by element name rather than by role.
@changeset-bot

changeset-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e9f470a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 76 packages
Name Type
@siteimprove/alfa-rules Minor
@siteimprove/alfa-act Minor
@siteimprove/alfa-affine Minor
@siteimprove/alfa-applicative Minor
@siteimprove/alfa-aria Minor
@siteimprove/alfa-array Minor
@siteimprove/alfa-bits Minor
@siteimprove/alfa-branched Minor
@siteimprove/alfa-cache Minor
@siteimprove/alfa-callback Minor
@siteimprove/alfa-cascade Minor
@siteimprove/alfa-clone Minor
@siteimprove/alfa-collection Minor
@siteimprove/alfa-comparable Minor
@siteimprove/alfa-continuation Minor
@siteimprove/alfa-css-feature Minor
@siteimprove/alfa-css Minor
@siteimprove/alfa-device Minor
@siteimprove/alfa-dom Minor
@siteimprove/alfa-eaa Minor
@siteimprove/alfa-earl Minor
@siteimprove/alfa-either Minor
@siteimprove/alfa-emitter Minor
@siteimprove/alfa-encoding Minor
@siteimprove/alfa-equatable Minor
@siteimprove/alfa-flags Minor
@siteimprove/alfa-fnv Minor
@siteimprove/alfa-foldable Minor
@siteimprove/alfa-functor Minor
@siteimprove/alfa-generator Minor
@siteimprove/alfa-graph Minor
@siteimprove/alfa-hash Minor
@siteimprove/alfa-http Minor
@siteimprove/alfa-iana Minor
@siteimprove/alfa-iterable Minor
@siteimprove/alfa-json-ld Minor
@siteimprove/alfa-json Minor
@siteimprove/alfa-lazy Minor
@siteimprove/alfa-list Minor
@siteimprove/alfa-map Minor
@siteimprove/alfa-mapper Minor
@siteimprove/alfa-math Minor
@siteimprove/alfa-monad Minor
@siteimprove/alfa-network Minor
@siteimprove/alfa-option Minor
@siteimprove/alfa-painting-order Minor
@siteimprove/alfa-parser Minor
@siteimprove/alfa-performance Minor
@siteimprove/alfa-predicate Minor
@siteimprove/alfa-record Minor
@siteimprove/alfa-rectangle Minor
@siteimprove/alfa-reducer Minor
@siteimprove/alfa-refinement Minor
@siteimprove/alfa-result Minor
@siteimprove/alfa-rng Minor
@siteimprove/alfa-sarif Minor
@siteimprove/alfa-selective Minor
@siteimprove/alfa-selector Minor
@siteimprove/alfa-sequence Minor
@siteimprove/alfa-set Minor
@siteimprove/alfa-slice Minor
@siteimprove/alfa-string Minor
@siteimprove/alfa-style Minor
@siteimprove/alfa-table Minor
@siteimprove/alfa-test Minor
@siteimprove/alfa-thunk Minor
@siteimprove/alfa-time Minor
@siteimprove/alfa-toolchain Minor
@siteimprove/alfa-trampoline Minor
@siteimprove/alfa-tree Minor
@siteimprove/alfa-trilean Minor
@siteimprove/alfa-tuple Minor
@siteimprove/alfa-url Minor
@siteimprove/alfa-wcag Minor
@siteimprove/alfa-web Minor
@siteimprove/alfa-xpath Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Flattened-tree and namespace handling can produce incorrect results, and the required API report is outdated.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds stable rule SIA-R119 to validate HTML list content models.

Changes:

  • Implements validation for <ul>, <ol>, and <dl>.
  • Adds comprehensive rule tests.
  • Exports the rule and adds release metadata.
File summaries
File Description
.changeset/brave-lists-gather.md Documents the new rule.
packages/alfa-rules/src/rules.ts Exports R119.
packages/alfa-rules/src/sia-r119/rule.ts Implements list validation.
packages/alfa-rules/src/tsconfig.json Includes the rule source.
packages/alfa-rules/test/sia-r119/rule.spec.tsx Tests rule behavior.
packages/alfa-rules/test/tsconfig.json Includes the new tests.
Review details

Suppressed comments (4)

packages/alfa-rules/src/sia-r119/rule.ts:85

  • This also checks flattened-tree text rather than direct DOM text. For example, text in the light DOM of a shadow-host <div> wrapper can be hidden by its shadow root and escape the <dl> group validation. Use the default DOM-tree traversal, matching the content model being validated.
    .children(Node.fullTree)

packages/alfa-rules/src/sia-r119/rule.ts:91

  • This accepts any element whose local name is li, including a foreign-namespace element created with createElementNS. The content model requires an HTML <li>, so include the HTML namespace in the predicate.
  const disallowed = elementChildren(target).reject(hasName("li"));

packages/alfa-rules/src/sia-r119/rule.ts:135

  • A wrapper can similarly pass with foreign-namespace dt/dd children because hasName compares only the local name. Validate that the wrapper children are HTML elements before treating them as a well-formed group.
  if (!children.reject(hasName("dt", "dd")).isEmpty()) {

packages/alfa-rules/src/sia-r119/rule.ts:100

  • The allowed direct children are matched only by local name, which lets foreign-namespace div, dt, or dd elements satisfy the HTML <dl> content model. Require Namespace.HTML in this predicate so those nodes are reported as disallowed.
  const disallowed = children.reject(hasName("div", "dt", "dd"));
  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/alfa-rules/src/rules.ts Outdated
Comment thread packages/alfa-rules/src/sia-r119/rule.ts Outdated
Comment thread packages/alfa-rules/src/sia-r119/rule.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The text check can produce incorrect results for elements with shadow content, and one diagnostic is inaccurate for description lists.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread packages/alfa-rules/src/sia-r119/rule.ts
Comment thread packages/alfa-rules/src/sia-r119/rule.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation matches the documented content models and has comprehensive focused coverage.

Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Flat-tree traversal produces incorrect HTML content-model results for slotted children.

Review details

Suppressed comments (1)

packages/alfa-rules/src/sia-r119/rule.ts:101

  • Node.fullTree uses flat-tree traversal (packages/alfa-dom/src/node/node.ts:397-407), which replaces a direct <slot> with assigned light-DOM nodes (packages/alfa-dom/src/node/slotable/element.ts:174-186). HTML content models constrain actual DOM children, so <ul><slot></slot></ul> remains invalid even when <li> nodes are assigned; assigned text is likewise not direct list text. This currently makes the rule contradict its stated HTML-spec check and causes the slot tests to lock in incorrect results. Use default children() for both element and text checks, remove the straySlots workaround, and update the slot expectations accordingly.
    .children(Node.fullTree)
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@pooya-si

pooya-si commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author
  • Node.fullTree uses flat-tree traversal (packages/alfa-dom/src/node/node.ts:397-407), which replaces a direct <slot> with assigned light-DOM nodes (packages/alfa-dom/src/node/slotable/element.ts:174-186). HTML content models constrain actual DOM children, so <ul><slot></slot></ul> remains invalid even when <li> nodes are assigned; assigned text is likewise not direct list text. This currently makes the rule contradict its stated HTML-spec check and causes the slot tests to lock in incorrect results. Use default children() for both element and text checks, remove the straySlots workaround, and update the slot expectations accordingly.

@copilot The case you originally raised is already caught. <ul><slot></slot></ul> in an ordinary document now fails, naming the slot. straySlots is not a workaround for the traversal; it is what closed that gap. It targets precisely the slot that can never be assigned anything, because its root is a document rather than a shadow root.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Flattened-tree traversal validates slotted content instead of the lists’ actual DOM children.

Review details

Suppressed comments (2)

packages/alfa-rules/src/sia-r119/rule.ts:101

  • Node.fullTree requests flattened traversal (packages/alfa-dom/src/node/node.ts:403-407), and Element.children consequently replaces a direct <slot> with its assigned nodes (packages/alfa-dom/src/node/slotable/element.ts:174-189). This makes a shadow-tree <ul><slot></slot></ul> pass whenever <li> nodes are assigned, even though the list's actual child is the disallowed <slot>. Content-model validation should use children() and remove the shadow-dependent straySlots exception; the slot tests that currently expect this case to pass need to be updated as part of the fix.
    .children(Node.fullTree)

packages/alfa-rules/src/sia-r119/rule.ts:108

  • The text check also uses the flattened tree, so assigned light-DOM text is reported as direct text of the list. For <ul><slot></slot></ul> with text assigned to the slot, the actual invalid child is <slot>, but this short-circuits with HasDisallowedText instead. Inspect default children() here so only actual direct text nodes participate in the content-model check, and update the corresponding test expectation.
    .children(Node.fullTree)
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@pooya-si

Copy link
Copy Markdown
Contributor Author

🔵 Needs a closer look

Flattened-tree traversal validates slotted content instead of the lists’ actual DOM children.

Review details

Suppressed comments (2)

packages/alfa-rules/src/sia-r119/rule.ts:101

  • Node.fullTree requests flattened traversal (packages/alfa-dom/src/node/node.ts:403-407), and Element.children consequently replaces a direct <slot> with its assigned nodes (packages/alfa-dom/src/node/slotable/element.ts:174-189). This makes a shadow-tree <ul><slot></slot></ul> pass whenever <li> nodes are assigned, even though the list's actual child is the disallowed <slot>. Content-model validation should use children() and remove the shadow-dependent straySlots exception; the slot tests that currently expect this case to pass need to be updated as part of the fix.
    .children(Node.fullTree)

packages/alfa-rules/src/sia-r119/rule.ts:108

  • The text check also uses the flattened tree, so assigned light-DOM text is reported as direct text of the list. For <ul><slot></slot></ul> with text assigned to the slot, the actual invalid child is <slot>, but this short-circuits with HasDisallowedText instead. Inspect default children() here so only actual direct text nodes participate in the content-model check, and update the corresponding test expectation.
    .children(Node.fullTree)
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@copilot review your suggestion against cons of using children() e.g. a list whose items are slotted in from the light DOM

Copilot AI commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

🔵 Needs a closer look

Flattened-tree traversal validates slotted content instead of the lists’ actual DOM children.

...

I withdraw the blanket recommendation to switch to children(). It would change what this rule checks:

  • For a shadow-tree <ul><slot></slot></ul> receiving <li> elements, children() sees only the slot and reports a failure despite the assembled list having the intended structure.
  • For assigned loose text or disallowed elements, it reports the slot instead of inspecting the problematic content. An empty shadow-tree slot would also fail rather than represent an empty list.
  • The ordinary-document slot case is already covered by straySlots() and its tests; removing it while retaining flattened traversal would lose that coverage.

Default traversal is appropriate for a strict direct-DOM HTML conformance check. Flattened traversal serves the assembled-list accessibility check explicitly intended by the new slot tests. Those are different contracts; my earlier suggestion privileged the former without accounting for the latter.

Given the intended slot support, I recommend keeping the current traversal and straySlots(). The remaining concern is wording: the rule’s description should distinguish applying the allowed-child constraints to flattened content from strictly validating DOM content models. No code changes made.

@pooya-si
pooya-si marked this pull request as ready for review September 22, 2026 03:19
@pooya-si
pooya-si requested a review from a team as a code owner September 22, 2026 03:19
@Siteimprove Siteimprove deleted a comment from JoeCroninSI Sep 22, 2026
@pooya-si pooya-si changed the title [A11Y-12317] Add SIA-R119. [A11Y-12317] [A11Y-12318] Add SIA-R121 and SIA-R122. Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants