Skip to content

fix(a11y): add missing ARIA row/rowgroup structure to grid headers - #2694

Merged
ghiscoding merged 4 commits into
masterfrom
bugfix/aria-grid-header
Aug 5, 2026
Merged

fix(a11y): add missing ARIA row/rowgroup structure to grid headers#2694
ghiscoding merged 4 commits into
masterfrom
bugfix/aria-grid-header

Conversation

@ghiscoding

@ghiscoding ghiscoding commented Aug 4, 2026

Copy link
Copy Markdown
Owner

vibe coded with Copilot using GPT-5.3-Codex and double-checked implementation with Claude Sonnet 5

Describe your change

This PR supersedes #2680 and keeps the same a11y intent, with a standards-safe focus sink implementation.

The grid container uses role="grid" and header cells use role="columnheader", but intermediate header containers did not expose the required rowgroup and row hierarchy in the accessibility tree. That can produce two common audit failures:

  • aria-required-parent: each columnheader must have a row parent
  • aria-required-children: grid ownership can be computed incorrectly when required structural roles are missing

Nothing is visually broken, but assistive technology does not get a complete grid header structure.

This patch adds the missing header and filter-row role skeleton at creation time:

Element Role added
.slick-header (left/right header scroller) rowgroup
.slick-header-columns (left/right) row
.slick-headerrow (left/right filter-row scroller) rowgroup
.slick-headerrow-columns (left/right) row
.slick-headerrow-column (filter-row cell) gridcell

Focus sink handling is intentionally different from #2680:

  • no aria-hidden on focus sinks
  • focus sinks remain tabindex="-1" for keyboard sentinel behavior
  • both sinks are moved outside the role="grid" subtree, so they are not owned children of grid
  • destroy now explicitly removes both external sinks to avoid detached leftovers

Scope note:

  • filter row changes are applied as one unit; adding role=row to .slick-headerrow-columns without role=gridcell on .slick-headerrow-column would introduce a new aria-required-children issue when showHeaderRow is enabled

aria-colcount and aria-rowcount behavior remains unchanged.

How to reproduce

Run an a11y audit (axe or Lighthouse) on any page with a grid.

Before:

  • aria-required-parent on header columns
  • aria-required-children on grid ownership

After:

  • header and filter-row hierarchy is exposed as rowgroup > row > columnheader or gridcell
  • focus sinks are excluded from grid ownership by DOM placement, not aria-hidden

Verification

Updated unit tests in slickGrid.spec.ts verify:

  • rowgroup > row > columnheader and rowgroup > row > gridcell structure
  • focus sinks are outside the grid subtree
  • focus sinks remain focusable with tabindex -1 and have no aria-hidden
  • external focus sinks are removed on destroy
  • role structure remains correct with frozenColumn enabled

Targeted test run passed:

  • vitest run --config vitest.config.mts slickGrid.spec.ts
  • Result: PASS (415), FAIL (0)

Attribution

Caveat

One caveat worth knowing (not a bug, just a limitation):

If a consumer moves _container to a different DOM parent after grid init, the focus sinks stay attached to the original parent reference, not wherever the container moved to. This is a narrow edge case — SlickGrid doesn't support post-init container relocation elsewhere either, so it's consistent with existing assumptions, not a new regression.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (3d74d24) to head (f509d1c).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2694   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         200      200           
  Lines       25462    25465    +3     
  Branches     8998     8999    +1     
=======================================
+ Hits        25462    25465    +3     
Flag Coverage Δ
angular 100.0% <ø> (ø)
universal 100.0% <100.0%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@pkg-pr-new

pkg-pr-new Bot commented Aug 4, 2026

Copy link
Copy Markdown
angular-slickgrid

npm i https://pkg.pr.new/angular-slickgrid@2694

aurelia-slickgrid

npm i https://pkg.pr.new/aurelia-slickgrid@2694

slickgrid-react

npm i https://pkg.pr.new/slickgrid-react@2694

slickgrid-vue

npm i https://pkg.pr.new/slickgrid-vue@2694

@slickgrid-universal/angular-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/angular-row-detail-plugin@2694

@slickgrid-universal/aurelia-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/aurelia-row-detail-plugin@2694

@slickgrid-universal/react-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/react-row-detail-plugin@2694

@slickgrid-universal/vue-row-detail-plugin

npm i https://pkg.pr.new/@slickgrid-universal/vue-row-detail-plugin@2694

@slickgrid-universal/binding

npm i https://pkg.pr.new/@slickgrid-universal/binding@2694

@slickgrid-universal/common

npm i https://pkg.pr.new/@slickgrid-universal/common@2694

@slickgrid-universal/composite-editor-component

npm i https://pkg.pr.new/@slickgrid-universal/composite-editor-component@2694

@slickgrid-universal/custom-footer-component

npm i https://pkg.pr.new/@slickgrid-universal/custom-footer-component@2694

@slickgrid-universal/custom-tooltip-plugin

npm i https://pkg.pr.new/@slickgrid-universal/custom-tooltip-plugin@2694

@slickgrid-universal/empty-warning-component

npm i https://pkg.pr.new/@slickgrid-universal/empty-warning-component@2694

@slickgrid-universal/event-pub-sub

npm i https://pkg.pr.new/@slickgrid-universal/event-pub-sub@2694

@slickgrid-universal/excel-export

npm i https://pkg.pr.new/@slickgrid-universal/excel-export@2694

@slickgrid-universal/graphql

npm i https://pkg.pr.new/@slickgrid-universal/graphql@2694

@slickgrid-universal/odata

npm i https://pkg.pr.new/@slickgrid-universal/odata@2694

@slickgrid-universal/pagination-component

npm i https://pkg.pr.new/@slickgrid-universal/pagination-component@2694

@slickgrid-universal/pdf-export

npm i https://pkg.pr.new/@slickgrid-universal/pdf-export@2694

@slickgrid-universal/row-detail-view-plugin

npm i https://pkg.pr.new/@slickgrid-universal/row-detail-view-plugin@2694

@slickgrid-universal/rxjs-observable

npm i https://pkg.pr.new/@slickgrid-universal/rxjs-observable@2694

@slickgrid-universal/sql

npm i https://pkg.pr.new/@slickgrid-universal/sql@2694

@slickgrid-universal/text-export

npm i https://pkg.pr.new/@slickgrid-universal/text-export@2694

@slickgrid-universal/utils

npm i https://pkg.pr.new/@slickgrid-universal/utils@2694

@slickgrid-universal/vanilla-bundle

npm i https://pkg.pr.new/@slickgrid-universal/vanilla-bundle@2694

@slickgrid-universal/vanilla-force-bundle

npm i https://pkg.pr.new/@slickgrid-universal/vanilla-force-bundle@2694

@slickgrid-universal/web-mcp

npm i https://pkg.pr.new/@slickgrid-universal/web-mcp@2694

commit: f509d1c

@ghiscoding
ghiscoding merged commit 8df454e into master Aug 5, 2026
17 checks passed
@ghiscoding
ghiscoding deleted the bugfix/aria-grid-header branch August 5, 2026 01:16
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.

1 participant