Naar inhoud

Kaart

Een kader met rand voor dashboards, prijsniveaus en productoverzichten — koptekst, inhoud en voettekst met de neobrutalistische harde schaduw.

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

De card groepeert verwante inhoud in een op zichzelf staand kader met optionele slots voor koptekst, inhoud en voettekst. Er zit geen primitive onder — het is kale markup, vormgegeven volgens het neobrutalistische recept: een dikke rand, een harde offset-schaduw en een titel in het koplettertype.

Gebruik deze component bij:

  • Dashboardpanelen — statistiektegels, grafieken en activiteitenfeeds geordend in een raster.
  • Prijsniveaus — plannaam en prijs in de koptekst, features in de body, de koopknop in de voettekst.
  • Losstaande formulieren — inlog-, registratie- en instellingenkaders die een visuele begrenzing nodig hebben.

Installatie

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

Kopieer en plak de volgende code in je project.

components/ui/card.tsx
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,
}

Werk de import-paden bij zodat ze overeenkomen met je projectstructuur.

Gebruik

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>

Compositie

Gebruik de volgende compositie om een Card te bouwen:

Card
├── CardHeader
│   ├── CardTitle
│   ├── CardDescription
│   └── CardAction
├── CardContent
└── CardFooter

Voorbeelden

Grootte

Gebruik de prop size="sm" om de grootte van de card op klein in te stellen. De variant met kleine grootte gebruikt minder ruimte.

import { ChevronRightIcon } from "lucide-react"

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

Ruimte

Naast de prop size kun je de CSS-variabele --card-spacing gebruiken om de ruimte tussen secties en de inspringing van de card-onderdelen te bepalen.

"use client"

import * as React from "react"

Gebruik negatieve marges met -mx-(--card-spacing) om inhoud van rand tot rand te laten lopen terwijl deze uitgelijnd blijft met de inspringing van de card. Wanneer de inhoud van rand tot rand boven een footer staat, gebruik je -mb-(--card-spacing) op CardContent om de tussenruimte tussen secties te verwijderen.

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

Afbeelding

Voeg een afbeelding toe vóór de card-header om een card met een afbeelding te maken.

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

RTL

Om RTL-ondersteuning in Neobrutalism in te schakelen, zie de RTL-configuratiegids.

"use client"

import * as React from "react"

Toegankelijkheid

De card is puur presentationeel — elk onderdeel rendert een kale <div> zonder impliciete rol, dus hij voegt niets toe aan de toegankelijkheidsboom. CardTitle is geen kopelement: nestel er een echte <h2><h4> in wanneer de titel in de documentstructuur moet verschijnen. Zet bij klikbare cards de link of knop binnen de card en rek het klikgebied over het hele oppervlak uit — hang nooit een onClick aan de <div> van de card zelf.

API-referentie

Card

De component Card is de wortelcontainer voor de card-inhoud.

PropTypeDefault
size"default" | "sm""default"
classNamestring-

CardHeader

De component CardHeader wordt gebruikt voor een titel, beschrijving en optionele actie.

PropTypeDefault
classNamestring-

CardTitle

De component CardTitle wordt gebruikt voor de titel van de card.

PropTypeDefault
classNamestring-

CardDescription

De component CardDescription wordt gebruikt voor de helptekst onder de titel.

PropTypeDefault
classNamestring-

CardAction

De component CardAction plaatst inhoud rechtsboven in de header (bijvoorbeeld een knop of een badge).

PropTypeDefault
classNamestring-

CardContent

De component CardContent wordt gebruikt voor het hoofdgedeelte van de card.

PropTypeDefault
classNamestring-

CardFooter

De component CardFooter wordt gebruikt voor acties en secundaire inhoud onderaan de card.

PropTypeDefault
classNamestring-

Changelog

Ruimte-variabele

Als je upgradet van een eerdere versie van de component Card, moet je de volgende updates toepassen om de variabele --card-spacing te gebruiken:

Werk de ruimte-klassen van de Card-wortel bij.

Vervang de hardgecodeerde gap en verticale padding door --card-spacing en stel de waarden voor de standaard- en kleine grootte in op de wortel:

  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
  )}

Werk de ruimte-klassen van CardHeader bij.

Vervang de horizontale padding en randruimte door de gedeelde variabele:

  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
  )}

Werk de ruimte-klassen van CardContent en CardFooter bij.

Gebruik --card-spacing voor de inspringing van de inhoud en de padding van de footer:

  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
  )}

Nadat je deze wijzigingen hebt toegepast, kun je de ruimte van de card aanpassen door --card-spacing op de Card in te stellen met een klasse voor een willekeurige eigenschap:

function Example() {
  return <Card className="[--card-spacing:--spacing(6)]">...</Card>
}