Table
ProntoListagem tabular com header sticky, hover de linha, sort por coluna, seleção múltipla e ações por linha. Otimizada para densidade jurídica (CNJ, datas, valores monetários).
Usar quando
Listas de processos, contatos, lançamentos financeiros, intimações. Quando comparação por coluna importa.
Não usar quando
Cards visuais (use grid de Cards). Lista de poucos itens (use list simples). Mobile (use lista vertical com touch-friendly rows).
Variantes
Microinterações
| Microinteração | Disparada por | Comportamento | Timing |
|---|---|---|---|
| Row hover | mouseenter row | Background → surfaceHover, ações revelam | 100ms ease |
| Sort click | click no header | Setinha gira ↑↓ + reordena com fade-cross | 200ms cubic-bezier(0.32,0.72,0,1) |
| Select all | check no header | Cascata visual: linhas marcam top-down | 50ms stagger |
| Bulk action bar | seleção > 0 | Bar escorrega de cima com slide-down | 250ms ease |
| Resize column (advanced) | drag entre colunas | Coluna ajusta width, cursor col-resize | follows pointer |
| Empty state | 0 resultados | Substitui tbody por <EmptyState> inline | instant |
| Focused row keyboard | ↑↓ com foco | Background ring brass-edge, scroll automático | instant |
Acessibilidade
Acessibilidade — checklist
Teclado
| Tab | Foco na tabela |
| ↑ / ↓ | Navega linhas |
| ← / → | Navega células |
| Space (header) | Toggle sort |
| Space (row) | Toggle seleção |
| Cmd/Ctrl+A | Selecionar todos |
| Enter | Abre detalhe (drill-down) |
ARIA esperado
- <table role="table"><thead><tr role="row"><th role="columnheader" aria-sort="ascending" />
- <tbody><tr role="row" aria-selected="true|false"><td role="cell" /></tr>
- caption acima da tabela com aria-label
- Seleção: aria-multiselectable="true" no role="grid"
Notas
- Header sticky em scroll vertical (top: 0 + position: sticky).
- Sort: ícone setinha próxima ao label, gira na mudança.
- Multi-select com Shift+Click suporta range.
- Em mobile: deserialize tabela em lista vertical.
- Datas e valores monetários SEMPRE em mono (tabular-nums alinha decimal).
Código
'use client';
import { useState } from 'react';
import { ArrowUp, ArrowDown } from 'lucide-react';
import { T, TYPE, SP } from '@/lib/tokens';
interface Column<T> {
key: keyof T;
label: string;
sortable?: boolean;
render?: (row: T) => React.ReactNode;
align?: 'left' | 'right';
}
export function Table<T>({ columns, rows }: { columns: Column<T>[]; rows: T[] }) {
const [sort, setSort] = useState<{ key: keyof T; dir: 'asc' | 'desc' } | null>(null);
const sorted = sort
? [...rows].sort((a, b) => {
const va = a[sort.key], vb = b[sort.key];
return sort.dir === 'asc' ? (va > vb ? 1 : -1) : (va < vb ? 1 : -1);
})
: rows;
return (
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ background: T.surface2 }}>
{columns.map((col) => (
<th key={String(col.key)}
onClick={() => col.sortable && setSort({ key: col.key, dir: sort?.dir === 'asc' ? 'desc' : 'asc' })}>
{col.label}
{sort?.key === col.key && (sort.dir === 'asc' ? <ArrowUp size={10} /> : <ArrowDown size={10} />)}
</th>
))}
</tr>
</thead>
<tbody>{sorted.map((r, i) => <tr key={i}>...</tr>)}</tbody>
</table>
);
}Regras
Faça
- ✓Header sticky no scroll vertical.
- ✓Hover de linha em surfaceHover (suave).
- ✓Datas e valores em font-mono + tabular-nums.
- ✓Ações por linha aparecem em hover (não polir layout em rest).
- ✓Sort com ícone setinha visível, default ASC primeiro click, DESC segundo, OFF terceiro.
- ✓Seleção múltipla: header com indeterminate quando parcial.
Não faça
- ✗Não use bordas em todas as células (use só borderTop entre linhas).
- ✗Não force colunas estreitas (mínimo confortável para conteúdo).
- ✗Não esconda o sort (seta deve indicar coluna ativa).
- ✗Não use tabela para layout (use grid CSS).
- ✗Não esqueça empty state.
- ✗Não tenha mais de 7-8 colunas visíveis (ofereça toggle/scroll horizontal).
Tokens usados
| Token | Valor | Papel |
|---|---|---|
T.surface | #FFFFFF | background |
T.surface2 | #FAF8F2 | background do header |
T.surfaceHover | #F7F5EC | hover de linha |
T.borderFaint | #F0EEE5 | borda entre linhas |
TYPE.base | 13 | fontSize célula |
FONT.mono | JetBrains Mono | datas e números |