Design System

Table Molecules

ImportCC

Available via @stackmates/ui-interactive/molecules

ColumnHeader

Sortable column header with CVA alignment variants and optional inline filter. Renders a <th> with SortIndicator atom.

Click “Name” to cycle sort direction.

Name
Email
Amount
Jane Doejane@co.com$1,200

Sort: false

ColumnHeaderProps

PropTypeDefaultDescription
title*stringColumn title text
sortablebooleanfalseEnable sorting
sortDirection'asc' | 'desc' | falsefalseCurrent sort state
onSort() => voidSort trigger callback
filterablebooleanfalseShow inline filter input
align'left' | 'center' | 'right''left'Text alignment

Installation

typescript
import { ColumnHeader } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Sets aria-sort when sorted
  • Adds role="button" and tabIndex when sortable
  • Keyboard activation via Enter and Space

ColumnFilter

Minimal inline filter input for a table column. Stops click propagation to avoid triggering column sort.

Name

Value: (empty)

ColumnFilterProps

PropTypeDefaultDescription
title*stringFilter label
valuestring''Current value
onChange*(value: string) => voidChange handler
placeholderstringInput placeholder

Installation

typescript
import { ColumnFilter } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Click propagation stopped to prevent sort interference
  • Native text input with full keyboard support

RowActions

Dropdown menu triggered by a vertical ellipsis button. Generic over row data type TData. Supports icons, destructive styling, and separators.

Acme CorpLast: (none)

RowActionsProps

PropTypeDefaultDescription
row*TDataRow data passed to action handlers
actions*RowAction<TData>[]Actions with label, onClick, icon?, destructive?, separatorBefore?
triggerVariant'ghost' | 'outline''ghost'Trigger button style

Installation

typescript
import { RowActions } from '@stackmates/ui-interactive/molecules';
import type { RowAction } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Trigger has aria-label="Row actions"
  • Stop-propagation prevents row selection
  • Built on Radix DropdownMenu with keyboard nav

TablePagination

Pagination controls with first/prev/next/last buttons, page size selector, and summary text showing item range or selection count.

Showing 1 to 10 of 87 results
Rows per page
Page 1 of 9

TablePaginationProps

PropTypeDefaultDescription
pageIndex*numberCurrent page (0-based)
pageSize*numberItems per page
pageCount*numberTotal pages
onPageChange*(pageIndex: number) => voidPage change handler
totalItemsnumberTotal count for summary
onPageSizeChange(size: number) => voidPage size change handler

Installation

typescript
import { TablePagination } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Each button has descriptive aria-label
  • Buttons disabled at boundaries
  • Summary provides range context

SearchFilterBar

Debounced search input + dropdown filters (status, priority, type) + date range + action buttons. The primary toolbar for table search and filtering.

State: {"query":""}

SearchFilterBarProps

PropTypeDefaultDescription
filters*SearchFiltersCurrent filter state: { query, status?, priority?, type? }
onFiltersChange*(filters) => voidFilter change callback
configFilterConfigstatusOptions, priorityOptions, typeOptions, searchPlaceholder
primaryActionsActionButton[]Buttons on the right of search row
debounceMsnumber300Search debounce delay

Installation

typescript
import { SearchFilterBar } from '@stackmates/ui-interactive/molecules';
import type { FilterConfig, SearchFilters } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Search debounced to reduce screen reader noise
  • Filter dropdowns are keyboard navigable
  • Clear all button resets all filters

BulkActionsToolbar

Multi-select action bar for bulk delete (with confirmation), status/priority updates, and custom actions. Hides when selectedCount is 0.

3 contacts selected

BulkActionsToolbarProps

PropTypeDefaultDescription
selectedCount*numberNumber of selected items
onClearSelection*() => voidDeselect all handler
onBulkDelete() => Promise<void>Delete with confirmation dialog
onBulkStatusUpdate(status: string) => Promise<void>Status update dropdown
configBulkActionsConfigentityLabel, showDelete, statusOptions, priorityOptions

Installation

typescript
import { BulkActionsToolbar } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Clear selection has aria-label
  • Delete shows confirmation dialog
  • Spinner announces processing state

TableToolbar

Unified toolbar composing SearchFilterBar and BulkActionsToolbar. Switches mode automatically: search when nothing selected, bulk actions when selectedCount > 0.

TableToolbarProps

PropTypeDefaultDescription
filters*SearchFiltersFilter state for SearchFilterBar
onFiltersChange*(filters) => voidFilter change handler
selectedCountnumber0When > 0, switches to bulk actions mode
onClearSelection() => voidEnables bulk actions mode
filterConfigFilterConfigSearchFilterBar configuration
bulkActionsConfigBulkActionsConfigBulkActionsToolbar configuration

Installation

typescript
import { TableToolbar } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Mode switch preserves focus context
  • Inherits a11y from both sub-toolbars

DataImportExport

Import/export dropdown with file upload, format selection (CSV, JSON, XLSX), optional drag-and-drop, template download, and result dialog.

DataImportExportProps

PropTypeDefaultDescription
onExport*(format: 'csv'|'json'|'xlsx') => voidExport handler with format
onImport*(file: File) => Promise<ImportResult>Import handler
exportCountnumberItems available for export
configDataImportExportConfigentityLabel, acceptedFileTypes, maxFileSizeMB, exportFormats
onDownloadTemplate() => voidShows template download option

Installation

typescript
import { DataImportExport } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Hidden file input has aria-label
  • Spinner provides loading feedback
  • Result dialog announces import outcome

ExportMenu

Export format selection with dropdown or inline button variants. Shows format icons, labels, and optional descriptions. Supports disabled and loading states.

Dropdown variant

Inline variant

Last exported: (none)

ExportMenuProps

PropTypeDefaultDescription
onExport*(format: string) => voidCallback when an export format is selected
formats*ExportFormat[]Available formats with key, label, optional icon and description
variant'dropdown' | 'inline''dropdown'Display as dropdown menu or inline buttons
buttonLabelstring'Export'Custom label for the dropdown trigger button
disabledbooleanfalseDisable all export actions
loadingbooleanfalseShow loading state

Installation

typescript
import { ExportMenu } from '@stackmates/ui-interactive/molecules';
import type { ExportFormat } from '@stackmates/ui-interactive/molecules';

Usage

Report download menu

tsx
<ExportMenu
  onExport={(format) => downloadReport(format)}
  formats={[
    { key: 'pdf', label: 'PDF', description: 'Best for printing' },
    { key: 'csv', label: 'CSV', description: 'For data analysis' },
  ]}
  buttonLabel="Download Report"
/>

Accessibility

  • Built on Radix DropdownMenu with full keyboard navigation
  • data-testid="export-menu" on the trigger for test targeting
  • Disabled state prevents interaction and visually mutes the button

Accordion

Expandable disclosure sections built on Radix Accordion. Supports single or multiple open items with animated expand/collapse transitions.

Accordion / AccordionItem / AccordionTrigger / AccordionContent

PropTypeDefaultDescription
type*'single' | 'multiple'Single or multiple items open at a time
valuestring | string[]Controlled open item(s)
defaultValuestring | string[]Initially open item(s) (uncontrolled)
collapsiblebooleanfalseAllow closing all items (type="single" only)

Installation

typescript
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@stackmates/ui-interactive/molecules';

Usage

FAQ section

tsx
<Accordion type="single" collapsible>
  <AccordionItem value="q1">
    <AccordionTrigger>Question one?</AccordionTrigger>
    <AccordionContent>Answer one.</AccordionContent>
  </AccordionItem>
</Accordion>

Accessibility

  • Built on Radix Accordion with WAI-ARIA disclosure pattern
  • Trigger has aria-expanded and aria-controls
  • Content uses aria-hidden when closed
  • Keyboard navigation: Space/Enter to toggle, Arrow keys between items

Carousel

Content carousel powered by Embla Carousel. Supports looping, horizontal/vertical orientation, keyboard navigation, and previous/next controls.

Slide 1
Slide 2
Slide 3
Slide 4

CarouselProps

PropTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Scroll direction
optsCarouselOptionsEmbla options (loop, align, etc.)
pluginsCarouselPluginEmbla plugins (autoplay, etc.)
setApi(api: CarouselApi) => voidReceive Embla API for programmatic control

Installation

typescript
import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext } from '@stackmates/ui-interactive/molecules';

Accessibility

  • role="region" with aria-roledescription="carousel"
  • Slides have role="group" + aria-roledescription="slide"
  • Arrow keys scroll, nav buttons have sr-only labels
Related:

Resizable

Split-panel layout with draggable handles built on react-resizable-panels. Supports horizontal and vertical directions with minimum size constraints.

Sidebar (40%)
Content (60%)

ResizablePanelGroup / ResizablePanel / ResizableHandle

PropTypeDefaultDescription
direction'horizontal' | 'vertical''horizontal'Split direction
defaultSizenumberPanel default size as percentage (ResizablePanel)
minSizenumberPanel minimum size as percentage (ResizablePanel)
withHandlebooleanfalseShow grip icon (ResizableHandle)

Installation

typescript
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@stackmates/ui-interactive/molecules';

Accessibility

  • Handle is focusable and keyboard-operable
  • withHandle adds visible grip for discoverability
  • Arrow keys resize panels when handle is focused
Related: