Skip to content

fix: add role="group" to stack layout div with aria-label for WCAG 2.2 compliance#18

Open
iago1501 wants to merge 2 commits into
masterfrom
fix/add-role-region-aria-label
Open

fix: add role="group" to stack layout div with aria-label for WCAG 2.2 compliance#18
iago1501 wants to merge 2 commits into
masterfrom
fix/add-role-region-aria-label

Conversation

@iago1501

@iago1501 iago1501 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds role="group" to the stack layout <div> that already carries aria-label, fixing the accessibility violation identified in the external audit for the kohlerqa account.

Why is this needed?

The current implementation uses aria-label on a <div> without an explicit role. Per the ARIA 1.2 spec, aria-label is a prohibited attribute on elements with the implicit role="generic" (the default for <div>).

This violates:

  • WCAG 2.2 Level A — Criterion 4.1.2 (Name, Role, Value)
  • ARIA 1.2aria-label prohibited on generic role elements

Why role="group" and not role="region"?

role="region" is a landmark, meant for a small number of significant page sections. A layout container that always renders an aria-label (falling back to a generic message) would add landmarks with generic/duplicated names, polluting screen-reader landmark navigation. role="group" also accepts aria-label, resolves the ARIA 1.2 / WCAG 4.1.2 violation, and is not a landmark — so it fixes the audit finding without the side effect. This also keeps stack-layout consistent with the sibling fix in vtex.flex-layout and with the existing role="group" usage in vtex.slider-layout.

What changed?

  • react/index.tsx — set role="group" on the main stack layout div
  • CHANGELOG.md — added entry for this fix

Testing / impact

This does not change the visual output, but it does change the accessibility tree: the div now exposes a valid group role with its accessible name, and no landmark is introduced.

References

….2 compliance

The div rendered by stack-layout uses aria-label without an explicit
role. Per ARIA 1.2, aria-label is a prohibited attribute on elements
with the implicit role="generic" (the default for a div), which is a
WCAG 2.2 Level A (4.1.2 Name, Role, Value) violation flagged by an
external audit on the kohlerqa account. role="region" makes the
element a valid landmark that accepts an accessible name.
@vtex-io-ci-cd

vtex-io-ci-cd Bot commented Jul 3, 2026

Copy link
Copy Markdown

Hi! I'm VTEX IO CI/CD Bot and I'll be helping you to publish your app! 🤖

Please select which version do you want to release:

  • Patch (backwards-compatible bug fixes)

  • Minor (backwards-compatible functionality)

  • Major (incompatible API changes)

And then you just need to merge your PR when you are ready! There is no need to create a release commit/tag.

  • No thanks, I would rather do it manually 😞

@vtex-io-docs-bot

vtex-io-docs-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

Beep boop 🤖

I noticed you didn't make any changes at the docs/ folder

  • There's nothing new to document 🤔
  • I'll do it later 😞

In order to keep track, I'll create an issue if you decide now is not a good time

  • I just updated 🎉🎉

@iago1501 iago1501 self-assigned this Jul 3, 2026
@iago1501 iago1501 added the enhancement New feature or request label Jul 3, 2026
@iago1501
iago1501 requested a review from gabpaladino July 3, 2026 21:21
@vsseixaso

vsseixaso commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Suggestion: use role="group" instead of role="region".

role="region" is a landmark. Since flex-layout (flex-row) is one of the most-used blocks in the Store Framework — and frequently nested — this patch makes a typical page expose dozens of region landmarks, including nested rows (the role is added regardless of isTopLevel). On top of that, when the arialabel prop is not provided, the label falls back to the intl default and several regions end up with the same generic name ('row', since htmlId is usually undefined). Multiple landmarks with an identical name is an anti-pattern (WCAG ARIA13) and pollutes screen-reader landmark navigation — in practice it can be worse than the original violation.

Since this is a global app (used by every store, not just kohlerqa), this side effect regresses accessibility across the whole installed base just to resolve one account's audit.

role="group" resolves the ARIA 1.2 violation equally well (it also accepts aria-label), is semantically appropriate for a layout wrapper, and is not a landmark → zero navigation noise. Worth noting that the kohlerqa audit only required "a valid role for the aria-label", not region specifically — choosing region was a suggestion from the ticket, so we can adjust it without contradicting the auditor.

Alternative (or complement): make role/aria-label opt-in — only render them when the arialabel prop is passed, removing the intl fallback. This addresses the root cause: today the fallback forces label + role on every instance, even where the author didn't ask for it.

role="region" is a landmark and would turn every stack container into a
region in the screen-reader landmark navigation, with a generic/duplicated
accessible name. role="group" also accepts aria-label and resolves the
ARIA 1.2 / WCAG 4.1.2 violation without introducing any landmark, matching
the existing pattern in slider-layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vsseixaso vsseixaso changed the title fix: add role="region" to stack layout div with aria-label for WCAG 2.2 compliance fix: add role="group" to stack layout div with aria-label for WCAG 2.2 compliance Jul 22, 2026
@vsseixaso

vsseixaso commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Change: role="region"role="group"

The audit finding (kohlerqa) is aria-label on a <div> without a valid role (ARIA 1.2 / WCAG 4.1.2). region fixes it, but it is a landmark: since the container always renders an aria-label (with a generic fallback), it adds landmarks with generic/duplicated names to screen-reader navigation. group also accepts aria-label, resolves the same violation, and is not a landmark — the same approach as the sibling PR in flex-layout and the pattern already used in slider-layout.

Before vs After (validated live on flex-layout, same homepage)

Metric region (before) group (after)
role="region" landmarks 4 (all "Section row") 0
Div with valid aria-label 4 4
ARIA 1.2 violation (label without role) 0 0

Note: stack-layout is not placed on any page of this test store, so the live measurement was done on flex-layout (same one-line change, same mechanism). The behavior is identical.

Workspaces to test (axe / screen reader / landmarks rotor)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants