HextaUI

Card

コンテンツをまとめるサーフェスです。3つのバリアント、端まで広がるメディア、同心円状の角丸、カード全体のリンクに対応します。

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

コンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。

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

バリアント

default は細いリングと柔らかい影を持ち、outline は塗りと影をなくし、muted は控えめなセクション向けに塗りの面を使います。

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

サイズ

size="sm" は余白、角丸、タイトルサイズをまとめて小さくするため、コンパクトなカードも同じファミリーの一部に見えます。size="flush" は内側の余白をなくし、リストや、独自のパディングを持つ折りたたみ可能な行などのカスタムレイアウト向けです。

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

メディア

先頭または末尾に置いた画像、動画、picture、アスペクト比は、端から端まで広がります。カードはその側のパディングをなくし、メディアをカードの角でクリップします。

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

クリック可能なカード

タイトルに <CardLink /> を入れると、カード全体が 1 つのリンクになります。内側のボタンなどのコントロールはクリック可能なままで、ここにある星のように、それぞれ独自のフォーカスを保ちます。

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

クリック可能な variant

各 variant は、リンクを含むとそれぞれ独自のホバーフィードバックを持ち、リンクがキーボードでフォーカスされると、フォーカスリングがカード全体を囲みます。

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

枠線

ヘッダーに border-b、フッターに border-t を追加すると、カードが内側に対応するパディングを追加します。

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

入れ子

CardContent 内のカードは、親の角丸の半径から余白を引いた値を自分の角丸にするため、角は同心のままです。

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

長いコンテンツ

区切りのないタイトル、長い URL、絵文字、CJK のテキストは、狭いカードでもカードを広げず、内側で折り返されます。

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

セマンティクス

すべてのパーツが render prop を受け取るため、カードを本物の見出し、ヘッダー、フッターを持つ <article> にできます。

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

右から左

アクションは行頭側に移動し、フッターは読む方向に従います。

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

カード自体はフォーカスできません。これらのキーは、<CardLink /> を含む場合に適用されます。

キーアクション
Tabカードのリンクにフォーカスしてカード全体にフォーカスリングを描画し、その後カード内の各コントロールに移動します。
Enterカードのリンクに追従します。
  • リンクのアクセシブルな名前はそのテキスト自体になるため、カード全体をアンカーで囲まず、<CardLink /> をタイトルの内側に置いてください。
  • クリック可能なカード内のコントロールは、リンクのヒット領域より前面に持ち上げられるため、ポインターとキーボードの両方で操作できます。
  • <CardTitle /> に render を使うと、ページに合った見出しレベルをタイトルに与えられます。

各パーツはデフォルトで <div> をレンダリングし、その属性を受け付け、要素を差し替える render prop を受け取ります。

プロパティ型デフォルト
variant
"default" | "outline" | "muted""default"
size余白、角丸、タイトルサイズをまとめて拡大縮小します。
"default" | "sm" | "flush""default"
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="card"CSSでカードを指定します。
data-variant現在のバリアント。
data-size現在のサイズ。
data-linkカードに CardLink が含まれている間、付与されます。
--card-spacingすべてのパーツで共有される内側の余白。size によって設定されます。
--card-radius角丸の半径。ネストしたカードは親から算出します。
--card-title-sizeCardTitle に使われるフォントサイズ。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="card-header"CardAction を上端の終了側の角に配置するグリッド。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="card-title"タイトルを指定します。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="card-description"説明を指定します。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="card-action"タイトルと説明の行にまたがり、CardLink のヒット領域の上に配置されます。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="card-content"カードの角丸と余白を、ネストしたカードに引き継ぎます。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="card-footer"フッターを指定します。
プロパティ型デフォルト
href
string–
renderここにルーターのリンクコンポーネントを渡します。
ReactElement | (props, state) => ReactElement<a>
属性説明
data-slot="card-link"ヒット領域をカード全体に広げます。

使用しているブロック

Card の上に構築されるブロック。