ATOM

Table

Rows of data you can sort and select. Dense records, not marketing layouts.

Preview

Editorial

<!-- F12c editorial — non-derivable only. Review: Karen. -->

## Ejemplos

Dense records:

```tsx import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableCaption, } from '@/components/atoms/Table';

<Table> <TableCaption>Invoices</TableCaption> <TableHeader> <TableRow> <TableHead>Customer</TableHead> <TableHead>Amount</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow selected={false}> <TableCell>Ada</TableCell> <TableCell>$120</TableCell> </TableRow> </TableBody> </Table> ```

## Accesibilidad

- Use `TableHead` for column headers and `TableCaption` for a summary. - `selected` lives on `TableRow`, not the root table.

## Cuándo no usar

- Marketing comparison layouts → layout blocks / cards. - Simple key-value pairs → definition list or `Item` rows.

## Criterio de uso

- Usa Table cuando las relaciones entre columnas y filas importan y el usuario necesita comparar registros. - Mantén encabezados descriptivos, una caption útil y estados de selección que se entiendan junto con el contenido de la fila. - Para tablas densas, prioriza lectura y navegación por teclado antes de añadir decoración o acciones por celda.

## Gotchas

- Table es para datos, no para construir layout visual; usarlo como grid de marketing perjudica semántica y responsive. - `selected` pertenece a `TableRow`; no lo apliques al root esperando que comunique una selección global.

Uso

import {
  Table, TableHeader, TableBody, TableRow, TableHead, TableCell, TableCaption,
} from '@/components/atoms/Table';

<Table>
  <TableCaption>Invoices</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead>Customer</TableHead>
      <TableHead>Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Ada</TableCell>
      <TableCell>$120</TableCell>
    </TableRow>
  </TableBody>
</Table>

Props

  • selected: boolean

Gotchas

  • react

    Compose Table > TableHeader/Body/Footer + TableRow + TableHead/Cell. selected lives on TableRow, not the root table.

  • a11y

    Use TableHead for column headers and TableCaption for a summary; avoid layout tables.

Anatomía CSS

<div class="table-wrapper">

</div>
ClasePropósito
table-wrapperroot

Tokens resueltos

Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.

VariantePropValorToken
allfg#0a0a0aforeground
allborder1pxstroke.hairline
allhover-bg#f5f5f5muted
allbg#f5f5f5muted

Animaciones

PropiedadDuraciónEasing
background-colorvar(--duration-100)var(--easing-in-out)

CSS mínimo funcional

Autocontenido: sin imports ni tokens. Para previews y prototipos — en producción se consume el CSS del DS (@atom-uikit/css/components.css</code>, <code>@atom-uikit/tokens/tokens.css).

/* -------------------------------------------------------------------------
   Table

   Semantic HTML table with consistent styling.
   Thin wrapper: Table, TableHeader, TableBody, TableFooter,
                 TableRow, TableHead, TableCell, TableCaption

   Parts: .table, .table__header, .table__body, .table__footer,
          .table__row, .table__head, .table__cell, .table__caption
   ------------------------------------------------------------------------- */

/* ---- Wrapper ---- */

.table-wrapper {
  width: 100%;
  overflow-x: auto;
}

/* ---- Table ---- */

.table {
  width: 100%;
  border-collapse: collapse;
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif, ui-sans-serif, system-ui, sans-serif;
  font-size: 12.8px;
  line-height: 1.45;
  text-align: left;
}

/* ---- Caption ---- */

.table__caption {
  padding-top: 16px;
  font-size: 12.8px;
  color: #525252;
  caption-side: bottom;
}

/* ---- Header ---- */

.table__header {
  border-bottom: 1px solid #e5e5e5;
}

.table__head {
  padding: 12px 16px;
  font-weight: 500;
  color: #525252;
  white-space: nowrap;
  vertical-align: middle;
  height: 40px;
}

/* ---- Body ---- */

.table__row {
  border-bottom: 1px solid #e5e5e5;
  transition: background-color 100ms cubic-bezier(0.4, 0, 0.2, 1);
}

.table__row:hover {
  background-color: #f5f5f5;
}

.table__row--selected {
  background-color: #f5f5f5;
}

.table__cell {
  padding: 12px 16px;
  color: #0a0a0a;
  vertical-align: middle;
}

/* ---- Footer ---- */

.table__footer {
  border-top: 1px solid #e5e5e5;
  background-color: #f5f5f5;
}

.table__footer .table__cell {
  font-weight: 600;
}

/* ---- Alignment helpers ---- */

.table__head--right,
.table__cell--right {
  text-align: right;
}

.table__head--center,
.table__cell--center {
  text-align: center;
}

/* ---- Reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
  .table__row {
    transition-duration: 0ms;
  }
}

Codigo fuente

components-react
components/atoms/Table.tsx
import { forwardRef, type HTMLAttributes, type TdHTMLAttributes, type ThHTMLAttributes } from 'react';

function cn(...classes: (string | false | undefined | null)[]) {
  return classes.filter(Boolean).join(' ');
}

export const Table = forwardRef<HTMLTableElement, HTMLAttributes<HTMLTableElement>>(
  ({ className, ...props }, ref) => (
    <div className="table-wrapper">
      <table ref={ref} className={cn('table', className)} {...props} />
    </div>
  ),
);
Table.displayName = 'Table';

export const TableHeader = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(
  ({ className, ...props }, ref) => (
    <thead ref={ref} className={cn('table__header', className)} {...props} />
  ),
);
TableHeader.displayName = 'TableHeader';

export const TableBody = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(
  ({ className, ...props }, ref) => (
    <tbody ref={ref} className={cn('table__body', className)} {...props} />
  ),
);
TableBody.displayName = 'TableBody';

export const TableFooter = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(
  ({ className, ...props }, ref) => (
    <tfoot ref={ref} className={cn('table__footer', className)} {...props} />
  ),
);
TableFooter.displayName = 'TableFooter';

export type TableRowProps = {
  selected?: boolean;
} & HTMLAttributes<HTMLTableRowElement>;

export const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
  ({ className, selected, ...props }, ref) => (
    <tr ref={ref} className={cn('table__row', selected && 'table__row--selected', className)} {...props} />
  ),
);
TableRow.displayName = 'TableRow';

export const TableHead = forwardRef<HTMLTableCellElement, ThHTMLAttributes<HTMLTableCellElement>>(
  ({ className, ...props }, ref) => (
    <th ref={ref} className={cn('table__head', className)} {...props} />
  ),
);
TableHead.displayName = 'TableHead';

export const TableCell = forwardRef<HTMLTableCellElement, TdHTMLAttributes<HTMLTableCellElement>>(
  ({ className, ...props }, ref) => (
    <td ref={ref} className={cn('table__cell', className)} {...props} />
  ),
);
TableCell.displayName = 'TableCell';

export const TableCaption = forwardRef<HTMLTableCaptionElement, HTMLAttributes<HTMLTableCaptionElement>>(
  ({ className, ...props }, ref) => (
    <caption ref={ref} className={cn('table__caption', className)} {...props} />
  ),
);
TableCaption.displayName = 'TableCaption';

Webflow

Webflowtable

Pega en el Designer como application/json, luego convierte a Component (Atom / Table) y publica. Formato interno no documentado de Webflow — regenerable, no dependencia de runtime.

Setup del sitio (una vez)

Custom Code → Head:

<link rel="stylesheet" href="https://atom-web-ds.vercel.app/v1/tokens.css">
<link rel="stylesheet" href="https://atom-web-ds.vercel.app/v1/components.css">

Unsupported (no silencioso)

  • selector on .table__footer .table__cellcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .table__head--right, .table__cell--rightcompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • selector on .table__head--center, .table__cell--centercompound/descendant selector — moved to head Custom Code (Designer styles are single-class)
  • @media on (prefers-reduced-motion: reduce)not a Designer breakpoint — moved to head Custom Code block

Tras pegar: Create component → nombre Atom / Table → Publish.

Componentes relacionados

On this page

Detalles

Publicado14 de mayo de 2026
Categoriadata-display
Lectura...
Visitas...
Ayuda?Slack

Componente

Table

Source

components-react / cssDisponible via MCP: atom_uikit_source("table")
Abrir en Storybook