You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We should add a Table plugin that owns grid focus management and applies grid ARIA semantics (roles, keyboard interaction model), rather than baking a single fixed semantic model into the base table.
The motivating insight: table semantics are not one-size-fits-all — they change depending on which features are enabled. A read-only data table, an interactive grid, a treegrid, and a spreadsheet/worksheet each want different roles, different keyboard behaviors, and different focus models. That variation should be expressed as composable plugin behavior, not hardcoded in XDSBaseTable.
Why a plugin
The Table already has a clean plugin pipeline (TablePlugin in packages/core/src/Table/types.ts) with the exact seams this needs:
transformTable — set the container role (grid / treegrid) and grid-level ARIA (aria-rowcount, aria-colcount, aria-multiselectable).
transformHeaderCell / transformBodyCell — set columnheader / rowheader / gridcell roles and aria-colindex / roving tabindex.
transformBodyRow — set row role, aria-rowindex, aria-expanded / aria-level for tree rows.
transformTableContext — provide the focus-manager context (active cell, roving tabindex state).
So grid focus + semantics is a natural plugin rather than a change to the base component. It stays opt-in: tables that just want static tabular data keep the default <table> semantics and pay nothing.
Semantic variants to support
grid — 2D roving-tabindex focus. Arrow keys move between cells; Home/End, Ctrl+Home/End, PageUp/PageDown; Enter/F2 to enter cell edit/actionable mode, Escape to exit. Composes with the existing selection plugin (aria-multiselectable, Shift+Arrow range extension).
treegrid — everything grid has, plus row expand/collapse (Right/Left on a collapsed/expanded parent), aria-level, aria-expanded, aria-setsize/aria-posinset. Different keyboard model from a flat grid — this is a big part of why semantics can't be fixed.
worksheet / spreadsheet — likely needs a custom model: cell-range selection, type-to-edit, formula-bar-style entry, copy/paste of ranges. May not map cleanly to grid and could warrant its own plugin/variant. Flagging as its own track rather than assuming grid covers it.
Prior art in the repo
packages/core/src/hooks/useGridFocus.ts already implements roving-focus keyboard navigation, but it is currently Calendar-scoped (used by XDSCalendar for date-grid nav) and selector-based rather than table-aware. Worth evaluating whether the plugin builds on / generalizes this hook or introduces a table-native focus manager, so we don't fork the keyboard logic.
Existing plugins (selection, sortable, columnResize, columnSettings, filtering, pagination) are the pattern to mirror for structure, tests, and docs.
Scope of this issue
This is a scoping/RFC issue — settle the design before building:
Decide the plugin surface: one useXDSTableGridFocus plugin with a semantics: 'grid' | 'treegrid' option, vs. separate plugins.
Decide worksheet/spreadsheet: does it fit this plugin or is it a separate custom track?
Decide whether to generalize useGridFocus or add a table-native focus manager.
Summary
We should add a Table plugin that owns grid focus management and applies grid ARIA semantics (roles, keyboard interaction model), rather than baking a single fixed semantic model into the base table.
The motivating insight: table semantics are not one-size-fits-all — they change depending on which features are enabled. A read-only data table, an interactive
grid, atreegrid, and a spreadsheet/worksheet each want different roles, different keyboard behaviors, and different focus models. That variation should be expressed as composable plugin behavior, not hardcoded inXDSBaseTable.Why a plugin
The Table already has a clean plugin pipeline (
TablePlugininpackages/core/src/Table/types.ts) with the exact seams this needs:transformTable— set the container role (grid/treegrid) and grid-level ARIA (aria-rowcount,aria-colcount,aria-multiselectable).transformHeaderCell/transformBodyCell— setcolumnheader/rowheader/gridcellroles andaria-colindex/ rovingtabindex.transformBodyRow— setrowrole,aria-rowindex,aria-expanded/aria-levelfor tree rows.transformTableContext— provide the focus-manager context (active cell, roving tabindex state).So grid focus + semantics is a natural plugin rather than a change to the base component. It stays opt-in: tables that just want static tabular data keep the default
<table>semantics and pay nothing.Semantic variants to support
grid— 2D roving-tabindex focus. Arrow keys move between cells; Home/End, Ctrl+Home/End, PageUp/PageDown; Enter/F2 to enter cell edit/actionable mode, Escape to exit. Composes with the existingselectionplugin (aria-multiselectable, Shift+Arrow range extension).treegrid— everythinggridhas, plus row expand/collapse (Right/Left on a collapsed/expanded parent),aria-level,aria-expanded,aria-setsize/aria-posinset. Different keyboard model from a flat grid — this is a big part of why semantics can't be fixed.gridand could warrant its own plugin/variant. Flagging as its own track rather than assuminggridcovers it.Prior art in the repo
packages/core/src/hooks/useGridFocus.tsalready implements roving-focus keyboard navigation, but it is currently Calendar-scoped (used byXDSCalendarfor date-grid nav) and selector-based rather than table-aware. Worth evaluating whether the plugin builds on / generalizes this hook or introduces a table-native focus manager, so we don't fork the keyboard logic.selection,sortable,columnResize,columnSettings,filtering,pagination) are the pattern to mirror for structure, tests, and docs.Scope of this issue
This is a scoping/RFC issue — settle the design before building:
useXDSTableGridFocusplugin with asemantics: 'grid' | 'treegrid'option, vs. separate plugins.useGridFocusor add a table-native focus manager.selection(multi-select, range selection).Acceptance criteria (post-scoping)
role="grid"with a full roving-tabindex keyboard model matching the WAI-ARIA APG grid pattern.treegridmode (or sibling plugin) with expand/collapse + hierarchy semantics.selectionplugin..doc.mjs, story, and showcase per the create-component protocol.