All components
line
inset
outline
sm
md
lg
Table
Tabular data with line, inset, and outline treatments across three densities.
| Name | Role | Status |
|---|---|---|
| Ava Chen | Design Engineer | Active |
| Noah Patel | Frontend Developer | Active |
| Mia Torres | Product Designer | Invited |
| Liam Novak | Engineering Manager | Active |
123456789101112131415161718192021222324252627282930313233import { Table } from "#components/ui/ark-ui/table.tsx";
const members = [ ["Ava Chen", "Design Engineer", "Active"], ["Noah Patel", "Frontend Developer", "Active"],];
export function Example() { return ( // variant/size resolve once on Root; every part inherits them via context <Table.Root variant="line" size="md"> <Table.Header> <Table.Row> <Table.ColumnHeader>Name</Table.ColumnHeader> <Table.ColumnHeader>Role</Table.ColumnHeader> <Table.ColumnHeader>Status</Table.ColumnHeader> </Table.Row> </Table.Header> <Table.Body> {members.map(([name, role, status]) => ( <Table.Row key={name}> <Table.Cell>{name}</Table.Cell> <Table.Cell>{role}</Table.Cell> <Table.Cell>{status}</Table.Cell> </Table.Row> ))} </Table.Body> </Table.Root> );}
// composition works per part — e.g. link a cell's content:// <Table.Cell asChild><a href="/members/ava">Ava Chen</a></Table.Cell>Variants
| Name | Role | Status |
|---|---|---|
| Ava Chen | Design Engineer | Active |
| Noah Patel | Frontend Developer | Active |
| Mia Torres | Product Designer | Invited |
| Liam Novak | Engineering Manager | Active |
| Name | Role | Status |
|---|---|---|
| Ava Chen | Design Engineer | Active |
| Noah Patel | Frontend Developer | Active |
| Mia Torres | Product Designer | Invited |
| Liam Novak | Engineering Manager | Active |
| Name | Role | Status |
|---|---|---|
| Ava Chen | Design Engineer | Active |
| Noah Patel | Frontend Developer | Active |
| Mia Torres | Product Designer | Invited |
| Liam Novak | Engineering Manager | Active |
Sizes
| Name | Role | Status |
|---|---|---|
| Ava Chen | Design Engineer | Active |
| Noah Patel | Frontend Developer | Active |
| Mia Torres | Product Designer | Invited |
| Liam Novak | Engineering Manager | Active |
| Name | Role | Status |
|---|---|---|
| Ava Chen | Design Engineer | Active |
| Noah Patel | Frontend Developer | Active |
| Mia Torres | Product Designer | Invited |
| Liam Novak | Engineering Manager | Active |
| Name | Role | Status |
|---|---|---|
| Ava Chen | Design Engineer | Active |
| Noah Patel | Frontend Developer | Active |
| Mia Torres | Product Designer | Invited |
| Liam Novak | Engineering Manager | Active |
Install
1npx stylexblocks add tableSource
components/ui/ark-ui/table.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260import * as stylex from "@stylexjs/stylex";import { createSlotRecipeContext } from "#lib/ark-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe, fromMap } from "#lib/recipe-props.ts";import { colors } from '@stylexblocks/ui/tokens/colors.stylex.ts'import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { fontWeights } from '@stylexblocks/ui/tokens/font-weights.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'
const tableRoot = stylex.create({ base: { fontVariantNumeric: 'lining-nums tabular-nums', borderCollapse: 'collapse', width: sizes.full, textAlign: 'start', verticalAlign: 'top', },})
const tableRootSizes = stylex.create({ sm: { '--table-cell-padding-x': spacing._1, '--control-height': sizes.controlSm, fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, md: { '--table-cell-padding-x': spacing._2, '--control-height': sizes.controlMd, fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, lg: { '--table-cell-padding-x': spacing._4, '--control-height': sizes.controlLg, fontSize: fontSizes.md, lineHeight: lineHeights.md, },})
const tableRootVariants = stylex.create({ inset: { marginInline: 'calc(0px - var(--table-cell-padding-x))', width: 'calc(100% + var(--table-cell-padding-x) + var(--table-cell-padding-x))', }, outline: { boxShadow: '0 0 0 1px var(--sui-colors-border)', overflow: 'hidden', },})
const tableRow = stylex.create({ base: {},})
const tableRowVariants = stylex.create({ line: { backgroundColor: 'transparent', }, inset: { backgroundColor: 'transparent', }, outline: {},})
const tableRowStripeds = stylex.create({ true: {},})
const tableCell = stylex.create({ base: { textAlign: 'start', alignItems: 'center', },})
const tableCellSizes = stylex.create({ sm: { paddingInline: 'var(--table-cell-padding-x)', paddingBlock: spacing._1, }, md: { paddingInline: 'var(--table-cell-padding-x)', paddingBlock: spacing._2, }, lg: { paddingInline: 'var(--table-cell-padding-x)', paddingBlock: spacing._3, },})
const tableCellVariants = stylex.create({ line: { borderBottomWidth: '1px', }, inset: { borderBottomWidth: '1px', },})
const tableCellShowColumnBorders = stylex.create({ true: {},})
const tableColumnHeader = stylex.create({ base: { fontWeight: fontWeights.medium, textAlign: 'start', color: semanticColors.fg, },})
const tableColumnHeaderSizes = stylex.create({ sm: { paddingInline: 'var(--table-cell-padding-x)', paddingBlock: spacing._1, }, md: { paddingInline: 'var(--table-cell-padding-x)', paddingBlock: spacing._2, }, lg: { paddingInline: 'var(--table-cell-padding-x)', paddingBlock: spacing._3, },})
const tableColumnHeaderVariants = stylex.create({ line: { borderBottomWidth: '1px', }, inset: { borderBottomWidth: '1px', }, outline: { borderBottomWidth: '1px', },})
const tableColumnHeaderShowColumnBorders = stylex.create({ true: {},})
const tableCaption = stylex.create({ base: { fontWeight: fontWeights.medium, fontSize: fontSizes.xs, lineHeight: lineHeights.xs, },})
const tableFooter = stylex.create({ base: { fontWeight: fontWeights.medium, },})
const tableFooterVariants = stylex.create({ outline: { borderTopWidth: '1px', },})
const tableBody = stylex.create({ base: {},})
const tableBodyInteractives = stylex.create({ true: {},})
const tableHeader = stylex.create({ base: {},})
const tableHeaderVariants = stylex.create({ outline: { backgroundColor: semanticColors.bgMuted, },})
const tableHeaderStickyHeaders = stylex.create({ true: {},})
export type TableInteractive = keyof typeof tableBodyInteractives
export type TableStickyHeader = keyof typeof tableHeaderStickyHeaders
export type TableStriped = keyof typeof tableRowStripeds
export type TableShowColumnBorder = keyof typeof tableCellShowColumnBorders
export type TableVariant = keyof typeof tableRootVariants
export type TableSize = keyof typeof tableRootSizes
// defineSlotRecipe validates the shape at compile time: default values must// exist in the axis maps, and slot keys must be `styles` or a declared axisconst tableSlotRecipe = defineSlotRecipe({ slots: { root: { styles: tableRoot, sizes: tableRootSizes, variants: tableRootVariants, }, header: { styles: tableHeader, variants: tableHeaderVariants }, body: { styles: tableBody }, row: { styles: tableRow, variants: tableRowVariants }, columnHeader: { styles: tableColumnHeader, sizes: tableColumnHeaderSizes, variants: tableColumnHeaderVariants, }, cell: { styles: tableCell, sizes: tableCellSizes, variants: tableCellVariants }, footer: { styles: tableFooter, variants: tableFooterVariants }, caption: { styles: tableCaption }, }, defaultVariants: { variant: "line", size: "md" },});
/** * Table — recipe and component colocated in one file. `<Table.Root variant * size>` resolves the recipe once and every part inherits it through * context, rendering real `<table>` semantics; each part composes via * `asChild`. Sparse variant maps (root styles only inset/outline, header and * footer only outline) resolve through explicit resolvers. */const { withProvider, withContext } = createSlotRecipeContext(tableSlotRecipe);
export const Table = { Root: withProvider( "table", (v) => [tableRoot.base, tableRootSizes[v.size], fromMap(tableRootVariants, v.variant)], "TableRoot", ), Header: withContext( "thead", (v) => [tableHeader.base, v.variant === "outline" && tableHeaderVariants.outline], "TableHeader", ), Body: withContext("tbody", "body", "TableBody"), Row: withContext("tr", "row", "TableRow"), ColumnHeader: withContext("th", "columnHeader", "TableColumnHeader"), Cell: withContext( "td", (v) => [tableCell.base, tableCellSizes[v.size], fromMap(tableCellVariants, v.variant)], "TableCell", ), Footer: withContext( "tfoot", (v) => [tableFooter.base, v.variant === "outline" && tableFooterVariants.outline], "TableFooter", ), Caption: withContext("caption", "caption", "TableCaption"),};