HextaUI

Card

Eine Fläche zum Gruppieren von Inhalten, mit drei Varianten, randlosen Medien, konzentrischen Radien und Links über die ganze Karte.

Team plan
Billed monthly. Renews on October 28, 2026.

12 of 20 seats used. Invite teammates or remove inactive members to free up seats.

import { IconDotsVertical } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardDemo() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Team plan</CardTitle>
        <CardDescription>
          Billed monthly. Renews on October 28, 2026.
        </CardDescription>
        <CardAction>
          <Button variant="ghost" size="icon-sm" aria-label="More options">
            <IconDotsVertical />
          </Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        <p>
          12 of 20 seats used. Invite teammates or remove inactive members to
          free up seats.
        </p>
      </CardContent>
      <CardFooter>
        <Button>Manage seats</Button>
        <Button variant="outline">View invoices</Button>
      </CardFooter>
    </Card>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/card.json

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"
<Card>
  <CardHeader>
    <CardTitle>Team plan</CardTitle>
    <CardDescription>Billed monthly.</CardDescription>
    <CardAction>
      <Button variant="ghost" size="icon-sm" aria-label="More options">
        <IconDotsVertical />
      </Button>
    </CardAction>
  </CardHeader>
  <CardContent>12 of 20 seats used.</CardContent>
  <CardFooter>
    <Button>Manage seats</Button>
  </CardFooter>
</Card>
Card
├── CardHeader
│   ├── CardTitle
│   │   └── CardLink
│   ├── CardDescription
│   └── CardAction
├── CardContent
└── CardFooter

Varianten

default sitzt auf einem haarfeinen Ring mit weichem Schatten, outline entfernt Füllung und Schatten, und muted verwendet eine gefüllte Fläche für ruhigere Bereiche.

import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardVariants() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-3">
      <Card>
        <CardHeader>
          <CardTitle>Default</CardTitle>
          <CardDescription>Hairline ring and a soft shadow.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="outline">
        <CardHeader>
          <CardTitle>Outline</CardTitle>
          <CardDescription>Ring only, no fill, no shadow.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="muted">
        <CardHeader>
          <CardTitle>Muted</CardTitle>
          <CardDescription>Filled surface for quiet sections.</CardDescription>
        </CardHeader>
      </Card>
    </div>
  )
}

Größen

size="sm" strafft Abstand, Radius und Titelgröße gemeinsam, sodass eine kompakte Karte weiterhin zur selben Familie gehört. size="flush" entfernt den Innenabstand für eigene Layouts wie Listen und einklappbare Zeilen, die ihr eigenes Padding mitbringen.

import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardSizes() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-2">
      <Card>
        <CardHeader>
          <CardTitle>Default</CardTitle>
          <CardDescription>Spacing and radius scale together.</CardDescription>
        </CardHeader>
        <CardContent>
          <p>Deploys run on every push to main.</p>
        </CardContent>
        <CardFooter>
          <Button variant="outline">Configure</Button>
        </CardFooter>
      </Card>
      <Card size="sm">
        <CardHeader>
          <CardTitle>Small</CardTitle>
          <CardDescription>Spacing and radius scale together.</CardDescription>
        </CardHeader>
        <CardContent>
          <p>Deploys run on every push to main.</p>
        </CardContent>
        <CardFooter>
          <Button size="sm" variant="outline">
            Configure
          </Button>
        </CardFooter>
      </Card>
    </div>
  )
}

Media

Ein Bild, Video, Picture oder Seitenverhältnis, das als Erstes oder Letztes steht, läuft randlos. Die Karte entfernt das Padding auf dieser Seite und beschneidet das Media an ihren Ecken.

import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardMedia() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-2">
      <Card>
        <img
          src="/preview/landscape.svg"
          alt="Mountain landscape"
          className="aspect-video w-full object-cover"
        />
        <CardHeader>
          <CardTitle>Media first</CardTitle>
          <CardDescription>
            The image runs edge to edge and inherits the card corners.
          </CardDescription>
        </CardHeader>
      </Card>
      <Card>
        <CardHeader>
          <CardTitle>Media last</CardTitle>
          <CardDescription>Bottom padding drops for the image.</CardDescription>
        </CardHeader>
        <img
          src="/preview/landscape.svg"
          alt="Mountain landscape"
          className="aspect-video w-full object-cover"
        />
      </Card>
    </div>
  )
}

Klickbare Karte

Setze einen <CardLink /> in den Titel, um die ganze Karte zu einem Link zu machen. Buttons und andere Bedienelemente darin bleiben klickbar und behalten ihren eigenen Fokus, wie der Stern hier.

"use client"

import * as React from "react"
import { IconGitBranch, IconStar } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Card,
  CardAction,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

const projects = [
  {
    name: "Acme website",
    description: "Marketing site and docs, deployed on every push.",
    stars: 128,
    branch: "main",
  },
  {
    name: "Payments service",
    description: "Card processing, webhooks and the reconciliation job.",
    stars: 42,
    branch: "release/2.4",
  },
]

export function CardClickable() {
  const [starred, setStarred] = React.useState<string[]>([])

  return (
    <div className="grid w-full gap-4 sm:grid-cols-2">
      {projects.map((project) => {
        const isStarred = starred.includes(project.name)

        return (
          <Card key={project.name}>
            <CardHeader>
              <CardTitle>
                <CardLink href="#">{project.name}</CardLink>
              </CardTitle>
              <CardDescription>{project.description}</CardDescription>
              <CardAction>
                <Button
                  variant="ghost"
                  size="icon-sm"
                  aria-label={`Star ${project.name}`}
                  aria-pressed={isStarred}
                  onClick={() =>
                    setStarred(
                      isStarred
                        ? starred.filter((name) => name !== project.name)
                        : [...starred, project.name]
                    )
                  }
                >
                  <IconStar className={isStarred ? "fill-current" : ""} />
                </Button>
              </CardAction>
            </CardHeader>
            <CardFooter>
              <span className="flex items-center gap-1 text-muted-foreground">
                <IconGitBranch className="size-4" />
                {project.branch}
              </span>
              <span className="ms-auto text-muted-foreground">
                {project.stars + (isStarred ? 1 : 0)} stars
              </span>
            </CardFooter>
          </Card>
        )
      })}
    </div>
  )
}

Klickbare Varianten

Jede Variante erhält ihr eigenes Hover-Feedback, sobald sie einen Link enthält, und der Fokusring umschließt die ganze Karte, wenn der Link per Tastatur fokussiert wird.

import { IconArrowUpRight } from "@tabler/icons-react"

import {
  Card,
  CardDescription,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

export function CardLinkVariants() {
  return (
    <div className="grid w-full gap-4 sm:grid-cols-3">
      <Card size="sm">
        <CardHeader>
          <CardTitle>
            <CardLink href="#">
              Default <IconArrowUpRight className="inline size-4" />
            </CardLink>
          </CardTitle>
          <CardDescription>The shadow lifts on hover.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="outline" size="sm">
        <CardHeader>
          <CardTitle>
            <CardLink href="#">
              Outline <IconArrowUpRight className="inline size-4" />
            </CardLink>
          </CardTitle>
          <CardDescription>A soft fill on hover.</CardDescription>
        </CardHeader>
      </Card>
      <Card variant="muted" size="sm">
        <CardHeader>
          <CardTitle>
            <CardLink href="#">
              Muted <IconArrowUpRight className="inline size-4" />
            </CardLink>
          </CardTitle>
          <CardDescription>The fill deepens on hover.</CardDescription>
        </CardHeader>
      </Card>
    </div>
  )
}

Rahmen

Füge border-b zum Header oder border-t zum Footer hinzu, und die Karte ergänzt passendes Padding auf der Innenseite.

import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardBorders() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader className="border-b">
        <CardTitle>Notifications</CardTitle>
        <CardDescription>Choose what you hear about.</CardDescription>
      </CardHeader>
      <CardContent>
        <p>Mentions, replies and deploy failures are on.</p>
      </CardContent>
      <CardFooter className="border-t">
        <Button variant="outline">Reset</Button>
        <Button className="ms-auto">Save</Button>
      </CardFooter>
    </Card>
  )
}

Verschachtelt

Eine Karte in CardContent leitet ihren Radius vom Radius des Elternelements abzüglich des Abstands ab, sodass die Ecken konzentrisch bleiben.

import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

export function CardNested() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Usage</CardTitle>
        <CardDescription>This billing period.</CardDescription>
      </CardHeader>
      <CardContent>
        <Card variant="muted" size="sm">
          <CardHeader>
            <CardTitle>Build minutes</CardTitle>
            <CardDescription>1,240 of 3,000 used</CardDescription>
          </CardHeader>
        </Card>
        <Card variant="outline" size="sm">
          <CardHeader>
            <CardTitle>
              <CardLink href="#">Bandwidth</CardLink>
            </CardTitle>
            <CardDescription>84 GB of 100 GB used</CardDescription>
          </CardHeader>
        </Card>
      </CardContent>
    </Card>
  )
}

Langer Inhalt

Titel ohne Umbruchstellen, lange URLs, Emoji und CJK-Text werden in einer schmalen Karte umgebrochen, statt sie breiter zu drücken.

import { Button } from "@/components/ui/button"
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardLongContent() {
  return (
    <Card size="sm" className="w-72 max-w-full">
      <CardHeader>
        <CardTitle>
          Supercalifragilisticexpialidocious_unbroken_title_that_never_ends
        </CardTitle>
        <CardDescription>
          https://example.com/a/very/long/path/that/keeps/going/and/going?with=query
        </CardDescription>
        <CardAction>
          <Button variant="outline" size="sm">
            Edit
          </Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        <p>👩‍👩‍👧‍👦 你好世界,这是一个很长的中文句子用于测试换行</p>
      </CardContent>
      <CardFooter>
        <Button variant="outline">Cancel</Button>
        <Button>Continue</Button>
      </CardFooter>
    </Card>
  )
}

Semantik

Jeder Teil nimmt eine render-Prop, sodass eine Karte ein <article> mit echter Überschrift, Header und Footer sein kann.

import { Button } from "@/components/ui/button"
import {
  Card,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"

export function CardSemantics() {
  return (
    <Card render={<article />} className="w-full max-w-sm">
      <CardHeader render={<header />}>
        <CardTitle render={<h3 />}>Rendered as an article</CardTitle>
        <CardDescription render={<p />}>
          Every part accepts render for real semantics.
        </CardDescription>
      </CardHeader>
      <CardFooter render={<footer />}>
        <Button variant="outline">Read more</Button>
      </CardFooter>
    </Card>
  )
}

Rechts nach links

Die Aktion wandert an den Inline-Start, und der Footer folgt der Leserichtung.

import { IconDotsVertical } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import {
  Card,
  CardAction,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLink,
  CardTitle,
} from "@/components/ui/card"

export function CardRtl() {
  return (
    <div dir="rtl" className="w-full max-w-sm">
      <Card>
        <CardHeader>
          <CardTitle>
            <CardLink href="#">موقع أكمي</CardLink>
          </CardTitle>
          <CardDescription>
            موقع تسويقي ووثائق، يُنشر مع كل دفعة.
          </CardDescription>
          <CardAction>
            <Button variant="ghost" size="icon-sm" aria-label="المزيد">
              <IconDotsVertical />
            </Button>
          </CardAction>
        </CardHeader>
        <CardFooter>
          <Button variant="outline">إلغاء</Button>
          <Button>متابعة</Button>
        </CardFooter>
      </Card>
    </div>
  )
}

Eine Karte ist für sich nicht fokussierbar. Diese Tasten gelten, wenn sie einen <CardLink /> enthält.

TasteAktion
TabFokussiert den Karten-Link, der den Fokusring um die ganze Karte zeichnet, und wandert dann zu jedem Bedienelement darin.
EnterFolgt dem Karten-Link.
  • Der zugängliche Name des Links ist sein eigener Text, belasse <CardLink /> daher im Titel, statt die ganze Karte in einen Anker zu packen.
  • Bedienelemente in einer klickbaren Karte liegen über der Trefferfläche des Links, sodass sie per Zeiger und Tastatur erreichbar bleiben.
  • Nutze render auf <CardTitle />, um dem Titel die Überschriftenebene zu geben, die zu deiner Seite passt.

Jeder Teil rendert standardmäßig ein <div>, akzeptiert dessen Attribute und nimmt eine render-Prop zum Austauschen des Elements.

PropTypStandard
variant
"default" | "outline" | "muted""default"
sizeSkaliert Abstand, Radius und Titelgröße gemeinsam.
"default" | "sm" | "flush""default"
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="card"Die Card in CSS ansprechen.
data-variantDie aktuelle Variante.
data-sizeDie aktuelle Größe.
data-linkVorhanden, solange die Karte einen CardLink enthält.
--card-spacingInnenabstand, den jeder Teil teilt. Wird von size gesetzt.
--card-radiusEckenradius. Verschachtelte Karten leiten ihren vom Elternelement ab.
--card-title-sizeSchriftgröße, die CardTitle verwendet.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="card-header"Ein Raster, das CardAction in die obere Endecke setzt.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="card-title"Den Titel ansprechen.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="card-description"Die Beschreibung ansprechen.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="card-action"Erstreckt sich über die Titel- und Beschreibungszeilen, über der Trefferfläche eines CardLink.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="card-content"Gibt Radius und Abstand der Karte an verschachtelte Karten weiter.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="card-footer"Den Footer ansprechen.
PropTypStandard
href
string–
renderÜbergib hier die Link-Komponente deines Routers.
ReactElement | (props, state) => ReactElement<a>
AttributBeschreibung
data-slot="card-link"Dehnt seine Trefferfläche über die ganze Karte.

In Blocks verwendet

Blocks, die auf Card aufbauen.