Designer view

Tree Grid

An interactive 2D data structure with hierarchical rows — the APG escalation from `Grid` for surfaces where rows have parent- child relationships and per-row expand / collapse semantics. Carries `role="treegrid"` on the root, `role="row"` with `aria-level` plus `aria-expanded` (on parent rows) per row. Inherits the grid keyboard model (roving-tabindex, 2D arrow navigation, F2 / Enter / Escape edit-mode lifecycle) and adds ArrowRight / ArrowLeft for tree-navigation: ArrowRight expands a collapsed parent or moves to first child; ArrowLeft collapses an expanded parent or moves to parent.

Also called Tree table Hierarchical grid Expandable data grid

When to use

Use

For tabular data where rows have parent-child relationships AND multiple attribute columns matter — file managers showing name + size + modified-date with folder-nesting, org charts with role + manager + headcount per row, project breakdown structures with task + assignee + status per node, nested budgets with category + amount per row. Use the static variant when cells are interactive but not editable; the editable variant when cells transition to input-mode for in-place editing.

Avoid

For flat tabular data without hierarchy — that is `Grid` (interactive cells without parent-child) or `Table` (read- only data display). For hierarchical single-column data without tabular metadata — that is the APG Tree pattern (separate canonical entry, future). For non-tabular hierarchical layouts (sidebar trees, file-only trees) — use a tree pattern, not a tree grid; the column overhead is pure cost without benefit. For deeply-nested hierarchies where users primarily drill down (5+ levels) — consider breadcrumb-driven navigation between flat lists rather than expanding a single deep tree.

Versus related

  • grid-pattern

    `Grid` is the flat 2D structure with cells in rows and columns; `TreeGrid` adds hierarchical row semantics — `aria-level` per row, `aria-expanded` on parent rows, ArrowRight / ArrowLeft for tree-navigation. Use Grid when rows are siblings without parent-child relationships; use TreeGrid when rows nest. The keyboard models share the roving-tabindex and 2D arrow-navigation foundation; TreeGrid extends with the expand-collapse semantics on top.

  • table

    `Table` is read-only flat data display with linear focus through interactive descendants; `TreeGrid` is interactive 2D structure with hierarchical rows and per-row expand-collapse. Use Table when the data is flat and read-only; use TreeGrid when the data is hierarchical AND requires interactive cells (selection, edit, focus-cell-driven drilldown). The escalation cost is significant — TreeGrid's keyboard model (roving-tabindex + arrow-navigation + tree-expand) is the steepest authoring overhead in the canonical canon; reach for it only when the affordances justify the cost.

Tree Grid is the APG-canonical pattern for interactive hierarchical tabular data — file managers with column metadata, org charts, nested budgets, project breakdown structures with cost columns. Distinct from `Grid` (flat 2D cells, no hierarchy) and from a single-column `Tree` (hierarchical rows without tabular columns). The reference documents the `aria-level` / `aria-expanded` / `aria-setsize` / `aria-posinset` contract that makes the hierarchy programmatic, the ArrowRight / ArrowLeft tree-navigation that extends the grid keyboard model, and the visual-indent-driven-by-aria-level pattern that keeps the design surface and the AT semantic in sync.

Highlight
Fig 1.1 · Tree Grid · Designer view
Designer

Figma anatomy

Slot Figma type Hint
root frame TreeGrid frame; variant property switches between static and editable shapes
caption text Caption text style; positioned above the treegrid; visibility per "Visible Caption" property
column-headers-row frame Header row container; sticky-position variant per "Sticky Header" property
column-header instance Column header cell instance with sort-affordance variants
row instance Row instance with level / expanded / collapsed / focused / selected variants
row-expander instance Expander icon instance; chevron-right (collapsed) / chevron-down (expanded) variants
row-indent frame Decorative indent spacer; width = aria-level × indent-step
cell instance Data cell instance with default / focused / selected / editing / readonly variants
row-header instance Row header cell instance; hosts row-expander + row-indent for parent rows
cell-editor instance Cell editor instance — input / select / textarea variants per cell-data-type
selection-overlay frame Decorative overlay rectangle; absolute-positioned over selected cell range
Designer

Token usage per slot

caption
color
  • foregroundcolor.text.primary
typography
  • sizetext.md
  • weightweight.semibold
column-header
color
  • foregroundcolor.text.primary
typography
  • sizetext.sm
  • weightweight.semibold
row-expander
color
  • foregroundcolor.text.muted
cell
typography
  • sizetext.sm
Both

Figma ↔ Code property map

FigmaKindCodeNotes
VariantEnumvariantMaps static / editable. Editable activates F2/Enter/Escape edit-mode lifecycle and cell-editor slot mounting.
SizeEnumsizesm / md.
Multi SelectBooleanmultiSelectWhen true, supports multi-cell selection via Shift+Arrow / Ctrl+Click; root carries `aria-multiselectable="true"`.
VirtualizedBooleanvirtualizedWhen true, only viewport-visible rows render; requires `aria-rowcount` on root and `aria-rowindex` per rendered row, recomputed as expansion state changes.
Sticky HeaderBooleanstickyHeaderWhen true, column-headers-row uses `position: sticky` so headers stay visible during vertical scroll.
SortableBooleansortablePer-column sort affordance plus `aria-sort` on column headers.
Expanded KeysTextexpandedKeysSet of row IDs currently expanded. Pairs with expandChange event for two-way binding. JSON serialization for URL persistence.
Lazy Load ChildrenBooleanlazyLoadChildrenWhen true, child rows are fetched on parent expansion via async callback; aria-busy="true" on the parent row during fetch.
Indent StepNumberindentStepPer-level indent in pixels. Drives row-indent slot via CSS `padding-inline-start: calc(var(--level) * indentStep)`. Default 24.
Row CountNumberrowCountTotal currently-visible row count (for virtualized treegrids); reflects to `aria-rowcount`. Recomputes as expansion state changes.
Designer

Motion

TransitionDuration token
expandCollapsemotion.duration.fast
cellFocusTransitionmotion.duration.fast
editModeEntermotion.duration.fast
Easing
motion.easing.standard
Reduced motion
Instant (jump cut)
Designer

Responsive behaviour

BreakpointChange
breakpoint.smBelow this width, the treegrid keyboard model is hard to use on touch (no arrow keys, no F2 to enter edit-mode, no ArrowRight / ArrowLeft to expand / collapse). The canonical pattern is to escalate to a single-column Tree or to a stacked-card per-row layout on narrow viewports. Editable treegrids on mobile route through tap-to-edit modal flows rather than in-place edit-mode.
breakpoint.mdAt and above this width, the full treegrid keyboard model is active — roving-tabindex, 2D arrow-navigation, ArrowRight / ArrowLeft tree-navigation, F2 / Enter / Escape edit-mode lifecycle. Sticky-header and sticky-first-column activate when the treegrid exceeds the viewport.
Both

Internationalisation

RTL · mirroring

Column flow follows logical direction — same as Grid. Row-indent uses `padding-inline-start` so the indent flips under RTL: visually right-side under Arabic / Hebrew. Row-expander chevron icons (chevron-right when collapsed, chevron-down when expanded) flip direction under RTL — visually collapsed-state chevron points left in RTL. ArrowRight / ArrowLeft tree-navigation maps to visual direction per APG recommendation; under RTL, ArrowRight collapses (visually moving toward parent at the right) and ArrowLeft expands.

Text expansion

Column headers and row content expand 30-50% under translation (file names and folder names typically don't translate but column headers do — "Name" → "Nome", "Modified" → "Geändert" 100% longer). Long row-content forces wider columns and may push the treegrid past viewport width — the scrollable-region wrapper absorbs this. Cell-editor inputs need flexible sizing for translated values. Indent steps stay constant across locales.

Both

Variants, properties, states

Variants

Structurally different versions of the component.

static editable

Properties

The same component, parameterised.

PropertyType
size sm | md
multiSelect boolean
virtualized boolean
stickyHeader boolean
sortable boolean

States

Browser/user-driven (interactive) vs. app-driven (data).

KindStates
interactive
hoverfocus-visibleactive
data
idleeditinghasSelectionhasSortinghasExpansionloading
Both

State transitions

FromToTrigger
idlehasExpansionUser activates a row-expander or presses ArrowRight on a collapsed parent row
hasExpansionidleUser collapses every previously-expanded row (ArrowLeft on expanded parent)
idleeditingUser presses F2 or Enter on a focused editable cell (editable variant only)
editingidleUser presses Enter (commit) or Escape (cancel) inside the cell-editor
idlehasSelectionUser activates selection via Space, Shift+Arrow, or Ctrl+Click
idlehasSortingUser activates a sortable columnheader via Enter / Space
idleloadingAsync fetch begins (lazy-load child rows on parent expansion, server-side sort, or filter)
loadingidleAsync fetch resolves with new data
Both

Figma↔Code mismatches

  1. 01
    Figma

    Indent drawn as fixed-pixel inline padding per row variant

    Code

    CSS `padding-inline-start: calc(var(--level) * indentStep)` driven by `aria-level`

    Consequence

    Designers compose treegrid rows with explicit indent values per nesting level (level-1: 0px, level-2: 24px, level-3: 48px); developers must compute the indent from `aria-level` so the visual surface and the AT semantic stay in sync. Without explicit anatomy, implementations may ship visual-only indents (sighted users see hierarchy, AT users hear flat row-list) or AT-only `aria-level` (sighted users see no hierarchy, AT users hear levels).

    Correct

    Document the row-indent slot as decorative AND explicitly derived from `aria-level`. Figma carries row variants per level with the indent pre-computed; the design surface signals the `aria-level` value as the source-of-truth. Code wires CSS `padding-inline-start: calc(var(--level) * 24px)` where `--level` reflects `aria-level`, OR uses an `aria-hidden` spacer with width computed from level. Visual and AT stay in sync by construction.

  2. 02
    Figma

    Expander drawn as decorative chevron with no interaction model

    Code

    Activation via row-click + ArrowRight/Left + (optionally) `<button>` wrapper around chevron

    Consequence

    Designers ship the expander chevron as a static icon with no documented interaction; developers must wire activation via row-click, keyboard ArrowRight / ArrowLeft, AND optionally a button wrapper for icon-click activation. Without explicit anatomy, the activation model is ambiguous and implementations may ship one pattern (mouse-click chevron only) without the keyboard model (ArrowRight / ArrowLeft on parent row), breaking keyboard accessibility.

    Correct

    Document the row-expander as decorative `aria-hidden="true"` AND document the activation contract as a row-level keyboard handler (ArrowRight / ArrowLeft) plus optional `<button>` wrapper for sighted-mouse users. Figma carries the chevron in two states (collapsed / expanded) plus annotation indicating the keyboard activation. Code wires the row-level keyboard handler; the icon stays decorative because `aria-expanded` on the row is the canonical AT signal.

  3. 03
    Figma

    Hierarchy drawn as visual nesting only

    Code

    `aria-level` plus `aria-expanded` programmatic semantics on each row

    Consequence

    Designers compose treegrid hierarchy as visual indent + chevron-direction; developers must add `aria-level` per row and `aria-expanded` on parent rows for the AT semantic. Without explicit anatomy, the hierarchy may ship as visual-only — sighted users see nesting, AT users hear a flat row-list. The most common treegrid implementation failure.

    Correct

    Document `aria-level` and `aria-expanded` as first-class row attributes in the row slot's a11y hint. Figma carries the level as a row property (Level 1 / 2 / 3 variants) and the expansion state as a separate property (Collapsed / Expanded). Code wires `aria-level="${depth}"` plus `aria-expanded="${isExpanded}"` (omitted on leaf rows); the visual indent and chevron read from the same source.

  4. 04
    Figma

    Expanded / collapsed states drawn as separate row arrangements

    Code

    Single row with state-driven children visibility — children DOM-mounted only when expanded

    Consequence

    Designers compose collapsed treegrid as one arrangement (parent rows visible) and expanded treegrid as another (parent + child rows visible); developers ship a single composition where child rows mount / unmount per parent `aria-expanded` state. The two surfaces diverge structurally — designer's separate arrangements lose the per-row state bookkeeping, developer's single composition preserves it. Without explicit anatomy, the state-driven mounting may ship as separate virtual-DOM trees (expansion swaps the whole tree, breaking focus + selection state).

    Correct

    Document expansion as a per-row state, not a tree-wide variant. Figma carries the row in collapsed / expanded states; child rows are conditionally-visible siblings of the parent row, not a separate composition. Code mounts child rows under parent rows in DOM order; `aria-expanded="false"` plus `display: none` hides them visually while keeping the DOM structure stable for focus + selection.

  5. 05
    Figma

    Drag-and-drop reorder drawn as separate user flow

    Code

    In-grid keyboard model + drag-handle slot for mouse / touch reordering

    Consequence

    Designers compose tree reordering as a separate "edit hierarchy" mode with explicit reorder controls; developers may ship in-grid drag-and- drop OR keyboard-driven reorder (Alt+ArrowUp / Alt+ArrowDown to move row up / down within siblings). The two surfaces diverge — designer's separate-mode keeps reordering rare, developer's in-grid pattern makes it ambient. Without explicit anatomy, implementations may ship mouse-only drag without keyboard equivalent (WCAG 2.1.1 Keyboard violation for reordering-as-affordance).

    Correct

    Document reordering as out-of-canonical-scope OR document the keyboard model explicitly (Alt+ArrowUp / Alt+ArrowDown for sibling- reorder, Alt+ArrowRight / Alt+ArrowLeft for level-change). Reordering is a heavyweight affordance — most production treegrids do not ship it; when they do, the keyboard model must match the mouse drag model for WCAG 2.1.1 compliance.

Both

Contracts

Non-negotiable contracts

  1. APGAPG: TreeGrid pattern aria-level requirement

    Every row carries `aria-level` (1-based depth, 1 is a top-level row). The level drives both the visual indent and the AT announcement. Implementations that ship visual indent without `aria-level` violate WCAG 1.3.1 — sighted users see hierarchy, AT users hear flat row-list.

    Without `aria-level`, the entire hierarchical semantic is invisible to AT. SR users hear "row 1, row 2, row 3" with no indication of which rows are children of which parents. The treegrid degrades to a flat grid with misleading `role="treegrid"`.

  2. APGAPG: TreeGrid pattern aria-expanded requirement

    Parent rows (rows with descendants) carry `aria-expanded` reflecting the current expansion state — `true` when children are visible, `false` when collapsed. Leaf rows OMIT `aria-expanded` (the absence is the canonical signal that the row has no descendants).

    Without `aria-expanded` on parent rows, AT users cannot tell which rows are expandable or whether a row is currently expanded. ArrowRight / ArrowLeft navigation has no programmatic state to observe. The expansion-toggle gesture becomes invisible to AT.

  3. APGAPG: TreeGrid pattern keyboard interaction

    ArrowRight / ArrowLeft implement tree- navigation on top of the grid keyboard model — ArrowRight expands collapsed parents OR moves to first child of expanded parents; ArrowLeft collapses expanded parents OR moves to parent of leaf rows.

    Without the tree-keyboard handlers, keyboard users cannot expand / collapse rows via keyboard — they must mouse- click the chevron, breaking keyboard accessibility. The defining tree- navigation affordance is gone.

  4. APGAPG: TreeGrid extends Grid pattern

    Inherits Grid's contracts: roving- tabindex (one cell at a time carries `tabindex="0"`), 2D arrow-key navigation, F2 / Enter / Escape edit- mode lifecycle, role-hierarchy consistency (cells inside `role="treegrid"` carry `role="gridcell"` / `role="columnheader"` / `role="rowheader"`), virtualized-aware ARIA attributes (`aria-rowcount` / `aria-rowindex`).

    Without inheriting Grid's contracts, the treegrid loses the foundation keyboard model that the tree-navigation extends. The full APG-canonical keyboard model is the union of Grid's plus the tree-extension; partial implementations fail one or both.

  5. Canon

    Visual indent per row derives from `aria-level` (single source-of-truth) — `padding-inline-start: calc(var(--level) * indentStep)` or equivalent. Visual and AT cannot diverge; both read from the same value.

    Without the single-source-of-truth rule, implementations may compute indent from a separate state value (e.g., the row's array-position) and the visual hierarchy diverges from the `aria-level` AT semantic. The canonical discipline keeps them in sync by construction — change the level, the indent updates; AT users and sighted users see the same structure.

Vocabulary drift

WAI-ARIA
role=treegrid + aria-level + aria-expanded
Canonical ARIA pattern. WAI-ARIA Authoring Practices Guide documents the full keyboard model — inherits Grid's foundation plus the ArrowRight / ArrowLeft tree-navigation. The canonical anatomy mirrors APG directly.
HTML
<table role="treegrid">
Native HTML `<table>` plus explicit `role="treegrid"` is the canonical hybrid. `<div role="treegrid">` is the fallback for non-tabular layouts (rare for treegrid because tabular columns are the differentiator from Tree).
AG Grid
AgGridReact with treeData
AG Grid Tree Data is the dominant production treegrid library. Implements the full APG keyboard model, lazy- loading children on expansion, virtualization, edit-mode lifecycle. `getDataPath(row) => string[]` is the canonical tree-shape API; AG Grid derives `aria-level` from path depth.
React Aria
Table with expandedKeys (tree mode)
React Aria's `Table` ships tree-mode opt-in via `expandedKeys` + `onExpandedChange`. Headless — consumers compose the DOM and visual treatment; React Aria wires `aria-level` / `aria-expanded` plus the keyboard model. The single-component-with-mode- prop reflects the canonical "treegrid extends grid" relationship.
PrimeVue
TreeTable
PrimeVue ships `TreeTable` as a first- class Vue treegrid primitive with `nodes` (tree-shaped data), `expandedKeys` v-model, `selectionKeys` v-model. Tabular columns are first- class. The naming-only divergence ("TreeTable" vs canonical "TreeGrid") does not affect the API surface.
Material 3
— (no formal TreeGrid pattern)
Material 3 spec does not include the TreeGrid pattern. Material UI ships `<TreeView>` (single-column tree, not treegrid). For canonical APG-compliant treegrid, Material projects escalate to AG Grid.
Carbon
— (no formal TreeGrid pattern)
Carbon ships `DataTable` (flat tabular data, Table pattern) and `TreeView` (single-column tree). For canonical treegrid, Carbon projects escalate to AG Grid or build custom on top of DataTable + manual `aria-level` / `aria-expanded` wiring.
Atlassian
— (no formal TreeGrid pattern)
Atlassian ships `DynamicTable` (Table pattern, flat). No first-class treegrid primitive in @atlaskit. For treegrid use cases, Atlassian projects escalate to AG Grid or build custom.
Designer

Common mistakes

Blocker

#treegrid-no-aria-level

Rows lack `aria-level` (hierarchy invisible to AT)

Problem

Rows render with visual indent but no `aria-level` attribute. AT users hear a flat list of rows with no hierarchy semantic — "row 1, row 2, row 3" — when the visual surface shows "Folder A > File 1 > Subfile 1". The hierarchy is invisible to AT entirely. The most common treegrid implementation failure.

Fix

Set `aria-level="${depth}"` on every row (1-based, 1 is a top-level row). The level drives both the visual indent (via CSS `padding-inline-start: calc(var(--level) * indentStep)`) and the AT announcement ("row, level 2, ${cellValues}"). The single source- of-truth keeps visual and AT in sync.

Blocker

#treegrid-no-aria-expanded

Parent rows lack `aria-expanded` (expansion state invisible to AT)

Problem

Parent rows render with chevron-right / chevron-down icons but no `aria-expanded` attribute. AT users hear no expansion state and cannot tell which parent rows have visible children. ArrowRight / ArrowLeft navigation has no programmatic state to observe — the expansion-toggle gesture has no AT effect. APG explicit rule violation.

Fix

Set `aria-expanded="true"` on parent rows whose children are visible; set `aria-expanded="false"` on collapsed parent rows; OMIT `aria-expanded` on leaf rows (the absence is the canonical signal that the row has no descendants). The visual chevron icon and the `aria-expanded` attribute share the same source-of-truth.

Major

#treegrid-arrow-not-tree-keyboard-model

ArrowRight / ArrowLeft do not expand / collapse parent rows

Problem

The treegrid implements the grid keyboard model (cell-by-cell navigation) but ArrowRight / ArrowLeft do not extend it with the tree-navigation semantic — on a collapsed parent, ArrowRight should expand it; on an expanded parent, ArrowRight should move to the first child; ArrowLeft should collapse expanded parents or move to the parent of leaf rows. Keyboard users cannot expand / collapse via keyboard — they must mouse-click the chevron, breaking keyboard accessibility.

Fix

Implement the tree-keyboard handlers on top of the grid keyboard model: ArrowRight on a collapsed parent expands; ArrowRight on an expanded parent moves to the first child; ArrowLeft on an expanded parent collapses; ArrowLeft on a child row (or leaf) moves focus to the parent row. Asterisk (*) expands all sibling parent rows. The handlers shift the roving-tabindex along with the focus; the row's `aria-expanded` flips on toggle.

Major

#treegrid-cell-not-gridcell-role

Cells use `<td>` without `role="gridcell"` when root is `role="treegrid"`

Problem

The root carries `role="treegrid"` but cells are bare `<td>` without `role="gridcell"`. AT announces the root as a tree grid but cells as plain table cells — role hierarchy broken. SR users hear inconsistent semantics on entry vs cell navigation. Inherits the grid-cell-not-gridcell-role mistake from `Grid`.

Fix

When the root is `role="treegrid"`, the cells inside MUST carry `role="gridcell"`, `role="columnheader"`, or `role="rowheader"`. For native `<th>` and `<td>` elements, add the explicit role — implicit table semantics do not propagate through the explicit `role="treegrid"` override.

Major

#treegrid-no-aria-rowcount-virtualized

Virtualized treegrid omits `aria-rowcount` and `aria-rowindex`

Problem

The treegrid renders only viewport-visible rows plus a buffer; without `aria-rowcount` on the root and `aria-rowindex` per row, AT reports the rendered count as the total. For treegrids the situation is worse than for flat grids because the rendered count depends on which parent rows are expanded — AT users perceive different row-counts at different expansion states with no indication of the underlying truth.

Fix

Set `aria-rowcount="${trueTotal}"` on the root, where `trueTotal` is the count of rows visible at the current expansion state (NOT the count of all rows including collapsed children). `aria-rowindex` per row is the 1-based index within the currently-visible set. Update both as expansion state changes.

Minor

#treegrid-when-tree-suffices

TreeGrid pattern used when single-column Tree would work

Problem

The component ships with `role="treegrid"` and full tabular columns but the cells have no useful column data — only the row-name column carries content; other columns are empty or carry redundant metadata. The tabular complexity buys nothing; a single- column Tree pattern would suffice.

Fix

Use the APG Tree pattern (single-column hierarchical structure, separate canonical pattern) when only one column carries meaningful data. Reserve TreeGrid for surfaces where multiple columns matter (file managers with name + size + date columns, org charts with role + manager + headcount columns). The escalation cost should match the affordance value.

Accessibility hints
Slot Accessibility hint
root `<table role="treegrid" aria-rowcount="..." aria-colcount="..." aria-multiselectable="...">`. Native `<table>` plus `role="treegrid"` is the canonical hybrid — retains structural HTML semantics while signaling the interactive hierarchical keyboard model. AT users hear "tree grid, N rows, M columns" on entry.
caption `<caption>` inside `<table role="treegrid">` is canonical. SR announces the caption on treegrid entry. Translate per locale.
column-headers-row `<tr role="row">` containing `<th role="columnheader" scope="col">` children. Columnheaders are focusable in the treegrid keyboard model — arrow keys navigate to them like any other cell.
column-header `<th role="columnheader" scope="col" tabindex="-1">`. `aria-sort="ascending"` / `"descending"` / `"none"` when sortable. Same roving-tabindex bookkeeping as Grid.
row `<tr role="row" aria-level="1" aria-expanded="true" aria-setsize="5" aria-posinset="2">` (parent row at depth 1, second of five siblings, currently expanded). For leaf rows omit `aria-expanded` entirely (the absence is the canonical signal that the row has no children). For deep nesting the level-driven indent is the visual surface; the AT semantic comes from `aria-level`. SR announces "row, level 1, expanded, 2 of 5, ${cellValues}".
row-expander `aria-hidden="true"` is the canonical default — the expansion semantic lives on the parent row's `aria-expanded`, not on the icon. SR users hear "expanded" / "collapsed" via the row-level `aria-expanded`, not via the icon's accessible name. Implementations that wrap the icon in `<button>` for mouse-click activation should still mark the inner icon `aria-hidden="true"` and rely on the row- level `aria-expanded` for AT announcement.
row-indent Render via CSS or as `aria-hidden` spacer. SR users do not hear the indent; they hear the `aria-level` value ("level 3"). Implementations that compute indent from anything other than `aria-level` risk visual-vs-AT divergence — always derive indent from the same value AT reads.
cell `<td role="gridcell" tabindex="-1">` for non-active cells; the active cell has `tabindex="0"`. Same roving-tabindex bookkeeping as Grid. `aria-colindex` for virtualized treegrids. Cells in the leading column of parent rows wrap the row-expander + row-indent + cell value; cells in non-leading columns are standard data content.
row-header `<th role="rowheader" scope="row" tabindex="-1">`. SR announces the row-header on cell-by-cell navigation, providing row context the same way columnheader provides column context. For treegrids, the row-header is conventionally the column that carries the row's hierarchical identity (file name, task name, account category).
cell-editor Native `<input>` / `<select>` / `<textarea>` carries the right keyboard model and AT semantics. On edit-mode entry, focus moves to the editor; on Enter / Tab commit, focus returns to the cell; on Escape, the value reverts and focus returns to the cell. Same edit-lifecycle as Grid.
selection-overlay `aria-hidden="true"`. The overlay is visual scaffolding; AT semantic lives on `aria-selected` per cell and per row.