Skip to content

fix(Table): drop row tree ARIA outside treegrid - #4690

Open
AKnassa wants to merge 1 commit into
facebook:mainfrom
AKnassa:rocky/issue-4681-aria-conditional-attr
Open

fix(Table): drop row tree ARIA outside treegrid#4690
AKnassa wants to merge 1 commit into
facebook:mainfrom
AKnassa:rocky/issue-4681-aria-conditional-attr

Conversation

@AKnassa

@AKnassa AKnassa commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Part of #4681 (weekly a11y scan). Clears all 10 aria-conditional-attr baseline entries (TableTree x7, TableGroupedRows x3), plus the identical violation in the never-baselined TableTree::Row Click Expansion story.

What this does

The tree and groupedRows Table plugins set aria-expanded (and the tree plugin aria-level) on <tr> elements. Those attributes are only valid on rows inside a treegrid, and Astryx Table is a native <table>, so axe flags every story. Rows now carry no tree ARIA; the expand/collapse state stays announced by the chevron buttons, which already carry aria-expanded (the same pattern the rowExpansion plugin uses, which has zero baseline entries).

Why not role="treegrid" instead

Treegrid contracts the full ARIA grid keyboard model (roving tabindex, arrow-key cell navigation, focusable cells), none of which BaseTable implements. Screen readers would switch users into grid interaction mode and hand them an unnavigable widget. Building that model is a feature decision, not this fix. For groupedRows it is also semantically wrong: group headers are section separators of flat data, not tree parents.

Trade-off, stated plainly

Tree depth is now conveyed visually by indentation only. The removed aria-level was invalid in this context (and unreliably announced because of it), but assistive technology currently gets no programmatic depth cue. A follow-up candidate is announcing the level through the expander's accessible name; the changeset records the same trade-off so it lands as a decision, not a side effect.

What changed

  • Rows lose aria-expanded (both plugins) and aria-level (tree plugin).
  • The tree plugin's per-row ref/subscription machinery existed solely to apply row ARIA and is removed with it (net -77 lines); row-click expansion is unaffected and a dedicated regression review of the removal found no other dependents.
  • Tests rewritten to assert rows never carry these attributes; the chevron buttons remain the single home of the expand state.
  • doc.mjs updated to describe the actual semantics, including the visual-only depth limitation.
  • Baseline: 10 entries removed, deletions only. Changeset (patch) includes a tr[aria-expanded] / tr[aria-level] test-migration hint.

Observation for a follow-up

Local audits report TableGroupedRows::Default::color-contrast and TableGroupedRows::Initially Collapsed::color-contrast as no longer occurring (stale baseline entries). Left untouched here; contrast entries are out of this PR's scope.

Verification

474/474 tests across all 22 Table test files, core typecheck, eslint clean, and the repo's axe audit with --fail-on-new: 0 violations across all 12 TableTree/TableGroupedRows stories.

axe aria-conditional-attr (serious): aria-expanded and aria-level are
valid on rows only inside a treegrid, and Astryx Table is a native
table. The tree and groupedRows plugins set them on every <tr>,
firing on all 10 baselined TableTree/TableGroupedRows stories plus
the never-baselined Row Click Expansion story.

Rows now carry no tree ARIA; expansion state stays announced by the
chevron buttons (rowExpansion-plugin pattern). The per-row
ref/subscription machinery existed solely to apply row ARIA and is
removed with it (-77 lines). Trade-off recorded in the changeset:
depth is currently visual-only via indentation; a programmatic depth
cue for AT is a tracked follow-up. Baseline: -10 entries.

Verified: 474/474 Table tests, core typecheck, eslint clean, axe
audit 0 violations across all 12 stories with --fail-on-new.

Part of facebook#4681.
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
astryx Ready Ready Preview Aug 3, 2026 9:43pm

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Aug 3, 2026
@AKnassa
AKnassa marked this pull request as ready for review August 4, 2026 01:48
@github-actions github-actions Bot added community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

📚 Storybook Preview

View Storybook for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

🧪 Sandbox Preview

View Sandbox for this PR
GitHub Pages may take up to a minute to hydrate after deploy.

Modified Components

Table (@astryxdesign/core) · View in Storybook
Metric Before After Delta
Bundle Size (ESM) N/A N/A N/A
Lines of Code N/A 1938 -
Complexity N/A Very High (110) -

Bundle Size Summary

Package Size (ESM) Size (CJS) Gzipped
@astryxdesign/core N/A 4.9KB 1.3KB

Accessibility Audit

Status: No accessibility violations detected.


Generated by PR Enrichment workflow | Storybook | Sandbox | View full report

github-actions Bot added a commit that referenced this pull request Aug 4, 2026

describe('useTableTreeData — row ARIA', () => {
it('sets 1-based aria-level on every body row', () => {
it('never sets aria-level on rows — the indent conveys hierarchy (axe aria-conditional-attr: rows outside a treegrid)', () => {

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.

Hmm well this seems wrong. If we include tree data the table should probably be a treegrid.

@cixzhang cixzhang 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.

I think the fix might be to apply the treegrid row when including tree data. We will likely need to make a version of useTreeGridFocus to achieve the right behaviors. It's quite a bit though. Let me know if you wanna do it!

@AKnassa

AKnassa commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Yes, happy to take it on.

Agreed treegrid is the right semantics. The row ARIA removed here was already treegrid shaped (1-based aria-level, aria-expanded on parents only), the container was the invalid part. Rough plan:

  • role="treegrid" via transformTable, applied only when expandable rows exist so flat data keeps the plain table.
  • Restore aria-level/aria-expanded on rows and add aria-posinset/aria-setsize, computed from the flattened visible list.
  • New useTreeGridFocus: roving tabindex with row focus, Up/Down across visible rows, Right expands, Left collapses or jumps to parent, Home/End, Enter toggles where row-click expansion is on, focus preserved across expand/collapse re-renders. Role and keyboard ship together, a treegrid without arrow support would regress below the plain table.

Two questions before I start:

  1. Do you have a reference useTreeGridFocus I should mirror? Related: is row focus enough for v1, or do you want cell navigation too? Either way the table becomes one tab stop, so in-cell widgets (sort headers, chevrons, selection checkboxes) stop being individually tabbable.
  2. groupedRows is not tree data, and its change here just leaves the expanded state on the group toggle button. OK to split it into its own PR and keep this branch for the treegrid work?

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

Labels

CLA Signed This label is managed by the Meta Open Source bot. community Authored by a community contributor (not on the eng/design team) needs:code-review High-risk change (new package/component/API) — needs human code review before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants