Scheda
Un riquadro con bordi per dashboard, livelli di prezzo e griglie di prodotti — intestazione, contenuto e piè di pagina con l'effetto "hard-shadow" in stile neobrutalista.
import { Button } from "@/components/ui/button"
import {
Card,La card raggruppa contenuti correlati in un riquadro autonomo con slot facoltativi per intestazione, contenuto e piè di pagina. Non c’è nessuna primitiva sotto — è semplice markup rifinito secondo la ricetta neobrutalista: un bordo spesso, un’ombra netta sfalsata e un titolo nel carattere dei titoli.
Quando usarlo:
- Pannelli di dashboard — riquadri di statistiche, grafici e feed di attività disposti in una griglia.
- Piani tariffari — nome e prezzo del piano nell’intestazione, le funzionalità nel corpo, il pulsante di acquisto nel piè di pagina.
- Moduli autonomi — riquadri di accesso, registrazione e impostazioni che hanno bisogno di un confine visivo.
Installazione#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/card.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/card.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/card.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/card.json
Copia e incolla il seguente codice nel tuo progetto.
import * as React from "react"
import { cn } from "@/lib/utils"
function Card({
className,
size = "default",
...props
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
return (
<div
data-slot="card"
data-size={size}
className={cn(
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded border-2 bg-card py-(--card-spacing) text-sm text-card-foreground shadow-md [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className
)}
{...props}
/>
)
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
className
)}
{...props}
/>
)
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn(
"cn-font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
className
)}
{...props}
/>
)
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
)
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...props}
/>
)
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-(--card-spacing)", className)}
{...props}
/>
)
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn(
"flex items-center rounded-b-xl border-t-2 bg-muted/50 p-(--card-spacing)",
className
)}
{...props}
/>
)
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
}
Aggiorna i percorsi di import in base alla struttura del tuo progetto.
Utilizzo#
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card Description</CardDescription>
<CardAction>Card Action</CardAction>
</CardHeader>
<CardContent>
<p>Card Content</p>
</CardContent>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</Card>Composizione#
Usa la seguente composizione per costruire una Card:
Card
├── CardHeader
│ ├── CardTitle
│ ├── CardDescription
│ └── CardAction
├── CardContent
└── CardFooterEsempi#
Dimensione#
Usa la prop size="sm" per impostare la dimensione della card su piccola. La variante piccola usa una spaziatura più ridotta.
import { ChevronRightIcon } from "lucide-react"
import { Button } from "@/components/ui/button"Spaziatura#
Oltre alla prop size, puoi usare la variabile CSS --card-spacing per controllare la spaziatura tra le sezioni e il margine interno delle parti della card.
"use client"
import * as React from "react"Usa margini negativi con -mx-(--card-spacing) per far estendere il contenuto da bordo a bordo, mantenendolo allineato al margine interno della card. Quando il contenuto da bordo a bordo si trova sopra un piè di pagina, usa -mb-(--card-spacing) su CardContent per rimuovere lo spazio tra le sezioni.
import { Button } from "@/components/ui/button"
import {
Card,Immagine#
Aggiungi un'immagine prima dell'intestazione della card per creare una card con immagine.
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {RTL#
Per abilitare il supporto RTL in Neobrutalism, consulta la guida alla configurazione RTL.
"use client"
import * as React from "react"Accessibilità#
La card è puramente presentazionale — ogni sua parte rende un semplice <div> senza ruolo implicito, quindi non aggiunge nulla all’albero dell’accessibilità. CardTitle non è un elemento di intestazione: annida un vero <h2>–<h4> al suo interno quando il titolo deve comparire nella struttura del documento. Per le card cliccabili, metti il link o il pulsante dentro la card ed estendine l’area cliccabile sull’intera superficie — non collegare mai onClick al <div> della card.
Riferimento API#
Card#
Il componente Card è il contenitore radice per il contenuto della card.
| Prop | Type | Default |
|---|---|---|
size | "default" | "sm" | "default" |
className | string | - |
CardHeader#
Il componente CardHeader si usa per un titolo, una descrizione e un'azione opzionale.
| Prop | Type | Default |
|---|---|---|
className | string | - |
CardTitle#
Il componente CardTitle si usa per il titolo della card.
| Prop | Type | Default |
|---|---|---|
className | string | - |
CardDescription#
Il componente CardDescription si usa per il testo di supporto sotto il titolo.
| Prop | Type | Default |
|---|---|---|
className | string | - |
CardAction#
Il componente CardAction posiziona il contenuto in alto a destra dell'intestazione (per esempio, un pulsante o un badge).
| Prop | Type | Default |
|---|---|---|
className | string | - |
CardContent#
Il componente CardContent si usa per il corpo principale della card.
| Prop | Type | Default |
|---|---|---|
className | string | - |
CardFooter#
Il componente CardFooter si usa per le azioni e il contenuto secondario nella parte inferiore della card.
| Prop | Type | Default |
|---|---|---|
className | string | - |
Registro delle modifiche#
Variabile di spaziatura#
Se stai aggiornando da una versione precedente del componente Card, dovrai applicare le seguenti modifiche per usare la variabile --card-spacing:
Aggiorna le classi di spaziatura della radice Card.
Sostituisci il gap e il padding verticale scritti a mano con --card-spacing, e imposta i valori per la dimensione predefinita e piccola sulla radice:
className={cn(
- "group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
+ "group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className
)}Aggiorna le classi di spaziatura di CardHeader.
Sostituisci il padding orizzontale e la spaziatura del bordo con la variabile condivisa:
className={cn(
- "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
+ "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
className
)}Aggiorna le classi di spaziatura di CardContent e CardFooter.
Usa --card-spacing per il margine interno del contenuto e il padding del piè di pagina:
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
- className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
+ className={cn("px-(--card-spacing)", className)}
{...props}
/>
)
} className={cn(
- "flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
+ "flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)",
className
)}Dopo aver applicato queste modifiche, puoi personalizzare la spaziatura della card impostando --card-spacing sulla Card con una classe di proprietà arbitraria:
function Example() {
return <Card className="[--card-spacing:--spacing(6)]">...</Card>
}