CP Registry

Table

Semantische Datentabelle mit optionalem Textfilter und clientseitiger Spalten-Sortierung. Responsive via Container Query: unter 40rem Container-Breite stacken Zeilen zu Karten (Column-Header bleibt für Screenreader erhalten, wird visuell als Label pro Zelle eingeblendet).

Basis

Minimales Beispiel: Spalten + Zeilen. Erste Spalte wird standardmäßig als Row-Header (<th scope="row">) gerendert.

Einfache Tabelle

Produkt Preis Lager
Standard-Abo19,90128
Komfort-Abo29,9042
Premium-Abo49,9012
Einzelticket3,20999
<Table
  columns={[
    { key: 'produkt', label: 'Produkt', isRowHeader: true },
    { key: 'preis',   label: 'Preis',   align: 'right' },
    { key: 'lager',   label: 'Lager',   align: 'right' },
  ]}
  rows={[
    { produkt: 'Standard-Abo', preis: '19,90', lager: 128 },
    { produkt: 'Komfort-Abo',  preis: '29,90', lager: 42 },
    { produkt: 'Premium-Abo',  preis: '49,90', lager: 12 },
  ]}
/>

Sortierbar

Spalten mit sortable: true erzeugen data-sortable="true" auf dem <th>. Klick auf den Header-Button toggelt aria-sort zwischen ascending und descending. Clientseitige Sortierung erkennt deutsche Zahlenformate (1.234,56).

Sortierbare Spalten

Produkt
Standard-Abo19,90128
Komfort-Abo29,9042
Premium-Abo49,9012
Einzelticket3,20999
<Table
  columns={[
    { key: 'produkt', label: 'Produkt', isRowHeader: true },
    { key: 'preis',   label: 'Preis',   sortable: true, align: 'right' },
    { key: 'lager',   label: 'Lager',   sortable: true, align: 'right' },
  ]}
  rows={produkteRows}
/>

Mit Filter

Die filterable-Prop blendet ein Suchfeld oberhalb der Tabelle ein. Der Filter matcht case-insensitive über alle sichtbaren Zellen. Bei leeren Treffern wird ein Platzhaltertext angezeigt.

Filterbare Tabelle

Strukturdaten 2025 NRW
Fläche (km2)4.9438.2214.11534.112
Einwohner5,1 Mio.5,9 Mio.3,2 Mio.18,1 Mio.
Einwohner/km21.032718778531
Bahnstationen170282198715
Schienennetz (km)8201.4209353.502
Elektrifizierungsquote82 %76 %88 %81 %
Zugkilometer (Mio.)32,441,828,9115,4
<Table
  caption="Strukturdaten 2025 NRW"
  filterable
  columns={strukturColumns}
  rows={strukturRows}
/>

Responsive (Container Query)

Die Tabelle beobachtet die Breite ihres Containers (nicht des Viewports). Unterhalb von 40rem wird jede Zeile zur abgerundeten Karte mit dem Row-Header als Titel. Jede Zelle zeigt ihr Spaltenlabel via data-label (kommt automatisch aus der Column-Config).

Ziehe das Browserfenster schmal, um das Stacked-Layout zu sehen. Oder platziere die Tabelle in einer schmalen Spalte (Sidebar-Grid):

Tabelle in schmaler Spalte

Produkt
Standard-Abo19,90128
Komfort-Abo29,9042
Premium-Abo49,9012
Einzelticket3,20999
<div class="max-w-sm">
  <Table columns={produkteColumns} rows={produkteRows} />
</div>

Props

Prop Typ Beschreibung
captionstring?Visuell versteckte <caption> für Screenreader.
columnsColumn[]Spalten-Definition: key, label, optional sortable, align, isRowHeader.
rowsRecord[]Array von Objekten, Keys entsprechen den Column-Keys.
filterablebooleanZeigt Filter-Input. Default false.
filterPlaceholderstring?Placeholder-Text des Filter-Inputs.
classstring?Zusätzliche Klassen auf dem Wrapper.

Column-Konfiguration

Feld Typ Beschreibung
keystringFeldname in den Row-Objekten.
labelstringHeader-Text und Stacked-Mode-Label.
sortableboolean?Macht Spalte clientseitig sortierbar.
align'left' \| 'right' \| 'center'Text-Alignment für <th> und <td>.
isRowHeaderboolean?Rendert Zelle als <th scope="row">. Default: erste Spalte.

Barrierefreiheit

  • Korrekte Tabellensemantik mit <thead>, <tbody>, scope="col" und scope="row".
  • Sortier-Buttons mit aria-sort (none / ascending / descending).
  • Filter-Input mit <label> (sr-only) und type="search".
  • Im Stacked-Layout bleibt <thead> erhalten (visually hidden via clip), Screenreader lesen die Spaltenueberschriften mit.
  • Caption via caption-Prop für Screenreader-Kontext.

Installation

cp-components add table