Components
Table
A responsive table with Header/Body/Footer/Row/Head/Cell/Caption parts. Purely presentational.
Installation
npx kosmesis add tablepnpm dlx kosmesis add tableyarn dlx kosmesis add tablebunx kosmesis add tableCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { cn } from "@/lib/utils";export interface TableSlotProps { class?: string; id?: string; children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Table extends StatelessComponent<TableSlotProps> { render() { const { class: cls, id, children } = this.props; return ( <div data-slot="table-container" class="relative w-full overflow-x-auto"> <table id={id} data-slot="table" class={cn("w-full caption-bottom text-sm", cls)}> {children} </table> </div> ); }}@Component()export class TableHeader extends StatelessComponent<TableSlotProps> { render() { const { class: cls, id, children } = this.props; return ( <thead id={id} data-slot="table-header" class={cn("[&_tr]:border-b", cls)}> {children} </thead> ); }}@Component()export class TableBody extends StatelessComponent<TableSlotProps> { render() { const { class: cls, id, children } = this.props; return ( <tbody id={id} data-slot="table-body" class={cn("[&_tr:last-child]:border-0", cls)}> {children} </tbody> ); }}@Component()export class TableFooter extends StatelessComponent<TableSlotProps> { render() { const { class: cls, id, children } = this.props; return ( <tfoot id={id} data-slot="table-footer" class={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", cls)} > {children} </tfoot> ); }}@Component()export class TableRow extends StatelessComponent<TableSlotProps> { render() { const { class: cls, id, children } = this.props; return ( <tr id={id} data-slot="table-row" class={cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", cls)} > {children} </tr> ); }}export interface TableHeadProps extends TableSlotProps { onClick?: (event: MouseEvent) => void;}@Component()export class TableHead extends StatelessComponent<TableHeadProps> { render() { const { class: cls, id, children, onClick } = this.props; return ( <th id={id} data-slot="table-head" class={cn( "h-10 whitespace-nowrap px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0", cls, )} onClick={onClick} > {children} </th> ); }}@Component()export class TableCell extends StatelessComponent<TableSlotProps> { render() { const { class: cls, id, children } = this.props; return ( <td id={id} data-slot="table-cell" class={cn("whitespace-nowrap p-2 align-middle [&:has([role=checkbox])]:pr-0", cls)} > {children} </td> ); }}@Component()export class TableCaption extends StatelessComponent<TableSlotProps> { render() { const { class: cls, id, children } = this.props; return ( <caption id={id} data-slot="table-caption" class={cn("mt-4 text-sm text-muted-foreground", cls)}> {children} </caption> ); }}Install the following dependencies:
npm install @praxisjs/csspnpm add @praxisjs/cssyarn add @praxisjs/cssbun add @praxisjs/cssCopy and paste the following code into your project.
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class TableStyles extends Stylesheet { $container = this.css({ position: "relative", width: "100%", overflowX: "auto" }); $table = this.css({ width: "100%", captionSide: "bottom", fontSize: "0.875rem" }); $header = this.css({}).on("& tr", { borderBottom: `1px solid ${t.border}` }); $body = this.css({}).on("& tr:last-child", { borderBottom: "none" }); $footer = this.css({ borderTop: `1px solid ${t.border}`, backgroundColor: `color-mix(in oklab, ${t.muted} 50%, transparent)`, fontWeight: 500 }).on( "& > tr:last-child", { borderBottom: "none" }, ); $row = this.css({ borderBottom: `1px solid ${t.border}`, transition: "background-color 120ms ease" }) .hover({ backgroundColor: `color-mix(in oklab, ${t.muted} 50%, transparent)` }) .on('&[data-state="selected"]', { backgroundColor: t.muted }); $head = this.css({ height: "2.5rem", whiteSpace: "nowrap", padding: "0 0.5rem", textAlign: "left", verticalAlign: "middle", fontWeight: 500, color: t.mutedForeground, }).has('[role="checkbox"]', { paddingRight: "0" }); $cell = this.css({ whiteSpace: "nowrap", padding: "0.5rem", verticalAlign: "middle" }).has('[role="checkbox"]', { paddingRight: "0", }); $caption = this.css({ marginTop: "1rem", fontSize: "0.875rem", color: t.mutedForeground });}export interface TableSlotProps { class?: string; id?: string; children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Table extends StatelessComponent<TableSlotProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children } = this.props; return ( <div data-slot="table-container" class={this.$s.$container}> <table id={id} data-slot="table" class={cx(this.$s.$table, cls)}> {children} </table> </div> ); }}@Component()export class TableHeader extends StatelessComponent<TableSlotProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children } = this.props; return ( <thead id={id} data-slot="table-header" class={cx(this.$s.$header, cls)}> {children} </thead> ); }}@Component()export class TableBody extends StatelessComponent<TableSlotProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children } = this.props; return ( <tbody id={id} data-slot="table-body" class={cx(this.$s.$body, cls)}> {children} </tbody> ); }}@Component()export class TableFooter extends StatelessComponent<TableSlotProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children } = this.props; return ( <tfoot id={id} data-slot="table-footer" class={cx(this.$s.$footer, cls)}> {children} </tfoot> ); }}@Component()export class TableRow extends StatelessComponent<TableSlotProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children } = this.props; return ( <tr id={id} data-slot="table-row" class={cx(this.$s.$row, cls)}> {children} </tr> ); }}export interface TableHeadProps extends TableSlotProps { onClick?: (event: MouseEvent) => void;}@Component()export class TableHead extends StatelessComponent<TableHeadProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children, onClick } = this.props; return ( <th id={id} data-slot="table-head" class={cx(this.$s.$head, cls)} onClick={onClick}> {children} </th> ); }}@Component()export class TableCell extends StatelessComponent<TableSlotProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children } = this.props; return ( <td id={id} data-slot="table-cell" class={cx(this.$s.$cell, cls)}> {children} </td> ); }}@Component()export class TableCaption extends StatelessComponent<TableSlotProps> { @Styled(TableStyles) $s!: TableStyles; render() { const { class: cls, id, children } = this.props; return ( <caption id={id} data-slot="table-caption" class={cx(this.$s.$caption, cls)}> {children} </caption> ); }}Examples
Usage
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
<Table>
<TableCaption>A list of recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead class="text-right">Amount</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>INV001</TableCell>
<TableCell>Paid</TableCell>
<TableCell class="text-right">$250.00</TableCell>
</TableRow>
</TableBody>
</Table>For sortable/paginated tables, see Data Table.