Table composes several directives and components sharing one DI context.

table.ts
import {
  KuiCell,
  KuiRow,
  KuiSelectCell,
  KuiSelectTh,
  KuiTable,
  KuiTh,
  KuiThGroup,
} from '@kikita-labs/ui';

Apply kuiTable to a real table element, keep native thead/tbody/tr/th/td markup, and read #table='kuiTable' for sorted rows.

Ava ChenEngineerActive
Liam OseiDesignerInvited
Noor MalikProduct ManagerActive
Priya RaoSupportSuspended
Tomas SilvaEngineerActive
<table kuiTable [data]="rows" #table="kuiTable">
  <thead>
    <tr kuiThGroup>
      <th kuiTh sortKey="name">Name</th>
      <th kuiTh sortKey="role">Role</th>
      <th kuiTh sortKey="status">Status</th>
    </tr>
  </thead>
  <tbody>
    @for (row of table.sortedData(); track row.id) {
      <tr kuiRow [value]="row">
        <td kuiCell>{{ row.name }}</td>
        <td kuiCell>{{ row.role }}</td>
        <td kuiCell>{{ row.status }}</td>
      </tr>
    }
  </tbody>
</table>

sortKey on th[kuiTh] enables cycling through asc, desc, and clear via a native button. Uncontrolled sort (no (sortChange) listener) sorts data locally and exposes results as sortedData().

  • Controlled sort: observe (sortChange) and reorder data yourself.
  • Uncontrolled sort: leave (sortChange) unbound and read table.sortedData().
  • comparator overrides the default comparison for a column.

The selection column only renders when (selectionChange) is observed. th[kuiSelectTh] renders the select-all checkbox; td[kuiSelectCell] renders each row's checkbox.

Ava ChenEngineerActive
Liam OseiDesignerInvited
Noor MalikProduct ManagerActive
Priya RaoSupportSuspended
Tomas SilvaEngineerActive

0 of 5 selected

<table kuiTable [data]="rows" (selectionChange)="onSelectionChange($event)">
  <thead>
    <tr kuiThGroup>
      <th kuiSelectTh ariaLabel="Select all team members"></th>
      <th kuiTh sortKey="name">Name</th>
      <th kuiTh sortKey="role">Role</th>
      <th kuiTh sortKey="status">Status</th>
    </tr>
  </thead>
  <tbody>
    @for (row of rows; track row.id) {
      <tr kuiRow [value]="row">
        <td kuiSelectCell [ariaLabel]="'Select ' + row.name"></td>
        <td kuiCell>{{ row.name }}</td>
        <td kuiCell>{{ row.role }}</td>
        <td kuiCell>{{ row.status }}</td>
      </tr>
    }
  </tbody>
</table>
<p class="row-selection-table-example__note">
  {{ selected().length }} of {{ rows.length }} selected
</p>

sticky on th[kuiTh] pins that header cell horizontally; sticky on tr[kuiThGroup] pins the whole header row vertically inside a scrolling container.

Ava ChenEngineerPlatformRemoteActive
Liam OseiDesignerProductBerlinInvited
Noor MalikProduct ManagerProductRemoteActive
Priya RaoSupportOperationsSingaporeSuspended
Tomas SilvaEngineerPlatformLisbonActive
<div class="sticky-header-table-example__scroll">
  <table kuiTable [data]="rows" #table="kuiTable">
    <thead>
      <tr kuiThGroup sticky>
        <th kuiTh sortKey="name" sticky>Name</th>
        <th kuiTh sortKey="role">Role</th>
        <th kuiTh sortKey="department">Department</th>
        <th kuiTh sortKey="location">Location</th>
        <th kuiTh sortKey="status">Status</th>
      </tr>
    </thead>
    <tbody>
      @for (row of table.sortedData(); track row.id) {
        <tr kuiRow [value]="row">
          <td kuiCell>{{ row.name }}</td>
          <td kuiCell>{{ row.role }}</td>
          <td kuiCell>{{ row.department }}</td>
          <td kuiCell>{{ row.location }}</td>
          <td kuiCell>{{ row.status }}</td>
        </tr>
      }
    </tbody>
  </table>
</div>

All three behaviors compose on the same table through the shared kuiTable DI context.

Ava ChenEngineerPlatformActive
Liam OseiDesignerProductInvited
Noor MalikProduct ManagerProductActive
Priya RaoSupportOperationsSuspended
Tomas SilvaEngineerPlatformActive

0 of 5 selected

<div class="combined-table-example__scroll">
  <table kuiTable [data]="rows" #table="kuiTable" (selectionChange)="onSelectionChange($event)">
    <thead>
      <tr kuiThGroup sticky>
        <th kuiSelectTh ariaLabel="Select all team members"></th>
        <th kuiTh sortKey="name" sticky>Name</th>
        <th kuiTh sortKey="role">Role</th>
        <th kuiTh sortKey="department">Department</th>
        <th kuiTh sortKey="status">Status</th>
      </tr>
    </thead>
    <tbody>
      @for (row of table.sortedData(); track row.id) {
        <tr kuiRow [value]="row">
          <td kuiSelectCell [ariaLabel]="'Select ' + row.name"></td>
          <td kuiCell>{{ row.name }}</td>
          <td kuiCell>{{ row.role }}</td>
          <td kuiCell>{{ row.department }}</td>
          <td kuiCell>{{ row.status }}</td>
        </tr>
      }
    </tbody>
  </table>
</div>
<p class="combined-table-example__note">{{ selected().length }} of {{ rows.length }} selected</p>

Set defaults.table once for the application or for a subtree. Each option resolves as local input > defaults.table.<option> > built-in default.

import { provideKikitaUi } from '@kikita-labs/ui';

export const appConfig: ApplicationConfig = {
  providers: [
    provideKikitaUi({
      defaults: {
        table: {
          /* options below */
        },
      },
    }),
  ],
};
NameTypeDefaultDescription
size'xs' | 'sm' | 'md' | 'lg' — Component size. Takes precedence over the global defaults.size.

Set any of these on the component or an ancestor to restyle one part. Each token is optional: when it is not set, the part uses the role or scale token in the default column.

NameTypeDefaultDescription
--kui-table-colorCSS custom property--kui-color-textColor
--kui-table-sort-color-hoverCSS custom property--kui-color-textSort color, hover
--kui-table-checkbox-borderCSS custom property--kui-color-border-controlCheckbox border color
--kui-table-checkbox-border-hoverCSS custom property--kui-color-primary-indicatorCheckbox border color, hover
--kui-table-checkbox-bg-checkedCSS custom property--kui-color-primary-fillCheckbox background, checked
--kui-table-checkbox-border-checkedCSS custom property--kui-color-primary-indicatorCheckbox border color, checked
--kui-table-checkbox-mark-colorCSS custom property--kui-color-primary-on-fillCheckbox mark color
--kui-table-checkbox-mark-color-indeterminateCSS custom property--kui-color-primary-on-fillCheckbox mark color, indeterminate
--kui-table-sort-focus-ring-colorCSS custom property--kui-color-focusSort focus ring color
--kui-table-checkbox-focus-ring-colorCSS custom property--kui-color-focusCheckbox focus ring color
NameTypeDefaultDescription
--kui-table-th-sort-button-radiusCSS custom property--kui-radius-xsTh sort button corner radius
--kui-table-th-sort-icon-font-sizeCSS custom property--kui-text-2xs-sizeTh sort icon font size

Every text the component owns (accessible names, visible words, announcements) is a typed message with an English default. Override it for the application with provideKikitaUi({ messages }), or for a subtree with provideKuiMessages.

NameTypeDefaultDescription
selectRowstring — Name of a row checkbox. Default Select row.
selectAllRowsstring — Name of the header checkbox. Default Select all rows.
sortAscendingKuiMessageFn<{ label: string; }> — Name of the sort button of an unsorted column. Default Sort {label} ascending.
sortDescendingKuiMessageFn<{ label: string; }> — Name of the sort button of an ascending column. Default Sort {label} descending.
clearSortKuiMessageFn<{ label: string; }> — Name of the sort button of a descending column. Default Clear {label} sort.

Inputs and outputs verified against @kikita-labs/ui v2.0.0 public typings.

NameTypeDefaultDescription
dataT[][]Source rows for sorting and selection. Selector: table[kuiTable].
size'xs' | 'sm' | 'md' | 'lg' | undefinedundefinedTable density and typography scale. Falls back to defaults.table.size, then the global defaults.size, then md. Selector: table[kuiTable].
selectionChangeoutput<T[]> — Emits current selection whenever it changes. Observing this output is what makes the selection column appear. Selector: table[kuiTable].
sortChangeoutput<KuiActiveSortState | null> — Emits the active sort key/direction, or null when sort is cleared. When observed, kuiTable hands row ordering to the parent instead of sorting locally. Selector: table[kuiTable].
sortStateSignal<KuiSortState> — Current sort state (null when no column is sorted). Selector: table[kuiTable].
sortedDataSignal<T[]> — Rows in current sort order. Use this in the template instead of data when sort is uncontrolled. Selector: table[kuiTable].
isSelected(item)(item: T) => boolean — Returns whether a given row value is currently selected. Selector: table[kuiTable].
#table="kuiTable"exportAs — Template reference for reading sortedData(), sortState(), and selection state.
sortKeystring | undefined-Enables sort for this header cell; usually matches a row property name. Selector: th[kuiTh].
comparator(a: T, b: T) => number-Custom sort function for the column, used instead of default comparison. Selector: th[kuiTh].
stickybooleanfalsePins this header cell horizontally with position: sticky. Selector: th[kuiTh]. Also available on tr[kuiThGroup] to pin the whole header row vertically.
valueT | undefinedundefinedData object used for selected state. Omit it for a presentational row without selection. Selector: tr[kuiRow].
ariaLabelstring | undefinedundefinedAccessible label for the select-all checkbox; falls back to the table.selectAllRows message (Select all rows). Selector: th[kuiSelectTh].
ariaLabelstring | undefinedundefinedAccessible label for a row selection checkbox, falling back to the table.selectRow message (Select row), e.g. "Select " + row.name. Selector: td[kuiSelectCell].
td[kuiCell]marker directive — Applies Kikita table cell styling to a native td. No inputs in the installed version.
--kui-table-font-sizeCSS custom property — Base font size for table text.
--kui-table-th-pyCSS custom property — Vertical padding for header cells.
--kui-table-cell-pxCSS custom property — Horizontal padding for cells.
--kui-table-row-pyCSS custom property — Vertical padding for body row cells.
--kui-table-th-fgCSS custom property — Header cell text color.
--kui-table-th-bgCSS custom property — Header cell background color.
--kui-table-borderCSS custom property — Outer table border color.
--kui-table-row-borderCSS custom property — Row divider border color.
--kui-table-row-hover-bgCSS custom property — Row background on hover.
--kui-table-row-selected-bgCSS custom property — Row background when selected.
--kui-table-row-selected-accentCSS custom property — Accent color for selected rows.
--kui-table-sort-active-colorCSS custom property — Color used for the active sort indicator.
--kui-table-bgCSS custom property — Table background color.
KuiTableOptionsinterface-Shape of defaults.table: size.

Table keeps real table semantics and native interactive elements for sort and selection.

  • Keep real table, thead, tbody, tr, th, and td markup; kuiTable does not replace table semantics.
  • Sortable headers keep aria-sort on the th while a native button owns keyboard and click interaction.
  • Selection uses native checkboxes; pass a human-readable ariaLabel on kuiSelectTh and kuiSelectCell.
  • Without an ariaLabel the checkboxes are named by the table.selectRow and table.selectAllRows messages, and the sort buttons by table.sortAscending, table.sortDescending and table.clearSort.
  • Add a native caption when the surrounding page does not already provide a clear table title.
  • Avoid placing complex interactive widgets inside cells unless their focus order and labels are explicitly tested.

One documented library behavior is not available in the installed package version.

  • The library source documents sticky on td[kuiCell] for pinning body columns, but in the installed @kikita-labs/ui version (see the API section above for the exact version) KuiCell exposes no public inputs, so body cells cannot be pinned through a directive input yet. Only header-side stickiness (th[kuiTh] sticky and tr[kuiThGroup] sticky) is available in this version. This page documents only the working header behavior; do not add sticky to td[kuiCell] until the installed package exposes it.

Consumer-visible changes of @kikita-labs/ui 2.0 for Table.

  • The classes are KuiTable, KuiTh, KuiThGroup, KuiRow, KuiCell, KuiSelectTh and KuiSelectCell; the Directive and Component suffixes are gone.
  • The default size moved to provideKuiDefaults({ table: { size } }); the input is now undefined until set.
  • tr[kuiRow]value is optional: omit it for a presentational row without selection.
  • Select and sort button names come from the table messages instead of fixed English labels.
  • The header text is --kui-text-xs-size (11px, was 10px), and the checkbox and sort button corners follow the radius seed.