# Astryx Table Accessibility Features for Screen Readers: A Complete Implementation Guide

> Implement Astryx Table accessibility for screen readers. Explore native HTML, ARIA, keyboard navigation, focusable containers, sort announcements, selection states, and tree navigation.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: how-to-guide
- Published: 2026-08-04

---

**Astryx's Table component implements native HTML semantics, ARIA attributes, and keyboard navigation patterns including focusable scroll containers, sort announcements, row selection states, and hierarchical tree navigation.**

Astryx is an open-source React component library developed by Meta (facebook/astryx). The `Table` component demonstrates enterprise-grade accessibility patterns that enable screen-reader users to navigate complex data grids effectively. This article examines the specific accessibility mechanisms implemented across the core table system and its plugin architecture.

## Keyboard-Focusable Scroll Wrapper

Horizontal scrolling in data tables poses a significant accessibility challenge. Astryx solves this through the `TableScrollWrapper` component in [`packages/core/src/Table/Table.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/Table.tsx).

The wrapper receives three critical attributes:

- `tabIndex={0}` — makes the container keyboard-focusable
- `role="group"` — identifies the scrollable region to assistive technologies
- `aria-label={t('@astryx.table.label')}` — provides a translatable description

Lines 55-64 of [`Table.tsx`](https://github.com/facebook/astryx/blob/main/Table.tsx) implement this pattern:

```tsx
<TableScrollWrapper
  tabIndex={0}
  role="group"
  aria-label={t('@astryx.table.label')}
>
  <table>{/* table content */}</table>
</TableScrollWrapper>

```

This allows keyboard-only users to focus the container with Tab, then navigate horizontally using arrow keys without requiring a physical scroll wheel.

## Sortable Column Headers with ARIA Sort

When table sorting is enabled, Astryx communicates sort state through the `useTableSortable` plugin located in [`packages/core/src/Table/plugins/sortable/useTableSortable.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/plugins/sortable/useTableSortable.tsx).

The implementation includes two ARIA mechanisms:

**`aria-sort` on header cells** — Applied to `<th>` elements with values `"ascending"`, `"descending"`, or omitted when unsorted. Lines 430-440 handle this assignment based on the current sort configuration.

**Descriptive `aria-label` on sort buttons** — Each clickable header receives a computed label announcing both the column name and current sort direction, enabling screen-reader users to understand the action before activation.

```tsx
// When using the sortable plugin
const columns = [
  {
    key: 'name',
    header: 'Name',
    sortable: true, // Enables aria-sort handling automatically
  },
];

```

## Native Table Semantics for Screen Readers

Astryx preserves standard HTML table semantics through `TableHeaderCell` and `TableCell` components, ensuring assistive technologies can map relationships between headers and data cells.

### TableHeaderCell ([`packages/core/src/Table/TableHeaderCell.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/TableHeaderCell.tsx), lines 35-53)

The `TableHeaderCellProps` interface exposes:

- `scope` — defines header association (`col` or `row`)
- `headers` — references related header IDs for complex tables
- `colSpan` and `rowSpan` — handles merged cells without breaking screen-reader navigation

### TableCell ([`packages/core/src/Table/TableCell.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/TableCell.tsx), lines 43-55)

`TableCellProps` mirrors these attributes for `<td>` elements, maintaining structural integrity across the table grid.

## Row Selection State Announcement

The selection plugin in [`packages/core/src/Table/plugins/selection/useTableSelectionState.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/plugins/selection/useTableSelectionState.tsx) manages selectable rows through lines 20-30.

Rows receive `aria-selected="true"` when actively selected, or the attribute is omitted for unselected rows. This pattern aligns with the WAI-ARIA `grid` and `treegrid` specifications, allowing screen readers to announce selection state during navigation.

```tsx
import { useTableSelectionState } from '@astryxdesign/core/Table';

<Table
  data={rows}
  columns={columns}
  plugins={{
    selection: useTableSelectionState(),
  }}
/>

```

## Hierarchical Tree Navigation

For nested data structures, the tree plugin ([`packages/core/src/Table/plugins/tree/useTableTreeData.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/plugins/tree/useTableTreeData.tsx), lines 200-215) adds hierarchical ARIA attributes:

- `aria-level` — 1-based depth indicator for each row
- `aria-expanded` — present on expandable nodes, announcing current expand/collapse state

These attributes enable screen-reader users to understand their position within nested data and anticipate expandable content.

## Context Menu Accessibility

`TableCell` handles a subtle but important accessibility pattern in lines 14-22 of [`packages/core/src/Table/TableCell.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/TableCell.tsx). When a custom right-click context menu is present, the component relocates padding to a full-size trigger element.

This ensures the entire cell area remains interactive for keyboard users, not just the visible text content. The menu remains accessible through standard keyboard activation without requiring mouse emulation.

## Complete Implementation Example

The following example demonstrates multiple accessibility features working together:

```tsx
import { Table } from '@astryxdesign/core';
import { useTranslator } from '@astryxdesign/core/i18n';
import { useTableSelectionState } from '@astryxdesign/core/Table/plugins/selection';
import { useTableTreeData } from '@astryxdesign/core/Table/plugins/tree';

function AccessibleTable() {
  const t = useTranslator();

  const columns = [
    {
      key: 'name',
      header: t('@astryx.table.column.name'),
      width: proportional(1),
      sortable: true,
    },
    {
      key: 'status',
      header: t('@astryx.table.column.status'),
      width: proportional(1),
    },
  ];

  return (
    <Table
      data={treeData}
      columns={columns}
      density="compact"
      plugins={{
        selection: useTableSelectionState(),
        tree: useTableTreeData(),
      }}
    />
  );
}

```

This configuration activates: focusable scrolling, sort announcements, row selection states, and hierarchical navigation—all without additional accessibility configuration.

## Summary

- **Focusable scroll wrapper** — `tabIndex`, `role="group"`, and `aria-label` enable keyboard scrolling in [`Table.tsx`](https://github.com/facebook/astryx/blob/main/Table.tsx)
- **Sort state announcement** — `aria-sort` and descriptive labels in [`useTableSortable.tsx`](https://github.com/facebook/astryx/blob/main/useTableSortable.tsx)
- **Native table semantics** — `scope`, `headers`, `colSpan`, `rowSpan` in [`TableHeaderCell.tsx`](https://github.com/facebook/astryx/blob/main/TableHeaderCell.tsx) and [`TableCell.tsx`](https://github.com/facebook/astryx/blob/main/TableCell.tsx)
- **Row selection** — `aria-selected` attribute via [`useTableSelectionState.tsx`](https://github.com/facebook/astryx/blob/main/useTableSelectionState.tsx)
- **Hierarchical data** — `aria-level` and `aria-expanded` in [`useTableTreeData.tsx`](https://github.com/facebook/astryx/blob/main/useTableTreeData.tsx)
- **Context menu reachability** — Full-cell activation area in [`TableCell.tsx`](https://github.com/facebook/astryx/blob/main/TableCell.tsx)

## Frequently Asked Questions

### Does Astryx Table require manual ARIA configuration?

No. Accessibility attributes are applied automatically when using standard props and plugins. The `sortable` column property enables `aria-sort`, selection and tree plugins inject their respective ARIA attributes, and the scroll wrapper receives proper labeling through the translation system. Developers only need to provide translated strings via the `useTranslator` hook.

### How does Astryx handle horizontal scrolling for keyboard users?

The `TableScrollWrapper` component in [`Table.tsx`](https://github.com/facebook/astryx/blob/main/Table.tsx) receives `tabIndex={0}` and `role="group"`, making the scrollable region focusable. Once focused, users can scroll horizontally using arrow keys. The wrapper also includes an `aria-label` to describe the table's purpose to screen-reader users navigating by landmark.

### What screen-reader announcements occur when sorting columns?

When a column header with `sortable: true` is activated, the sort button's `aria-label` announces both the column name and the resulting sort direction (e.g., "Name, sorted ascending"). The `<th>` element simultaneously receives `aria-sort="ascending"` or `aria-sort="descending"`, which some screen readers may announce when navigating through the table structure.

### Are hierarchical tree tables fully accessible?

Yes. The tree plugin provides `aria-level` for depth indication and `aria-expanded` for fold state on every expandable row. This allows screen-reader users to understand their position in nested data and control expansion without visual reference to indentation or icons.