Skip to content

@vibe-labs/design-components-tables

Table component tokens, styles, and TypeScript types for the Vibe Design System.

Installation

css
@import "@vibe-labs/design-components-tables";
ts
import { TableVariants, TableSizes, TableAlignments, TableSortDirections } from "@vibe-labs/design-components-tables/types";
import type { TableColumn, TableSortState, TableStyleProps } from "@vibe-labs/design-components-tables/types";

Contents

Tokens (tables.css)

CSS custom properties defined in @layer vibe.tokens:

Container

TokenDefault
--table-bg--surface-base
--table-border-color--border-subtle
--table-border-width--border-width-1
--table-radius--radius-lg
TokenDefaultDescription
--table-header-bg--surface-subtleHeader background
--table-header-color--text-mutedHeader text color
--table-header-font-size--text-xsHeader font size
--table-header-font-weight--font-boldHeader font weight
--table-header-text-transformuppercaseHeader text transform
--table-header-height-sm2remHeader height (sm)
--table-header-height-md2.5remHeader height (md)
--table-header-height-lg3remHeader height (lg)
--table-header-px--space-3Horizontal padding

Row

TokenDefaultDescription
--table-row-height-sm2.25remRow height (sm)
--table-row-height-md2.75remRow height (md)
--table-row-height-lg3.25remRow height (lg)
--table-row-px--space-3Horizontal padding
--table-row-border-color--border-subtleRow separator color
--table-row-bgtransparentDefault row background
--table-row-bg-stripe--surface-subtleAlternating row background
--table-row-hover-bg--surface-hover-overlayHover background
--table-row-selected-bgaccent @ 8% mixSelected row background
--table-row-selected-border--color-accentSelected row left accent strip

Cell

TokenDefault
--table-cell-px--space-2
--table-cell-font-size-sm--text-xs
--table-cell-font-size-md--text-sm
--table-cell-font-size-lg--text-base
--table-cell-color--text-primary

Sort Indicator

TokenDefault
--table-sort-indicator-color--text-muted
--table-sort-indicator-active-color--color-accent
--table-sort-indicator-size0.75rem

Empty State

TokenDefault
--table-empty-py--space-12
--table-empty-color--text-muted
--table-empty-font-size--text-sm
TokenDefault
--table-footer-bgtransparent
--table-footer-border-color--border-subtle
--table-footer-px--space-3
--table-footer-py--space-2

Generated Styles (tables.g.css)

Component classes generated into @layer vibe.components.

Table Container

Flex column with border, radius, and overflow hidden. Supports four variants:

  • default — border + background
  • outlined — border only, transparent bg
  • elevated — no border, shadow elevation
  • ghost — no border, no background (rows only)

Size Modifier

data-size="sm|md|lg" controls header height, row height, and cell font size in one attribute.

Flags

  • striped — alternating row backgrounds via :nth-child(even)
  • hoverable — row hover background transition
  • interactive — pointer cursor on rows
  • fixed-header — sticky header with z-index
  • full — width: 100%

Header Cell

Flex item with uppercase muted text. Sortable columns show a chevron indicator:

  • [data-sortable] — cursor: pointer, chevron appears on hover
  • [data-sorted="asc"] — chevron rotated up, accent color
  • [data-sorted="desc"] — chevron down, accent color

Row

Flex row with bottom border separator. Supports:

  • [data-selected] — accent-tinted background + left border accent strip
  • [data-disabled] — reduced opacity, no pointer events
  • Last row automatically drops bottom border

Cell

Flex item with column padding and alignment:

  • [data-align="left|center|right"] — horizontal alignment
  • [data-truncate] — ellipsis overflow (with child element support)
  • [data-mono] — monospace font + tabular-nums
  • [data-nowrap] — prevent wrapping without truncation

Empty State

Centred muted text with generous vertical padding for no-data states.

Flex row with top border. Attachment point for pagination or summary content.

  • [data-align="left|center|right|between"] — content alignment

TypeScript Types (types/)

ts
// Runtime constants
TableSizes;          // ['sm', 'md', 'lg']
TableVariants;       // ['default', 'outlined', 'elevated', 'ghost']
TableAlignments;     // ['left', 'center', 'right']
TableSortDirections; // ['asc', 'desc']

// Column definition — generic over row type
interface TableColumn<T> {
  key: keyof T & string;
  label: string;
  width?: string;
  minWidth?: string;
  align?: TableAlignment;
  sortable?: boolean;
  truncate?: boolean;
  mono?: boolean;
  hidden?: boolean;
  cellClass?: string;
  headerClass?: string;
}

// Sort state
interface TableSortState {
  key: string;
  direction: TableSortDirection;
}

// Row identifier
type TableRowKey<T> = (keyof T & string) | ((item: T) => string | number);

// Per-element style props
interface TableStyleProps {
  variant?: TableVariant;
  size?: TableSize;
  striped?: boolean;
  hoverable?: boolean;
  interactive?: boolean;
  fixedHeader?: boolean;
  full?: boolean;
}
interface TableHeaderCellStyleProps {
  align?: TableAlignment;
  sortable?: boolean;
  sorted?: TableSortDirection;
}
interface TableRowStyleProps { selected?: boolean; disabled?: boolean }
interface TableCellStyleProps { align?: TableAlignment; truncate?: boolean; mono?: boolean; nowrap?: boolean }
interface TableFooterStyleProps { align?: string }

Dist Structure

FileDescription
index.cssBarrel — imports tokens + generated styles
tables.cssToken definitions
tables.g.cssGenerated component styles
index.js / index.d.tsTypeScript types + runtime constants

Dependencies

Requires tokens from @vibe-labs/design (colors, surfaces, borders, spacing, typography, transitions, elevation).

Pagination delegates to @vibe-labs/design-components-pagination — this package does not own --pagination-* tokens.

Build

bash
npm run build

Usage Guide

Import

css
@import "@vibe-labs/design-components-tables";
ts
import { TableVariants, TableSizes } from "@vibe-labs/design-components-tables/types";
import type { TableStyleProps, TableColumn } from "@vibe-labs/design-components-tables/types";

Variants

Attribute / FlagValuesNotes
data-variantdefault outlined elevated ghostDefault: default
data-sizesm md lgDefault: md
data-stripedboolean flagAlternating row backgrounds
data-hoverableboolean flagRow hover highlight
data-interactiveboolean flagPointer cursor on rows
data-fixed-headerboolean flagSticky header
data-fullboolean flagWidth: 100%
data-selectedon rowAccent-tinted selection
data-disabledon rowReduced opacity, no pointer
data-sortableon header cellShows sort chevron on hover
data-sortedasc desc on header cellActive sort indicator
data-alignleft center right (+ between on footer)Content alignment
data-truncateon cellEllipsis overflow
data-monoon cellMonospace + tabular nums
data-nowrapon cellNo text wrapping

Examples

Basic table

html
<div class="table">
  <div class="table-header">
    <div class="table-header-cell">Name</div>
    <div class="table-header-cell">Role</div>
    <div class="table-header-cell">Status</div>
  </div>
  <div class="table-row">
    <div class="table-cell">Alice</div>
    <div class="table-cell">Admin</div>
    <div class="table-cell">Active</div>
  </div>
  <div class="table-row">
    <div class="table-cell">Bob</div>
    <div class="table-cell">User</div>
    <div class="table-cell">Inactive</div>
  </div>
</div>

Striped + hoverable table

html
<div class="table" data-striped data-hoverable data-full>
  <div class="table-header">
    <div class="table-header-cell">Product</div>
    <div class="table-header-cell" data-align="right">Price</div>
    <div class="table-header-cell" data-align="right">Qty</div>
  </div>
  <div class="table-row">
    <div class="table-cell">Widget A</div>
    <div class="table-cell" data-align="right" data-mono>$12.00</div>
    <div class="table-cell" data-align="right">42</div>
  </div>
  <div class="table-row">
    <div class="table-cell">Widget B</div>
    <div class="table-cell" data-align="right" data-mono>$8.50</div>
    <div class="table-cell" data-align="right">17</div>
  </div>
</div>

Elevated variant with sortable columns

html
<div class="table" data-variant="elevated" data-size="sm">
  <div class="table-header">
    <div class="table-header-cell" data-sortable data-sorted="asc">Name</div>
    <div class="table-header-cell" data-sortable>Date</div>
    <div class="table-header-cell">Actions</div>
  </div>
  <div class="table-row">
    <div class="table-cell">Alice</div>
    <div class="table-cell" data-mono>2024-01-15</div>
    <div class="table-cell"><button>Edit</button></div>
  </div>
</div>

Selected and disabled rows

html
<div class="table" data-interactive data-hoverable>
  <div class="table-header">
    <div class="table-header-cell">Item</div>
    <div class="table-header-cell">Value</div>
  </div>
  <div class="table-row" data-selected>
    <div class="table-cell">Selected row</div>
    <div class="table-cell">100</div>
  </div>
  <div class="table-row" data-disabled>
    <div class="table-cell">Disabled row</div>
    <div class="table-cell">—</div>
  </div>
</div>
html
<div class="table" data-full>
  <div class="table-header">
    <div class="table-header-cell">Name</div>
    <div class="table-header-cell">Value</div>
  </div>
  <div class="table-empty">No results found.</div>
  <div class="table-footer" data-align="between">
    <span>0 items</span>
    <nav class="pagination"><!-- pagination here --></nav>
  </div>
</div>

Ghost variant

html
<div class="table" data-variant="ghost" data-size="lg">
  <div class="table-header">
    <div class="table-header-cell">Label</div>
    <div class="table-header-cell" data-truncate>Long Description</div>
  </div>
  <div class="table-row">
    <div class="table-cell">Alpha</div>
    <div class="table-cell" data-truncate>Very long text that will be truncated with ellipsis...</div>
  </div>
</div>

With Vue

vue
<template>
  <div class="table" :data-variant="variant" :data-size="size" :data-striped="striped || undefined" :data-full="full || undefined">
    <div class="table-header">
      <div
        v-for="col in columns"
        :key="col.key"
        class="table-header-cell"
        :data-align="col.align"
        :data-sortable="col.sortable || undefined"
        :data-sorted="sortState?.key === col.key ? sortState.direction : undefined"
        @click="col.sortable && $emit('sort', col.key)"
      >
        {{ col.label }}
      </div>
    </div>
    <div v-for="row in rows" :key="row.id" class="table-row">
      <div v-for="col in columns" :key="col.key" class="table-cell" :data-align="col.align">
        {{ row[col.key] }}
      </div>
    </div>
    <div v-if="!rows.length" class="table-empty">No data available.</div>
  </div>
</template>

Vibe