Aller au contenu

Tableau

Un tableau HTML sémantique destiné aux factures, aux listes administratives et aux grilles tarifaires — lignes bordées, en-têtes en gras et ombre néobrutaliste marquée.

import {
  Table,
  TableBody,

Le composant table habille les éléments de tableau natifs du HTML — <table>, <thead>, <tbody>, <th>, <td>, <caption> — sans bibliothèque de primitives en dessous ; les variantes Radix et Base livrent le même fichier. La recette néobrutaliste au programme : bordure épaisse et ombre franche autour du tableau entier, cellules d’en-tête dans la police de titres, et séparateurs de lignes de 2px.

À privilégier dans ces cas :

  • Factures et commandes — des lignes d’articles avec une ligne TableFooter pour le total.
  • Listes d’administration — utilisateurs, clés API, abonnements, avec un menu déroulant d’actions par ligne.
  • Grilles tarifaires et comparatives — des matrices de fonctionnalités où les en-têtes de colonnes portent le sens.

Installation

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/table.json
npx shadcn@latest add https://neobrutalism.com/r/base/table.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/table.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/table.json

Copiez-collez le code suivant dans votre projet.

components/ui/table.tsx
"use client"

import * as React from "react"

import { cn } from "@/lib/utils"

function Table({ className, ...props }: React.ComponentProps<"table">) {
  return (
    <div
      data-slot="table-container"
      className="relative w-full overflow-x-auto rounded border-2 shadow-md"
    >
      <table
        data-slot="table"
        className={cn("w-full caption-bottom text-sm", className)}
        {...props}
      />
    </div>
  )
}

function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
  return (
    <thead
      data-slot="table-header"
      className={cn("[&_tr]:border-b-2", className)}
      {...props}
    />
  )
}

function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
  return (
    <tbody
      data-slot="table-body"
      className={cn("[&_tr:last-child]:border-0", className)}
      {...props}
    />
  )
}

function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
  return (
    <tfoot
      data-slot="table-footer"
      className={cn(
        "border-t-2 bg-muted/50 font-medium [&>tr]:last:border-b-0",
        className
      )}
      {...props}
    />
  )
}

function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
  return (
    <tr
      data-slot="table-row"
      className={cn(
        "border-b-2 transition-colors hover:bg-accent has-aria-expanded:bg-accent data-[state=selected]:bg-accent",
        className
      )}
      {...props}
    />
  )
}

function TableHead({ className, ...props }: React.ComponentProps<"th">) {
  return (
    <th
      data-slot="table-head"
      className={cn(
        "h-10 bg-muted px-2 text-left align-middle font-head font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
        className
      )}
      {...props}
    />
  )
}

function TableCell({ className, ...props }: React.ComponentProps<"td">) {
  return (
    <td
      data-slot="table-cell"
      className={cn(
        "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0",
        className
      )}
      {...props}
    />
  )
}

function TableCaption({
  className,
  ...props
}: React.ComponentProps<"caption">) {
  return (
    <caption
      data-slot="table-caption"
      className={cn("mt-4 text-sm text-muted-foreground", className)}
      {...props}
    />
  )
}

export {
  Table,
  TableHeader,
  TableBody,
  TableFooter,
  TableHead,
  TableRow,
  TableCell,
  TableCaption,
}

Adaptez les chemins d’import à la configuration de votre projet.

Utilisation

import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table"
<Table>
  <TableCaption>A list of your recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead className="w-[100px]">Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead>Method</TableHead>
      <TableHead className="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell className="font-medium">INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell>Credit Card</TableCell>
      <TableCell className="text-right">$250.00</TableCell>
    </TableRow>
  </TableBody>
</Table>

Composition

Utilisez la composition suivante pour construire un Table :

Table
├── TableCaption
├── TableHeader
│   └── TableRow
│       ├── TableHead
│       ├── TableHead
│       ├── TableHead
│       └── TableHead
├── TableBody
│   ├── TableRow
│   │   ├── TableCell
│   │   ├── TableCell
│   │   ├── TableCell
│   │   └── TableCell
│   └── TableRow
│       ├── TableCell
│       ├── TableCell
│       ├── TableCell
│       └── TableCell
└── TableFooter

Exemples

Pied de tableau

Utilisez le composant <TableFooter /> pour ajouter un pied de tableau.

import {
  Table,
  TableBody,

Actions

Un tableau qui présente des actions pour chaque ligne à l’aide d’un composant <DropdownMenu />.

import { MoreHorizontalIcon } from "lucide-react"

import { Button } from "@/components/ui/button"

Data Table

Vous pouvez utiliser le composant <Table /> pour construire des tableaux de données plus complexes. Associez-le à @tanstack/react-table pour créer des tableaux avec tri, filtrage et pagination.

Consultez la documentation Data Table pour en savoir plus.

Vous pouvez aussi voir un exemple de tableau de données dans la démo Tasks.

RTL

Pour activer le RTL dans Neobrutalism, consultez le guide de configuration RTL.

"use client"

import * as React from "react"

Accessibilité

Rendre des éléments de tableau natifs offre aux lecteurs d’écran l’annonce du tableau, la navigation par lignes et colonnes et l’association des en-têtes, sans le moindre câblage ARIA. Nommez chaque tableau avec TableCaption et réservez TableHead aux véritables cellules d’en-tête. Le conteneur de défilement n’est pas focalisable, donc les utilisateurs au clavier seul ne peuvent pas faire défiler un tableau qui déborde — si des tableaux larges sont probables, ajoutez tabIndex={0}, role="region" et un aria-label au div conteneur dans votre copie du composant.