Inkbox

Component

Table

The console's table: a lilac head with muted labels, a separate white body with Shadow/S and 64px rows, and a lilac foot.

npx -y github:anwarlemu/inkbox-brand-cli#release add table
Preview
Email addressAgentWebhooksStorageCreated
acme@inkboxmail.com@acmeNone0%Jul 27, 2026
support@inkboxmail.com@support-desk24%Jul 29, 2026
ops@inkboxmail.com@ops-runner112%Aug 2, 2026
With footer
Usage this billing period.
ChannelMessagesCost
Email1,204$12.04
Phone86$4.30
iMessage412$8.24
Total$24.58

Usage

Use for rows of records people scan and act on (mailboxes, agents and lines, webhooks). Put labels in TableHeader, rows in TableBody, totals or Pagination with bar in a TableFooter or under the table, and row actions as ghost icon buttons in the last cell. Keep cells to one line; truncate long values.

Avoid

  • Zebra striping
  • Vertical rules between cells
  • A dark or purple head row
  • Bold cell text

Exports: Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow. Read the examples for composition and props.

Example

examples/table.tsx
import { ArrowsClockwiseIcon, PencilSimpleIcon } from "@phosphor-icons/react/ssr";​import { Button } from "@/components/ui/button";import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination";import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@/components/ui/table";​const MAILBOXES = [  { address: "acme@inkboxmail.com", agent: "@acme", webhooks: "None", storage: "0%", created: "Jul 27, 2026" },  { address: "support@inkboxmail.com", agent: "@support-desk", webhooks: "2", storage: "4%", created: "Jul 29, 2026" },  { address: "ops@inkboxmail.com", agent: "@ops-runner", webhooks: "1", storage: "12%", created: "Aug 2, 2026" },];​export function Preview() {  return (    <div className="grid gap-0">      <Table>        <TableHeader>          <TableRow>            <TableHead>Email address</TableHead>            <TableHead>Agent</TableHead>            <TableHead>Webhooks</TableHead>            <TableHead>Storage</TableHead>            <TableHead>Created</TableHead>            <TableHead className="w-12 text-right">              <Button variant="ghost" size="icon" aria-label="Refresh mailboxes">                <ArrowsClockwiseIcon />              </Button>            </TableHead>          </TableRow>        </TableHeader>        <TableBody>          {MAILBOXES.map((m) => (            <TableRow key={m.address}>              <TableCell>{m.address}</TableCell>              <TableCell className="text-rose">{m.agent}</TableCell>              <TableCell>{m.webhooks}</TableCell>              <TableCell>{m.storage}</TableCell>              <TableCell>{m.created}</TableCell>              <TableCell className="text-right">                <Button variant="ghost" size="icon" aria-label={`Edit ${m.address}`}>                  <PencilSimpleIcon weight="fill" />                </Button>              </TableCell>            </TableRow>          ))}        </TableBody>      </Table>      <Pagination bar className="mt-1">        <PaginationContent>          <PaginationItem>            <PaginationPrevious href="#" aria-disabled />          </PaginationItem>          <PaginationItem>            <PaginationLink href="#" isActive>              1            </PaginationLink>          </PaginationItem>          <PaginationItem>            <PaginationLink href="#">2</PaginationLink>          </PaginationItem>          <PaginationItem>            <PaginationNext href="#" />          </PaginationItem>        </PaginationContent>      </Pagination>    </div>  );}​export function WithFooter() {  return (    <Table>      <TableCaption>Usage this billing period.</TableCaption>      <TableHeader>        <TableRow>          <TableHead>Channel</TableHead>          <TableHead>Messages</TableHead>          <TableHead className="text-right">Cost</TableHead>        </TableRow>      </TableHeader>      <TableBody>        <TableRow>          <TableCell>Email</TableCell>          <TableCell>1,204</TableCell>          <TableCell className="text-right">$12.04</TableCell>        </TableRow>        <TableRow data-state="selected">          <TableCell>Phone</TableCell>          <TableCell>86</TableCell>          <TableCell className="text-right">$4.30</TableCell>        </TableRow>        <TableRow>          <TableCell>iMessage</TableCell>          <TableCell>412</TableCell>          <TableCell className="text-right">$8.24</TableCell>        </TableRow>      </TableBody>      <TableFooter>        <TableRow>          <TableCell colSpan={2}>Total</TableCell>          <TableCell className="text-right">$24.58</TableCell>        </TableRow>      </TableFooter>    </Table>  );}

Source

components/ui/table.tsx
import * as React from "react";​import { cn } from "@/lib/utils";​// The console's table (Figma 427:2715, Email: Mailboxes; iMessage: Agents and lines). Three separate panels: a 48px// lilac head with 10px corners and 14px muted labels; 8px below it a white body with 8px corners, the Shadow/S ring// and its short drop, 64px rows of 14px ink ruled by Line/S Bottom; and a lilac foot 8px below (the pagination bar or// totals). Cells keep 16px sides. The table scrolls sideways inside its frame on a narrow screen.function Table({ className, ...props }: React.ComponentProps<"table">) {  return (    <div data-slot="table-container" className="relative w-full overflow-x-auto px-px pt-px pb-1">      <table data-slot="table" className={cn("w-full caption-bottom border-separate border-spacing-0 font-sans text-body leading-tight text-fg", className)} {...props} />    </div>  );}​function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {  return (    <thead      data-slot="table-header"      className={cn(        "after:table-row after:h-2 after:content-[''] [&>tr>th]:bg-subtle [&>tr>th:first-child]:rounded-l-panel [&>tr>th:last-child]:rounded-r-panel",        className,      )}      {...props}    />  );}​function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {  return (    <tbody      data-slot="table-body"      className={cn(        "[&>tr>td]:bg-surface [&>tr:not(:last-child)>td]:border-b [&>tr:not(:last-child)>td]:border-b-line-row [&>tr>td:first-child]:border-l [&>tr>td:first-child]:border-l-line-ring [&>tr>td:last-child]:border-r [&>tr>td:last-child]:border-r-line-ring [&>tr:first-child>td]:border-t [&>tr:first-child>td]:border-t-line-ring [&>tr:first-child>td:first-child]:rounded-tl-control [&>tr:first-child>td:last-child]:rounded-tr-control [&>tr:last-child>td]:border-b [&>tr:last-child>td]:border-b-line-ring [&>tr:last-child]:shadow-[0_2px_4px_-1px_var(--ds-color-shadow-drop)] [&>tr:last-child]:[clip-path:inset(0_-8px_-8px_-8px_round_0_0_8px_8px)] [&>tr:last-child>td:first-child]:rounded-bl-control [&>tr:last-child>td:last-child]:rounded-br-control",        className,      )}      {...props}    />  );}​function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {  return (    <tfoot      data-slot="table-footer"      className={cn(        "before:table-row before:h-2 before:content-[''] [&>tr>td]:h-10.5 [&>tr>td]:bg-subtle [&>tr>td]:font-medium [&>tr>td:first-child]:rounded-l-panel [&>tr>td:last-child]:rounded-r-panel [&>tr>th]:bg-subtle",        className,      )}      {...props}    />  );}​function TableRow({ className, ...props }: React.ComponentProps<"tr">) {  return (    <tr      data-slot="table-row"      className={cn("transition-colors duration-150 ease-out hover:[&>td]:bg-subtle/60 data-[state=selected]:[&>td]:bg-subtle motion-reduce:transition-none", className)}      {...props}    />  );}​function TableHead({ className, ...props }: React.ComponentProps<"th">) {  return (    <th      data-slot="table-head"      className={cn("h-12 px-4 text-left align-middle text-body leading-tight font-normal whitespace-nowrap text-fg-muted [&:has([role=checkbox])]:w-10 [&:has([role=checkbox])]:pr-0", className)}      {...props}    />  );}​function TableCell({ className, ...props }: React.ComponentProps<"td">) {  return (    <td      data-slot="table-cell"      className={cn("h-16 px-4 align-middle whitespace-nowrap transition-colors duration-150 ease-out motion-reduce:transition-none [&:has([role=checkbox])]:w-10 [&:has([role=checkbox])]:pr-0", className)}      {...props}    />  );}​function TableCaption({ className, ...props }: React.ComponentProps<"caption">) {  return <caption data-slot="table-caption" className={cn("mt-3 text-body-sm leading-body text-fg-muted", className)} {...props} />;}​export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow };

Reference: Figma 427:2715: Email (Mailboxes table) and iMessage (Agents and lines table).