fix(a11y): add missing ARIA row/rowgroup structure to grid headers - #2680
Closed
hjaewon wants to merge 1 commit into
Closed
fix(a11y): add missing ARIA row/rowgroup structure to grid headers#2680hjaewon wants to merge 1 commit into
hjaewon wants to merge 1 commit into
Conversation
The grid container gets `role="grid"` and header cells get `role="columnheader"`, but the container levels in between (`.slick-header-container > .slick-header > .slick-header-columns`) carry no role. Elements without a role and without focusability are transparent in the accessibility tree, so axe reports two violations on every grid: - `aria-required-parent` (one per column): `role="columnheader"` has no `role="row"` parent - `aria-required-children`: `role="grid"` is computed to directly own `[role=columnheader]` and `div[tabindex]` (the two 0x0 focus sinks), neither allowed as a child of `grid` Nothing is visually broken; the impact is that screen readers do not get the table structure. Roles added at element-creation time: - `.slick-header` (header scroller, L/R) -> rowgroup - `.slick-header-columns` (L/R) -> row - `.slick-headerrow` (filter row scroller, L/R) -> rowgroup - `.slick-headerrow-columns` (L/R) -> row - `.slick-headerrow-column` (filter row cell) -> gridcell - focus sinks (0x0, tabindex=-1) -> aria-hidden="true" The filter row is fixed as a unit on purpose: giving `.slick-headerrow-columns` a `row` role without giving its cells a `gridcell` role would introduce a *new* aria-required-children violation on grids that show the filter row. `aria-colcount` / `aria-rowcount` are already handled by updateRowCount(), so untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2680 +/- ##
=======================================
Coverage 100.0% 100.0%
=======================================
Files 200 200
Lines 25469 25470 +1
Branches 9007 9007
=======================================
+ Hits 25469 25470 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
angular-slickgrid
aurelia-slickgrid
slickgrid-react
slickgrid-vue
@slickgrid-universal/angular-row-detail-plugin
@slickgrid-universal/aurelia-row-detail-plugin
@slickgrid-universal/react-row-detail-plugin
@slickgrid-universal/vue-row-detail-plugin
@slickgrid-universal/binding
@slickgrid-universal/common
@slickgrid-universal/composite-editor-component
@slickgrid-universal/custom-footer-component
@slickgrid-universal/custom-tooltip-plugin
@slickgrid-universal/empty-warning-component
@slickgrid-universal/event-pub-sub
@slickgrid-universal/excel-export
@slickgrid-universal/graphql
@slickgrid-universal/odata
@slickgrid-universal/pagination-component
@slickgrid-universal/pdf-export
@slickgrid-universal/row-detail-view-plugin
@slickgrid-universal/rxjs-observable
@slickgrid-universal/sql
@slickgrid-universal/text-export
@slickgrid-universal/utils
@slickgrid-universal/vanilla-bundle
@slickgrid-universal/vanilla-force-bundle
@slickgrid-universal/web-mcp
commit: |
Author
|
Closing this — submitted prematurely on my side, not ready for review. Sorry for the noise. |
Owner
|
you could have left it open but changed to "Draft" then I know it isn't ready yet... a11y fixes are more than welcome, since that is not my expertise at all, so thanks in advance... :) |
Owner
|
@hjaewon this PR seemed like a good start, are you planning on re-opening the PR? I'll be pushing a new release soon, thanks |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Describe your change
The grid container gets
role="grid"and header cells getrole="columnheader", but the three containerlevels in between (
.slick-header-container > .slick-header > .slick-header-columns) carry no role. Sinceelements without a role and without focusability are transparent in the accessibility tree, this produces two
axe violations on every grid:
aria-required-parent— one violation per column:role="columnheader"has norole="row"parent(
Required ARIA parent role not present: row).aria-required-children—role="grid"is computed to directly own[role=columnheader]anddiv[tabindex](the two 0×0 focus sinks), neither of which is an allowed child ofgrid.Nothing is visually broken — the impact is that screen readers do not get the table structure.
This adds the missing skeleton at element-creation time:
.slick-header(header scroller, L/R)rowgroup.slick-header-columns(L/R)row.slick-headerrow(filter row scroller, L/R)rowgroup.slick-headerrow-columns(L/R)row.slick-headerrow-column(filter row cell)gridcelltabindex="-1")aria-hidden="true"Two notes on scope:
.slick-headerrow-columnsarowrole without giving itscells a
gridcellrole would introduce a newaria-required-childrenviolation (arowmust own cells)on grids that show the filter row, so both are in this patch.
tabindex="-1", so they are not tabbable andaria-hiddendoes not triggeraria-hidden-focus.aria-colcount/aria-rowcountare already handled correctly (updateRowCount()), so they are untouched.How to reproduce
Run axe (e.g. a Lighthouse accessibility audit) on any example page containing a grid. Originally reported
downstream by a consumer of a fork: 15
aria-required-parentviolations (= column count) plus 1aria-required-childrenviolation, on a 15-column grid.Verification
pnpm exec tsc --build ./tsconfig.packages.json— no errors in the changed file.packages/commonunit tests: 4297 passed (181 files), which includes the 2 tests added here.No existing assertion changes: the suite asserts
classNameon those containers, not the full attribute set.oxlintclean,prettier --checkclean (long lines use the// prettier-ignoreconvention already used inthis file).
slickGrid.spec.ts: one asserting therowgroup > row > columnheaderandrowgroup > row > gridcellchains, one asserting the focus sinks arearia-hidden.The equivalent fix has also been running as a runtime patch in a downstream facade, verified against both axe
rules (ownership computed the same way axe does) across three feature combinations — plain grid, inline filter
row, and grouping + footer totals + toolbar — plus a negative control confirming the check actually catches the
violation when the roles are removed.