Getting Started
App Providerglobal.d.tsThemesTypes
cn(...args)Text ParserUnits Converters
AnchorAvatarBreadcrumbBurgerButtonCardCarouselCheckerCodeColor PickerCommandConfettiCopyButtonDouble Helix WordsFloating IndicatorGroupHighlight TextIndicatorInputKbdLabelLoaderPaginationPassword RequirementPolymorphic SlotProgressProseRatingRunning AreaScroll AreaSheetsSkeletonSliderStackSvgTableTabsTextareaTimelineTimesToasterTooltipTyping WordsTypography
useClickOutsideuseClipboarduseDeviceInfouseDialoguseDidUpdateuseDirectionuseDisclosureuseDocumentTitleuseDocumentVisibilityuseElementInfouseEyeDropperuseFetchuseFullscreenuseGeoLocationuseHotkeysuseHoveruseIduseImagePopupuseInputStateuseIntersectionuseIntervaluseIsomorphicEffectuseListStateuseLocalStorageuseMeasureScrollbaruseMediaQueryuseMergedRefuseMouseuseMoveuseMutationObserveruseNetworkuseOpenStateuseOrientationuseOSusePaginationusePWAInstalleruseRandomColorsuseReducedMotionuseReloaduseResizeObserveruseScrollIntoViewuseStateHistoryuseTimeoutuseTouchuseTriggeruseUncontrolleduseValidatedStateuseViewportSizeuseWindowEventuseWindowScroll
Docs
Web
Components
Table
Svg

An enhanced SVG wrapper with built-in styling, perfect for custom inline icons.

Tabs

A tab-based interface for switching between different content panels.


Edit this page on GitHub
  • Started
  • Utilities
  • Configuration
  • Components
  • Hooks
  • Examples
  • Github
  • Contributing
⌘+J

© 2025 oeri rights MIT


Designed in Earth-616

Built by oeri

Table

A component for rendering data in rows and columns with customizable headers, cells, and responsive behavior.

Usage

Basic usage example to quickly see how the Table works.

{ "name": "string", "symbol": "string", "position": "number", "mass": "number" }
NameSymbolPositionMass
CarbonC612.011
NitrogenN714.007
YttriumY3988.906
import { Table, TableBody, TableCaption, TableData, TableHead, TableHeader, TableRow } from "@/ui/table";
import { Code } from "@/ui/code";

const data = [
  { name: "Carbon", symbol: "C", position: 6, mass: 12.011 },
  { name: "Nitrogen", symbol: "N", position: 7, mass: 14.007 },
  { name: "Yttrium", symbol: "Y", position: 39, mass: 88.906 },
  { name: "Barium", symbol: "Ba", position: 56, mass: 137.33 },
  { name: "Cerium", symbol: "Ce", position: 58, mass: 140.12 },
  { name: "Sodium", symbol: "Na", position: 11, mass: 22.99 }
];

// Independent Components - Flat Component Structure
export function TableDemo() {
  return (
    <Table className="flex max-w-full flex-col items-center justify-center">
      <TableCaption className="px-2 text-left">
        <Code samp={data} className="w-full" />
      </TableCaption>

      <TableHeader>
        <TableRow>
          <TableHead>Name</TableHead>
          <TableHead>Symbol</TableHead>
          <TableHead>Position</TableHead>
          <TableHead>Mass</TableHead>
        </TableRow>
      </TableHeader>

      <TableBody>
        {data.slice(0, 3).map(i => (
          <TableRow key={i.name}>
            <TableData>{i.name}</TableData>
            <TableData>{i.symbol}</TableData>
            <TableData>{i.position}</TableData>
            <TableData>{i.mass}</TableData>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}

Properties

Interactive configurator to explore customization options for the Table component.

Some elements from periodic table
Element nameSymbolPositionAtomic massDescription
  1
Na1122.99A soft metal that reacts vigorously with water.
  2
Mg1224.305A metal used in lightweight alloys.
  3
Al1326.982A lightweight, corrosion-resistant metal.
  4
Si1428.085A key component in semiconductors.
  5
P1530.974Essential for DNA and energy transfer in cells.
  6
S1632.06A yellow, nonmetallic element used in fertilizers.
  7
Cl1735.45A reactive halogen used for water purification.
  8
Ar1839.948A noble gas used in light bulbs and welding.
  9
K1939.098An essential element for human health.
  10
Ca2040.078A metal crucial for bones and teeth.
  11
Fe2655.845A key element in hemoglobin, the oxygen-carrying molecule in blood.
  12
Cu2963.546A conductor of electricity used in wiring.
  13
Zn3065.38An essential trace element for humans.
  14
Y3988.906A rare-earth metal used in lasers and superconductors.
  15
Ba56137.33A metal used in medical imaging.
  16
Ce58140.12A rare-earth element used in catalytic converters.
  17
H11.008The lightest element and most abundant chemical substance in the universe.
  18
He24.0026A noble gas used in balloons and cooling systems.
  19
Li36.94A soft, silvery metal used in rechargeable batteries.
  20
Be49.0122A hard metal used in aerospace materials.
  21
B510.81An element used in glass and detergents.
  22
C612.011A versatile element, the basis for organic life.
  23
N714.007Makes up 78% of Earth's atmosphere.
  24
O815.999Essential for respiration in most living organisms.
  25
F918.998A highly reactive gas used in toothpaste.
  26
Ne1020.18A noble gas used in neon signs and lighting.
"use client";
import React from "react";
import { Input } from "@/ui/input";
import { Label } from "@/ui/label";
import { Stack } from "@/ui/stack";
import { Button } from "@/ui/button";
import { Table, dataRenderer, type DataTableProps } from "@/ui/table";
function name(value: string | number) {
  return String(value).toLowerCase().replace(/\s/g, "-");
}
function ConfiguratorDemo() {
  const [data, setData] = React.useState(somePeriodic);
  const [selectedRows, setSelectedRows] = React.useState<string[]>([]);
  const allRowPositions = React.useMemo(() => data.map(item => item.name), [data]);
  const isAllSelected = selectedRows.length === allRowPositions.length;
  const isSomeSelected = selectedRows.length > 0 && !isAllSelected;
  const handleSelectAll = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (event.currentTarget.checked) {
      setSelectedRows(allRowPositions); // Select all rows
    } else {
      setSelectedRows([]); // Deselect all rows
    }
  };
  const handleDelete = () => {
    // Filter out rows that are not selected
    const newData = data.filter(item => !selectedRows.includes(item.name));
    setData(newData); // Update data
    setSelectedRows([]); // Clear selected rows
  };
  const tableData: DataTableProps = {
    caption: (
      <div className="grid grid-flow-row items-center justify-center">
        <span>{data.length ? "Some elements from periodic table" : "Empty"}</span>
      </div>
    ),
    head: [
      <Label key={"select-all"} htmlFor="select-all" aria-label="select-all" className="mt-1.5 h-[17px] cursor-pointer">
        <Input
          type="checkbox"
          id="select-all"
          name="select-all"
          aria-label="Select All"
          checked={data.length > 0 && isAllSelected}
          indeterminate={isSomeSelected}
          onChange={handleSelectAll}
        />
        &nbsp;&nbsp;All
      </Label>,
      "Element name",
      "Symbol",
      "Position",
      "Atomic mass",
      "Description"
    ],
    body: dataRenderer(data, [
      (item, index) => (
        <div className="flex flex-row items-center justify-start gap-1">
          <Input
            type="checkbox"
            id={name(item.name)}
            name={name(item.name)}
            aria-label="Select row"
            checked={selectedRows.includes(item.name)}
            onChange={event => setSelectedRows(event.currentTarget.checked ? [...selectedRows, item.name] : selectedRows.filter(name => name !== item.name))}
          />
          &nbsp;&nbsp;{index + 1}
        </div>
      ),
      item => (
        <Label htmlFor={name(item.name)} className="cursor-pointer">
          {item.name}
        </Label>
      ),
      "symbol",
      "position",
      "mass",
      "description"
    ])
  };
  return (
    <Stack>
      <Table
        data={tableData}
        classNames={{ thead: "bg-primitive" }}
        styles={{
          root: {
            height: "29rem"
          },
          "tbody.tr": index => ({
            backgroundColor: selectedRows.includes(data[index].name) && "rgba(0, 123, 255, 0.15)"
          }),
          "thead.tr.th": index => ({
            backgroundColor: index === 2 && "rgb(202 138 4 / var(--tw-bg-opacity, 1))"
          }),
          "tbody.tr.td": index => ({
            backgroundColor: selectedRows.includes(data[index].name) && index === 2 && "rgb(234 179 8 / var(--tw-bg-opacity, 1))"
          })
        }}
      />
      <Button variant="destructive" size="default" className="w-max" disabled={selectedRows.length === 0} onClick={handleDelete}>
        Delete
      </Button>
    </Stack>
  );
}
const somePeriodic = [
  { name: "Sodium", symbol: "Na", position: 11, mass: 22.99, description: "A soft metal that reacts vigorously with water." },
  { name: "Magnesium", symbol: "Mg", position: 12, mass: 24.305, description: "A metal used in lightweight alloys." },
  { name: "Aluminum", symbol: "Al", position: 13, mass: 26.982, description: "A lightweight, corrosion-resistant metal." },
  { name: "Silicon", symbol: "Si", position: 14, mass: 28.085, description: "A key component in semiconductors." },
  { name: "Phosphorus", symbol: "P", position: 15, mass: 30.974, description: "Essential for DNA and energy transfer in cells." },
  { name: "Sulfur", symbol: "S", position: 16, mass: 32.06, description: "A yellow, nonmetallic element used in fertilizers." },
  { name: "Chlorine", symbol: "Cl", position: 17, mass: 35.45, description: "A reactive halogen used for water purification." },
  { name: "Argon", symbol: "Ar", position: 18, mass: 39.948, description: "A noble gas used in light bulbs and welding." },
  { name: "Potassium", symbol: "K", position: 19, mass: 39.098, description: "An essential element for human health." },
  { name: "Calcium", symbol: "Ca", position: 20, mass: 40.078, description: "A metal crucial for bones and teeth." },
  { name: "Iron", symbol: "Fe", position: 26, mass: 55.845, description: "A key element in hemoglobin, the oxygen-carrying molecule in blood." },
  { name: "Copper", symbol: "Cu", position: 29, mass: 63.546, description: "A conductor of electricity used in wiring." },
  { name: "Zinc", symbol: "Zn", position: 30, mass: 65.38, description: "An essential trace element for humans." },
  { name: "Yttrium", symbol: "Y", position: 39, mass: 88.906, description: "A rare-earth metal used in lasers and superconductors." },
  { name: "Barium", symbol: "Ba", position: 56, mass: 137.33, description: "A metal used in medical imaging." },
  { name: "Cerium", symbol: "Ce", position: 58, mass: 140.12, description: "A rare-earth element used in catalytic converters." },
  { name: "Hydrogen", symbol: "H", position: 1, mass: 1.008, description: "The lightest element and most abundant chemical substance in the universe." },
  { name: "Helium", symbol: "He", position: 2, mass: 4.0026, description: "A noble gas used in balloons and cooling systems." },
  { name: "Lithium", symbol: "Li", position: 3, mass: 6.94, description: "A soft, silvery metal used in rechargeable batteries." },
  { name: "Beryllium", symbol: "Be", position: 4, mass: 9.0122, description: "A hard metal used in aerospace materials." },
  { name: "Boron", symbol: "B", position: 5, mass: 10.81, description: "An element used in glass and detergents." },
  { name: "Carbon", symbol: "C", position: 6, mass: 12.011, description: "A versatile element, the basis for organic life." },
  { name: "Nitrogen", symbol: "N", position: 7, mass: 14.007, description: "Makes up 78% of Earth's atmosphere." },
  { name: "Oxygen", symbol: "O", position: 8, mass: 15.999, description: "Essential for respiration in most living organisms." },
  { name: "Fluorine", symbol: "F", position: 9, mass: 18.998, description: "A highly reactive gas used in toothpaste." },
  { name: "Neon", symbol: "Ne", position: 10, mass: 20.18, description: "A noble gas used in neon signs and lighting." }
];

Structure

Decide for yourself how you develop the Table component according to your needs. Currently the Table component can be used with several declarations, you can take advantage of it.

Independent Components

import { Table, TableBody, TableCaption, TableData, TableHead, TableHeader, TableRow } from "@/ui/table";
 
const data = [
  { name: "Sodium", symbol: "Na", position: 11, mass: 22.99 },
  { name: "Magnesium", symbol: "Mg", position: 12, mass: 24.305 }
  // others
];
 
export function TableDemo() {
  return (
    <Table>
      <TableCaption>
        Flat Component Structure
        <br />
        (Independent Components)
      </TableCaption>
      <TableHeader>
        <TableRow>
          <TableHead>Name</TableHead>
          <TableHead>Symbol</TableHead>
          <TableHead>Position</TableHead>
          <TableHead>Mass</TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {data.slice(0, 3).map(i => (
          <TableRow key={i.name}>
            <TableData>{i.name}</TableData>
            <TableData>{i.symbol}</TableData>
            <TableData>{i.position}</TableData>
            <TableData>{i.mass}</TableData>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}

Compound Components

"use client";
import { Table } from "@/ui/table";
 
const data = [
  { name: "Sodium", symbol: "Na", position: 11, mass: 22.99 },
  { name: "Magnesium", symbol: "Mg", position: 12, mass: 24.305 }
  // others
];
 
export function TableDemo() {
  return (
    <Table>
      <Table.Caption>
        Namespaced Component Structure
        <br />
        (Compound Components)
      </Table.Caption>
      <Table.Header>
        <Table.Row>
          <Table.Head>Name</Table.Head>
          <Table.Head>Symbol</Table.Head>
          <Table.Head>Position</Table.Head>
          <Table.Head>Mass</Table.Head>
        </Table.Row>
      </Table.Header>
      <Table.Body>
        {data.slice(3, 6).map(i => (
          <Table.Row key={i.name}>
            <Table.Data>{i.name}</Table.Data>
            <Table.Data>{i.symbol}</Table.Data>
            <Table.Data>{i.position}</Table.Data>
            <Table.Data>{i.mass}</Table.Data>
          </Table.Row>
        ))}
      </Table.Body>
    </Table>
  );
}

Declarative Props API

import { Table, dataRenderer, type DataTableProps } from "@/ui/table";
 
const data = [
  {
    name: "Sodium",
    symbol: "Na",
    position: 11,
    mass: 22.99,
    description: "A soft metal that reacts vigorously with water."
  },
  {
    name: "Magnesium",
    symbol: "Mg",
    position: 12,
    mass: 24.305,
    description: "A metal used in lightweight alloys."
  }
  // others
];
 
const tableData: DataTableProps = {
  caption: "Some elements from periodic table",
  head: ["Element name", "Symbol", "Element position", "Atomic mass", "Description"],
  body: dataRenderer(data, ["name", "symbol", "position", "mass", "description"])
};
 
export function TableDemo() {
  return <Table data={tableData} overflowWrap spacing={{ x: "16px", y: "20px" }} />;
}

API References

  • mdn

Styles API

type SeLv_0 = "root" | "table" | "caption" | "resizer";
type SeLv_1 = "thead" | "tbody" | "tfoot";
type SeLv_2 = `${SeLv_1}.tr`;
type SeLv_3 = "thead.tr.th" | "tbody.tr.td" | "tfoot.tr.td";
type SeLv_S = "tr" | "th" | "td";
type Selector = NonNullable<SeLv_0 | SeLv_1 | SeLv_2 | SeLv_3 | SeLv_S>;
 
export type StyleComposed<R> = Partial<Record<SeLv_0 | SeLv_1 | SeLv_S, R>> & Partial<Record<SeLv_2 | SeLv_3, R | ((index: number) => R)>>;
 
type CSSProperties = React.CSSProperties & { [key: string]: any };
Styles APITypeDefaultAnnotation
variant?"default" | "tile"defaultdefined style and classes
className?stringundefinedpass to root component <div>
style?CSSPropertiesundefinedpass to root component <div>
unstyled?boolean | StyleComposed<boolean>falseif true, default styles will be removed
classNames?StyleComposed<string | false>undefined
styles?StyleComposed<CSSProperties>undefined

Props API

type Spacing = { x?: number | string; y?: number | string };
type Striped = boolean | "odd" | "even" | undefined;
Props APITypeDefaultAnnotation
data?DataTablePropsundefinedData that should be used to generate table, ignored if children prop is set
orientation?"horizontal" | "vertical""horizontal"Determines whether the first column in each row is sticky or not
layout?["tableLayout"]autoValue of table-layout style
striped?StripedundefinedDetermines whether every odd/even row background should be changed to strippedColor
captionSide?"top" | "bottom"bottomDetermines on which side Table.Caption is displayed
overflowWrap?booleantrueSet table scroll area
stickyHeader?booleantrueDetermines whether Table.Thead should be sticky
stickyHeaderOffset?number | string0Offset from top at which Table.Thead should become sticky
spacing?SpacingDEFAULT_SPACE (by variant)x = Horizontal, y = Vertical cells spacing or any valid CSS value for padding
highlightOnHover?booleantrueDetermines whether table rows background should change to highlightOnHoverColor when hovered
withTableBorder?booleantrueDetermines whether the table should have outer border
withColumnBorders?booleanfalseDetermines whether the table should have borders between columns
withRowBorders?booleantrueDetermines whether the table should have borders between rows
stickyHeader?booleanfalseSet table scroll area
highlightOnHoverColor?Property.Colorhsl(var(--primitive))Background color of table rows when hovered, key of CSS color
borderColor?Property.Colorhsl(var(--border))Color of table borders, key of CSS color
stripedColor?Property.Colorhsl(var(--background-box))Background color of striped rows, key of CSS color

Source Codes

Full working code example, including necessary markup and styles. You can copy and paste this code directly to start using the component immediately.

table.tsx
globals.css
/* table */ @layer base { .stylelayer-table-tile { @apply w-full overflow-x-auto relative scrollbar; & table:where([data-orientation]) { @apply table w-full min-w-max border-separate [border-spacing:var(--table-spacing-x,0)_var(--table-spacing-y,0)]; & :where(.resizer) { @apply absolute z-[9] top-0 h-full w-[5px] min-w-[5px] bg-[rgba(0,0,0,0.5)] cursor-col-resize select-none touch-none [&[dir=ltr]]:right-0 [&[dir=rtl]]:left-0 data-[resizing]:bg-[red] data-[resizing]:opacity-100 [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:hover:opacity-100 [@media(hover:hover)]:[*:hover_>_&[data-resizing]]:opacity-100; } & :where(.tr) { @apply table-row align-middle outline-0 [&_th]:last-of-type:border-transparent [&_svg]:text-[--color-icon] [&:empty:not(:only-child)]:hidden [&:empty:not(:only-child)]:sr-only; } & :where(.thead) { @apply table-header-group align-middle; &:where([data-sticky]) { @apply sticky z-[9] top-[--table-sticky-header-offset,0] bg-background; } & :where(.tr) { & :where(.th) { @apply relative first-of-type:rounded-l-xl last-of-type:rounded-r-xl table-cell text-left [vertical-align:inherit] leading-normal w-max text-sm font-semibold text-[--color-cell] bg-[--background-th] p-3 first-of-type:pl-[var(--padding-cell)] last-of-type:pr-[var(--padding-cell)] [&:has([role=checkbox])]:pl-[calc(var(--padding-cell)-0.5rem)] [&:has([role=button])]:pl-[calc(var(--padding-cell)-0.5rem)] [@media(hover:hover)]:[&:hover_>_:where(.resizer)]:opacity-100; } } } & :where(.tbody) { @apply table-row-group; & :where(.tr) { @apply last-of-type:[border-bottom:--nth-of-type-border,none] hover:data-[hover]:[--tr-hover-bg:--table-highlight-on-hover-color,var(--table-hover-color)]; &:where([data-striped="odd"]:nth-of-type(odd)) { @apply [--bg-table-row:--table-striped-color]; } &:where([data-striped="even"]:nth-of-type(even)) { @apply [--bg-table-row:--table-striped-color]; } } & :where(.tr) { @apply rounded-[calc(2*var(--shape-rounded))] hover:bg-[var(--table-hover-color)] hover:shadow-md [transition:background-color_150ms_cubic-bezier(0.4,0,0.2,1),box-shadow_150ms_cubic-bezier(0.4,0,0.2,1)] data-[striped]:bg-[--bg-table-row] hover:data-[hover]:[--bg-table-row:--tr-hover-bg] transition-colors duration-300; & :where(.td) { @apply relative table-cell first-of-type:rounded-l-xl last-of-type:rounded-r-xl first-of-type:border-l last-of-type:border-r first-of-type:border-l-[var(--table-border-color)] last-of-type:border-r-[var(--table-border-color)] border-y border-y-[var(--table-border-color)] text-sm py-4 first-of-type:pl-[var(--padding-cell)] last-of-type:pr-[var(--padding-cell)] text-[--color-cell] align-middle [&:has([role=checkbox])]:pr-0 truncate pl-[var(--padding-cell)] pr-0 h-[var(--height-cell,78px)]; } } } } } } @layer base { .stylelayer-table-table { @apply relative ltr:text-left rtl:text-right [--table-border:0.0625rem_solid_var(--table-border-color)]; &:where([data-table-border]) { @apply [border:--table-border]; } & .table { & .tr, & .th, & .td { @apply relative; } } & .table:where([data-orientation="vertical"]) { & .tr .th { @apply [@media(hover:hover)]:[&:hover_>_:where(.resizer)]:opacity-100; } & :where(.resizer) { @apply absolute z-[9] top-0 h-full w-[5px] min-w-[5px] bg-[rgba(0,0,0,0.5)] cursor-col-resize select-none touch-none [&[dir=ltr]]:right-0 [&[dir=rtl]]:left-0 data-[resizing]:bg-[red] data-[resizing]:opacity-100 [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:hover:opacity-100 [@media(hover:hover)]:[*:hover_>_&[data-resizing]]:opacity-100; } } &:where([data-table-overflow]) { @apply w-full min-w-full max-w-full overflow-auto [scrollbar-color:#adb3bd_#0000] [scrollbar-width:thin] [scrollbar-gutter:auto] [&>table]:min-w-full [&>table]:max-w-max; & .table:where([data-orientation="horizontal"]) { & .tbody .tr:where([data-hover]):hover { & .td { @apply bg-[--bg-sticky-horizontal] [--bg-sticky-horizontal:--table-highlight-on-hover-color,var(--table-hover-color)]; } } & .tr .th, & .tr .td { &:nth-child(1) { @apply sticky z-[8] left-0 border-r-0 bg-[--bg-sticky-horizontal] [--bg-sticky-horizontal:--bg-table-row,hsl(var(--background))] after:absolute after:z-[2] after:content-[''] after:right-0 after:inset-y-0 after:h-full after:w-1 after:[border-left:var(--horizontal-table-border,var(--table-border,none))] after:[border-right:var(--horizontal-table-border,var(--table-border,none))] after:bg-[--bg-sticky-horizontal]; } } & .thead:where([data-sticky]) .tr .th:nth-child(1) { @apply after:right-[-0.5px]; } } } &:where([data-column-border]) { & .th, & .td { &:where(:not(:last-child)) { @apply [--column-border:--table-border]; } } } &:where([data-row-border]) { @apply [--row-border:--table-border]; & :where(.thead > .tr) { @apply border-b-transparent; } &:where([data-table-border]) { & .table:has(.caption) { & .tbody .tr:where(:last-of-type) { @apply [--nth-of-type-border:--row-border,none]; } } } } & .table { @apply border-collapse border-spacing-[0_1rem] [table-layout:--table-layout,auto] [caption-side:--table-caption-side,bottom]; & .tr { @apply [&:empty:not(:only-child)]:hidden [&:empty:not(:only-child)]:sr-only; } } & .thead { @apply relative z-[1] after:content-[''] after:absolute after:z-[2] after:w-full after:h-1 after:bg-transparent after:inset-x-0 after:bottom-0 after:[border-bottom:--table-border,none] after:[border-top:--row-border,none]; &:where([data-sticky]) { @apply sticky z-[9] top-[--table-sticky-header-offset,0] bg-background; } } & .tbody { @apply data-[state=active]:[&_tr]:bg-muted/55; & :where(.tr) { @apply last-of-type:[border-bottom:--nth-of-type-border,none] hover:data-[hover]:[--tr-hover-bg:--table-highlight-on-hover-color,var(--table-hover-color)]; &:where([data-striped="odd"]:nth-of-type(odd)) { @apply [--bg-table-row:--table-striped-color]; } &:where([data-striped="even"]:nth-of-type(even)) { @apply [--bg-table-row:--table-striped-color]; } } } & .tfoot { @apply bg-muted font-medium; } & .tr { @apply [border-bottom:--row-border,none] bg-[--bg-table-row,transparent] appearance-none; &:where([data-hover]) { @apply hover:[--bg-table-row:--tr-hover-bg]; } } & .th { @apply h-10 font-medium align-middle [&:has([role=checkbox])]:pr-0; } & .td { @apply [&:where(:not([data-table=root])_td)]:[vertical-align:var(--vertical-align)]; } & .th, & .td { @apply first:pl-4 last:pr-4 py-[--table-spacing-y] px-[--table-spacing-x]; &:where(:not(:last-child)) { @apply [border-inline-end:--column-border,none]; } } & .caption { @apply text-sm text-muted-foreground; &:where([data-side="top"]) { @apply py-[--table-spacing-y] [border-bottom:--row-border,none]; } &:where([data-side="bottom"]) { @apply py-[--table-spacing-y]; } } } }