@ -8,11 +8,11 @@ Table is for **flat tabular data**. Emits `role="table"` with grid-cell semantic
soma ships three WAI-ARIA patterns for tabular / list display. They are **not interchangeable** — pick by semantic intent:
| Component | ARIA role | Keyboard contract | Use when |
| -------------------------------------------- | -- ---------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| **Table** (this) | `table` | Tab + click. No arrow-key row nav. | Flat tabular data. Sort/filter/paginate large lists. Row expansion is a **detail panel** (disclosure), not hierarchy. |
| [`TreeGrid` ](../tree-grid/README.md ) | `treegrid` | Roving tabindex + ArrowUp/Down rows + ArrowRight/Left expand/collapse. | **Hierarchical** rows (parent + children of the same shape): file browsers, org charts, nested tasks. `aria-level` auto-derived. |
| [`GridList` ](../grid-list/README.md ) | `grid` | Roving tabindex + ArrowUp/Down rows + ArrowLeft/Right between focusable cells. | Flat **selectable** lists where rows contain multiple interactive elements (actions, links, per-row checkboxes). |
| Component | ARIA role | Keyboard contract | Use when |
| ------------------------------------ | ---------- | ------------------------------------------------------------------------------ | ----------------------------------------------- --------------------------------------------------------------------------------- |
| **Table** (this) | `table` | Tab + click. No arrow-key row nav. | Flat tabular data. Sort/filter/paginate large lists. Row expansion is a **detail panel** (disclosure), not hierarchy. |
| [`TreeGrid` ](../tree-grid/README.md ) | `treegrid` | Roving tabindex + ArrowUp/Down rows + ArrowRight/Left expand/collapse. | **Hierarchical** rows (parent + children of the same shape): file browsers, org charts, nested tasks. `aria-level` auto-derived. |
| [`GridList` ](../grid-list/README.md ) | `grid` | Roving tabindex + ArrowUp/Down rows + ArrowLeft/Right between focusable cells. | Flat **selectable** lists where rows contain multiple interactive elements (actions, links, per-row checkboxes). |
**"Expandable rows" in Table = row detail panel**, not sub-rows. If you need true hierarchy → TreeGrid. Table emits `role="table"` and cannot honestly carry `aria-level` semantics.
@ -21,7 +21,7 @@ soma ships three WAI-ARIA patterns for tabular / list display. They are **not in
```svelte
< script >
import { Table } from '$soma/components';
import { createTable, type ColumnDef } from '$soma/components/table ';
import { createTable, type ColumnDef } from '$libs/datagrid ';
const columns: ColumnDef< Person > [] = [
{ accessorKey: 'name', header: 'Name', enableSorting: true, enableFiltering: true },
@ -70,17 +70,17 @@ soma ships three WAI-ARIA patterns for tabular / list display. They are **not in
## Parts
| Part | Element | Description |
| -------------------- | ---------- | ---------------------------------------------------------------------------------- |
| `Provider` | `<table>` | Root. Renders `<table>` with translated `aria-label` . |
| `Header` | `<thead>` | Table header section. |
| `Body` | `<tbody>` | Table body section. |
| `Footer` | `<tfoot>` | Table footer section. |
| `ColumnHeader` | `<th>` | Column header. Emits `data-sortable` , `data-sorted` , `data-pinned` , sizing styles. |
| `Row` | `<tr>` | Table row. Emits `data-selected` . |
| `Cell` | `<td>` | Table cell. Emits `data-pinned` , sizing styles, `aria-colindex` . |
| `RowDetailTrigger` | `<button>` | Inside a Cell — toggles the row's detail panel. `aria-expanded` + `aria-controls` . |
| `RowDetail` | `<tr>` | Full-width disclosure panel rendered below the row when open. `<td colspan=N>` inside. |
| Part | Element | Description |
| ------------------ | ---------- | ---- ---------------------------------------------------------------------------------- |
| `Provider` | `<table>` | Root. Renders `<table>` with translated `aria-label` . |
| `Header` | `<thead>` | Table header section. |
| `Body` | `<tbody>` | Table body section. |
| `Footer` | `<tfoot>` | Table footer section. |
| `ColumnHeader` | `<th>` | Column header. Emits `data-sortable` , `data-sorted` , `data-pinned` , sizing styles. |
| `Row` | `<tr>` | Table row. Emits `data-selected` . |
| `Cell` | `<td>` | Table cell. Emits `data-pinned` , sizing styles, `aria-colindex` . |
| `RowDetailTrigger` | `<button>` | Inside a Cell — toggles the row's detail panel. `aria-expanded` + `aria-controls` . |
| `RowDetail` | `<tr>` | Full-width disclosure panel rendered below the row when open. `<td colspan=N>` inside. |
## State Manager (`createTable`)
@ -176,34 +176,34 @@ interface ColumnDef<TData> {
| Provider (`< table > `) | `aria-label` | Translated label (default: 'Data table') |
| ColumnHeader | `role` | `columnheader` |
| ColumnHeader | `aria-sort` | `ascending` \| `descending` \| `none` (only when sortable) |
| Row | `role` | `row` |
| Row | `aria-rowindex` | 1-based row index |
| Row | `aria-selected` | `true` \| `false` (when selection enabled) |
| Cell | `role` | `gridcell` |
| Cell | `aria-colindex` | 1-based column index |
| RowDetailTrigger | `aria-expanded` | `true` \| `false` — open state of the detail panel |
| RowDetailTrigger | `aria-controls` | id of the sibling `Table.RowDetail` |
| RowDetailTrigger | `aria-label` | Translated `Show details` / `Hide details` swap |
| Row | `role` | `row` |
| Row | `aria-rowindex` | 1-based row index |
| Row | `aria-selected` | `true` \| `false` (when selection enabled) |
| Cell | `role` | `gridcell` |
| Cell | `aria-colindex` | 1-based column index |
| RowDetailTrigger | `aria-expanded` | `true` \| `false` — open state of the detail panel |
| RowDetailTrigger | `aria-controls` | id of the sibling `Table.RowDetail` |
| RowDetailTrigger | `aria-label` | Translated `Show details` / `Hide details` swap |
## Data Attributes
| Part | Attribute | Values |
| ------------ | -------------------------- | ---------- ---------------------------------------------- |
| ColumnHeader | `data-table-column-header` | Always present |
| ColumnHeader | `data-sortable` | Present when `enableSorting: true` |
| ColumnHeader | `data-sorted` | `asc` \| `desc` (when actively sorted) |
| ColumnHeader | `data-pinned` | `left` \| `right` (when pinned) |
| Row | `data-table-row` | Always present |
| Row | `data-selected` | Present when selected |
| Cell | `data-table-cell` | Always present |
| Cell | `data-pinned` | `left` \| `right` (when column is pinned) |
| Header | `data-table-header` | Always present |
| Body | `data-table-body` | Always present |
| Footer | `data-table-footer` | Always present |
| RowDetailTrigger | `data-table-row-detail-trigger` | Always present |
| RowDetailTrigger | `data-state` | `open \| closed` |
| RowDetail | `data-table-row-detail` | Always present |
| RowDetail | `data-state` | `open \| closed` (hidden from DOM when closed) |
| Part | Attribute | Values |
| ---------------- | ----- -------------------------- | ---------------------------------------------- |
| ColumnHeader | `data-table-column-header` | Always present |
| ColumnHeader | `data-sortable` | Present when `enableSorting: true` |
| ColumnHeader | `data-sorted` | `asc` \| `desc` (when actively sorted) |
| ColumnHeader | `data-pinned` | `left` \| `right` (when pinned) |
| Row | `data-table-row` | Always present |
| Row | `data-selected` | Present when selected |
| Cell | `data-table-cell` | Always present |
| Cell | `data-pinned` | `left` \| `right` (when column is pinned) |
| Header | `data-table-header` | Always present |
| Body | `data-table-body` | Always present |
| Footer | `data-table-footer` | Always present |
| RowDetailTrigger | `data-table-row-detail-trigger` | Always present |
| RowDetailTrigger | `data-state` | `open \| closed` |
| RowDetail | `data-table-row-detail` | Always present |
| RowDetail | `data-state` | `open \| closed` (hidden from DOM when closed) |
## Sorting
@ -370,9 +370,9 @@ Both parts take `{row}` explicitly. `Table.RowDetail` is a **sibling `<tr>`** of
table.toggleRowDetail(rowId);
table.setRowDetailOpen({ '1': true, '3': true });
table.getIsRowDetailOpen(rowId);
table.getCanShowRowDetail(rowId); // honors getRowCanShowDetail config
table.getCanShowRowDetail(rowId); // honors getRowCanShowDetail config
table.closeAllRowDetails();
table.rowDetailOpen; // current state (read-only)
table.rowDetailOpen; // current state (read-only)
```
### Opting rows out
@ -389,27 +389,27 @@ const table = createTable({
## Comparison with reference libraries
| Feature | TanStack Table | React Aria | Soma Table |
| --------------------------- | -------------------- | ---------- | --------------------------- |
| Framework | Agnostic | React only | Svelte (soma) |
| Sorting (single) | Yes | Yes | Yes |
| Sorting (multi) | Yes | No | Yes (Shift+click) |
| Custom sort fn | Yes | No | Yes |
| Server-side sort | Yes | Manual | Yes (manual mode) |
| Selection none/single/multi | Yes | Yes | Yes |
| Select all / checkbox | Yes | Yes | Yes |
| Pagination (client) | Yes | No | Yes |
| Pagination (server) | Yes | No | Yes (manual mode) |
| ARIA table pattern | No (manual) | Yes (auto) | Yes (auto) |
| Keyboard select | No (manual) | Yes | Yes (Enter/Space on row) |
| `aria-sort` on headers | No (manual) | Yes | Yes |
| `data-*` attributes | No | No | Yes |
| Svelte components | Via adapter | No | Native |
| Column filtering | Yes | No | Yes (column + global) |
| Column visibility | Yes | No | Yes |
| Column resizing | Yes | Partial | Yes (programmatic, no drag) |
| Column pinning | Yes | No | Yes (left/right, sticky) |
| Row expansion (hierarchy) | Yes | No | **Use TreeGrid instead** |
| Row expansion (detail panel)| Yes | No | Yes (`RowDetail`) |
| Grouping / aggregation | Yes | No | No |
| Virtual scrolling | Via TanStack Virtual | No | Via composition with VirtualList |
| Feature | TanStack Table | React Aria | Soma Table |
| ---------------------------- | -------------------- | ---------- | ----- --------------------------- |
| Framework | Agnostic | React only | Svelte (soma) |
| Sorting (single) | Yes | Yes | Yes |
| Sorting (multi) | Yes | No | Yes (Shift+click) |
| Custom sort fn | Yes | No | Yes |
| Server-side sort | Yes | Manual | Yes (manual mode) |
| Selection none/single/multi | Yes | Yes | Yes |
| Select all / checkbox | Yes | Yes | Yes |
| Pagination (client) | Yes | No | Yes |
| Pagination (server) | Yes | No | Yes (manual mode) |
| ARIA table pattern | No (manual) | Yes (auto) | Yes (auto) |
| Keyboard select | No (manual) | Yes | Yes (Enter/Space on row) |
| `aria-sort` on headers | No (manual) | Yes | Yes |
| `data-*` attributes | No | No | Yes |
| Svelte components | Via adapter | No | Native |
| Column filtering | Yes | No | Yes (column + global) |
| Column visibility | Yes | No | Yes |
| Column resizing | Yes | Partial | Yes (programmatic, no drag) |
| Column pinning | Yes | No | Yes (left/right, sticky) |
| Row expansion (hierarchy) | Yes | No | **Use TreeGrid instead** |
| Row expansion (detail panel) | Yes | No | Yes (`RowDetail`) |
| Grouping / aggregation | Yes | No | No |
| Virtual scrolling | Via TanStack Virtual | No | Via composition with VirtualList |