Passer au contenu principal

Responsivetable

Implemented

Generic data table that switches between table, stacked, and card layouts by viewport width, with priority-sorted columns and an EmptyState fallback.

Categoryorganisms
Sourcecomponents/organisms/ResponsiveTable/ResponsiveTable.tsx
AI HintSet responsiveVariant to 'auto' or pin a layout; give Column entries a priority for narrow-screen ordering and a render function for custom cells.

Generic data table that switches between table, stacked, and card layouts by viewport width — priority-sorted columns, optional render functions, and an EmptyState molecule fallback for empty data.

ResponsiveTable

Organism — Data display family

One column model, three layouts: a real <table> with scope="col" headers from 1024px, a stacked label/value list below that, and <dl> definition-list cards below 768px. responsiveVariant "auto" follows the viewport or pins a layout; columns carry a priority that reorders narrow layouts; empty data renders the compact EmptyState molecule (or your emptyState node in a role="status" wrapper).

responsiveVariant: auto — viewport-driven (card < 768, stacked < 1024, table ≥ 1024)

Ticket sales by event
EventTicketsGrossVenue
Soirée Jazz312$12,012.00Café Barcode
Bal des Boys148$4,440.00Salle Albert-Rousseau
Yelle en spectacle640$35,200.00Impériel Bell
Nuit du Piano203$6,090.00Palais Montcalm

Column priorities (event 1 → venue 4) reorder the card/stacked layouts; an empty data array would render the compact EmptyState molecule.

This component supports light and dark mode.

Usage

ResponsiveTable is generic over its row type. Define columns with either an accessor (primitive value) or a render function (arbitrary content), give them priority values so the narrow layouts lead with the primary data, and pass data plus an ariaLabel describing the table. Leave responsiveVariant on auto unless a surface needs a pinned layout — the preview above pins each one so you can compare them.

import { ResponsiveTable } from '@epasslive/design-system'

const columns: Column<SalesRow>[] = [
  { id: 'event', header: 'Event', accessor: (r) => r.event, priority: 1 },
  { id: 'tickets', header: 'Tickets', accessor: (r) => r.tickets, priority: 2 },
  { id: 'gross', header: 'Gross', priority: 3, render: (r) => fmtCad(r.gross) },
  { id: 'venue', header: 'Venue', accessor: (r) => r.venue, priority: 4 },
]

<ResponsiveTable
  columns={columns}
  data={rows}
  responsiveVariant="auto"
  ariaLabel={t('salesByEvent')}
/>

Layout behavior

auto resolves the layout from the viewport width — card below 768px, stacked below 1024px, a real table from 1024px up. The card layout renders each row as a <dl> definition list (label from cardLabelAccessor or the header), the stacked layout pairs a label with each value, and the table layout keeps the declaration order with scope="col" headers. Priority reorders only the narrow layouts. Empty data never renders a blank box: the compact EmptyState molecule shows "No data available." unless you pass your own emptyState node.

Accessibility

Each layout keeps a perceivable structure: real <table> with scope="col" headers, definition lists for cards, paired label/value spans when stacked (WCAG 1.3.1). The region is named by ariaLabel so sibling tables are distinguishable (WCAG 4.1.2), and the empty state is announced politely (role="status"). The component is presentational — no focusable cells, no keyboard traps.

Props

Prop
Type
Default
Description
columns*
Column<T>[]
—

Column definitions — see the column table below.

data*
T[]
—

Row data — one item per row, typed by the generic. An empty array triggers the empty state.

responsiveVariant
"auto" | "table" | "stacked" | "card"
"auto"

Layout selector — auto resolves card < 768px, stacked < 1024px, table ≥ 1024px.

cardLabelAccessor
(row: T, column: Column<T>) => string
—

Overrides the card-layout field label (defaults to column.header).

emptyState
React.ReactNode
—

Custom empty-state node, rendered inside a role="status" wrapper. Omit for the compact EmptyState molecule.

ariaLabel
string
—

Accessible name for the layout region (or the empty-state wrapper).

Column definition

Column<T> — the single column model shared by all three layouts.

Prop
Type
Default
Description
id*
string
—

Column identifier — also the fallback key when reading the row value by property name.

header*
string
—

Header label; becomes the field label in card/stacked layouts.

render
(row: T, rowIndex: number) => React.ReactNode
—

Custom cell renderer — omitted cells fall back to accessor(row) or row[col.id].

width
string
—

CSS width applied to the <th> in the table layout (e.g. "25%").

priority
number
100

Sort key for the narrow layouts — columns re-order ascending by priority in card and stacked mode.

accessor
(row: T) => string | number
—

Primitive value extractor for the default cell content.

Design System v4.1.0 | ePassLive