Skip to content

FreeIn reviewv1.0.0

Data Table

A data table with a caption, aligned numbers and status pills, scrolling sideways inside its frame.
npx shadcn@latest add @vibebox/data-table

Controls

<DataTable />

About

A table that is right before it is clever: caption, scope, alignment and a scroll region. Add sorting or paging in your own layer.

Use it when

  • A signed-in area lists records.

Skip it when

  • Rows number in the thousands: virtualise.

Install

  1. 1Add the VibeBox registry to components.json:
    {
      "registries": {
        "@vibebox": "https://motion.vibeboxph.com/r/{name}.json"
      }
    }
  2. 2Add the component with the shadcn CLI:
    npx shadcn@latest add @vibebox/data-table
  3. 3Use it:
    import { DataTable } from "@/components/vibebox/data-table";
    
    <DataTable accent="#7c5cff" />

AI prompt

Everything an AI coding tool needs to rebuild this component the way it looks here: colours, fonts, the art it draws, props and rules.

Read the prompt
Build the VibeBox Motion component "Data Table" (Data) in a Next.js App Router project with Tailwind CSS v4.

What it is
- A data table with a caption, aligned numbers and status pills, scrolling sideways inside its frame.
- A semantic table for records: a visually hidden caption, header cells with `scope`, right-aligned tabular numbers and status pills (paid in the accent, pending quiet, late in the destructive colour). On narrow screens the table scrolls inside a focusable region instead of breaking the page.

Install
- `npx shadcn@latest add @vibebox/data-table`
- Registry: add `"@vibebox": "https://motion.vibeboxph.com/r/{name}.json"` to components.json, then run the command. The source lands in `components/vibebox/data-table` and is yours to edit.

Visual spec
- Colours (the VibeBox spectrum, the same in light and dark):
    - --spectrum-1: #00c5fc (cyan)
    - --spectrum-2: #1d6ef0 (blue)
    - --spectrum-3: #5225ed (indigo)
    - --spectrum-4: #e434dc (magenta)
    - --spectrum-5: #fd3aa9 (pink)
    - --spectrum-6: #f98472 (coral)
    - --spectrum-7: #fcbe72 (amber)
- Ink surface (dark): background #05070c, card #0a0d15, text #f5f6fa, muted text #a3abbd, border rgba(255,255,255,0.08).
- Paper surface (light): background #f3f0ea, card #fbfaf7, text #0c0e14, muted text #4a4f5c, border rgba(12,14,20,0.12).
- Fonts:
    - Display and body: Figtree (400, 500, 600, 700), falling back to ui-sans-serif, system-ui.
    - Emphasis: Fraunces italic, for one or two words of a heading.
    - Labels and code: JetBrains Mono (400, 500), uppercase labels with 0.14em tracking.
- Shape: radius 8 to 12px on cards and panels; pills may be fully round. Hairline borders, soft glow instead of heavy shadow.
- Motion: animate transform and opacity only; fast ease-out (cubic-bezier(0.16, 1, 0.3, 1)), 140ms for micro-interactions, 320 to 520ms for reveals.

Art and media
- Built from HTML, CSS and inline SVG only; no image files or video.

Props
- `caption`: `string` (default "Recent invoices"). The table's caption (also its region name).
- `columns`: `{ key: string; label: string; align?: "start" | "end" }[]`. The columns.
- `rows`: `Record<string, string>[]`. The rows; a `status` of paid, pending or late becomes a pill.
- `accent`: `string` (default "blue"). The accent colour: blue, violet, emerald, amber, rose, or any CSS colour. It sets --app-accent on the root, so a parent can set it once for a whole screen.
- `className`: `string`. Merged with cn(); give it a width.

Behaviour and access
- Reduced motion: Nothing animates on its own.
- Keyboard: The frame is focusable and scrolls with the arrow keys.
- Screen readers: Semantic elements with accessible names; decorative marks are hidden.
- Touch: Targets are 36 px or larger; nothing depends on hover.
- Budget: about 0 KB of JavaScript, WebGL none, GPU none. It pauses while off screen.

Theming
Everything reads `--app-accent` (set by the `accent` prop, or by a parent) and the mode's tokens, so light and dark and any brand colour work without code changes. Pass a named accent or any CSS colour; Launchpad sets it once per app from the client's theme.

Usage
```tsx
import { DataTable } from "@/components/vibebox/data-table";

<DataTable accent="#7c5cff" />
```

Notes
Add the VibeBox Motion Data Table with `npx shadcn@latest add @vibebox/data-table`. Map your records to string cells. Set the brand colour once with `--app-accent` on a parent (or the `accent` prop). It follows light and dark through the tokens; keep the licence header.

Rules
- Keep the licence header on every installed file.
- Original code only: do not copy components from other libraries, and do not add GSAP.
- Use tokens for colour, type and radius; no hard-coded brand values.
- Server Components by default; `"use client"` only for the island.

Code

1 files
components/vibebox/data-table/index.tsx
/**
 * VibeBox Motion · Data Table
 * Copyright (c) 2026 VibeBox. Licensed under the VibeBox Motion Free License:
 * https://motion.vibeboxph.com/license
 * Use it in unlimited personal, commercial and client projects.
 * Do not sell or redistribute it as a component, kit, template or builder.
 */
import { cn } from "@/lib/utils";
import { accentStyle } from "@/lib/vibebox/app-accent";

export type Column = { key: string; label: string; align?: "start" | "end" };
export type Row = Record<string, string> & { status?: "paid" | "pending" | "late" };
export type DataTableProps = { caption?: string; columns?: Column[]; rows?: Row[]; accent?: string; className?: string };

const COLUMNS: Column[] = [
  { key: "client", label: "Client" },
  { key: "service", label: "Service" },
  { key: "date", label: "Date" },
  { key: "amount", label: "Amount", align: "end" },
  { key: "status", label: "Status" },
];
const ROWS: Row[] = [
  { client: "Maria Santos", service: "Family portrait", date: "Oct 12", amount: "₱4,500", status: "paid" },
  { client: "Daniel Cruz", service: "Event, half day", date: "Oct 14", amount: "₱12,000", status: "pending" },
  { client: "Lia Ramos", service: "Prints and album", date: "Oct 09", amount: "₱6,800", status: "late" },
  { client: "Paolo Reyes", service: "Brand photos", date: "Oct 07", amount: "₱9,200", status: "paid" },
];

/** A table with a caption, right-aligned numbers and status pills; scrolls sideways inside its frame on narrow screens. */
export function DataTable({ caption = "Recent invoices", columns = COLUMNS, rows = ROWS, accent, className }: DataTableProps) {
  return (
    <div data-vb="data-table" style={accentStyle(accent)} className={cn("overflow-x-auto rounded-xl border border-border bg-card", className)} tabIndex={0} role="region" aria-label={caption}>
      <table className="w-full min-w-[34rem] border-collapse text-small">
        <caption className="sr-only">{caption}</caption>
        <thead>
          <tr className="border-b border-border text-left text-caption text-muted-foreground">
            {columns.map((c) => (
              <th key={c.key} scope="col" className={cn("px-4 py-2.5 font-medium", c.align === "end" && "text-right")}>{c.label}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((row, i) => (
            <tr key={i} className="border-b border-border last:border-0 hover:bg-accent/60">
              {columns.map((c) => (
                <td key={c.key} className={cn("px-4 py-2.5", c.align === "end" && "text-right tabular-nums")}>
                  {c.key === "status" && row.status ? (
                    <span className={cn("inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-caption font-medium", row.status === "paid" && "bg-[color-mix(in_oklab,var(--app-accent)_16%,transparent)] text-[var(--app-accent)]", row.status === "pending" && "bg-muted text-muted-foreground", row.status === "late" && "bg-[color-mix(in_oklab,var(--destructive)_16%,transparent)] text-destructive")}>{row.status}</span>
                  ) : (
                    row[c.key]
                  )}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Props

PropTypeDefaultDescription
captionstring"Recent invoices"The table's caption (also its region name).
columns{ key: string; label: string; align?: "start" | "end" }[]—The columns.
rowsRecord<string, string>[]—The rows; a status of paid, pending or late becomes a pill.
accentstring"blue"The accent colour: blue, violet, emerald, amber, rose, or any CSS colour. It sets --app-accent on the root, so a parent can set it once for a whole screen.
classNamestring—Merged with cn(); give it a width.

Accessibility and motion

Keyboard
The frame is focusable and scrolls with the arrow keys.
Screen readers
Semantic elements with accessible names; decorative marks are hidden.
Touch
Targets are 36 px or larger; nothing depends on hover.
Reduced motion
Nothing animates on its own.

Edge cases and limits

  • Statuses other than paid, pending and late render as text.
  • A smooth rising area chart in a blue gradient with faint gridlines and month labels

    A smooth area chart in your accent with gridlines, a marked last point and axis labels, in plain SVG.

    Data · Server

  • A list of people with accent monograms, short activity sentences and times

    A list of who did what and when, with initials in the accent.

    Data · Server

  • Three stat cards with a label, a large number, a green change and a small area sparkline

    Headline number cards with a change and a sparkline in the accent.

    Data · Server

↑ ↓ to move↵ to openesc to close