Card

Display content in a card layout with header, content, and footer sections.

Upgrade your plan
Unlock advanced analytics and unlimited projects.
  • Unlimited projects and members
  • Advanced analytics dashboard
  • Priority support
<twig:ui:Card class="w-full max-w-sm">
    <twig:ui:Card:Header>
        <twig:ui:Card:Title>Upgrade your plan</twig:ui:Card:Title>
        <twig:ui:Card:Description>Unlock advanced analytics and unlimited projects.</twig:ui:Card:Description>
        <twig:ui:Card:Action>
            <twig:ui:Button variant="ghost" size="icon-sm" aria-label="Dismiss">
                <twig:ux:icon name="lucide:x" />
            </twig:ui:Button>
        </twig:ui:Card:Action>
    </twig:ui:Card:Header>
    <twig:ui:Card:Content>
        <ul class="flex flex-col gap-2">
            <li class="flex items-center gap-2">
                <twig:ux:icon name="lucide:check" class="size-4 text-success" />
                Unlimited projects and members
            </li>
            <li class="flex items-center gap-2">
                <twig:ux:icon name="lucide:check" class="size-4 text-success" />
                Advanced analytics dashboard
            </li>
            <li class="flex items-center gap-2">
                <twig:ux:icon name="lucide:check" class="size-4 text-success" />
                Priority support
            </li>
        </ul>
    </twig:ui:Card:Content>
    <twig:ui:Card:Footer class="flex-col gap-2">
        <twig:ui:Button class="w-full">Upgrade</twig:ui:Button>
        <twig:ui:Button variant="outline" class="w-full">Compare plans</twig:ui:Button>
    </twig:ui:Card:Footer>
</twig:ui:Card>

Usage

Compose a card from <twig:ui:Card:Header>, <twig:ui:Card:Content> and <twig:ui:Card:Footer>. Every section is optional and can appear more than once.

Card title
Card description

This is the card content area.

Card footer

<twig:ui:Card class="w-full max-w-sm">
    <twig:ui:Card:Header>
        <twig:ui:Card:Title>Card title</twig:ui:Card:Title>
        <twig:ui:Card:Description>Card description</twig:ui:Card:Description>
    </twig:ui:Card:Header>
    <twig:ui:Card:Content>
        <p>This is the card content area.</p>
    </twig:ui:Card:Content>
    <twig:ui:Card:Footer>
        <p class="text-muted-foreground">Card footer</p>
    </twig:ui:Card:Footer>
</twig:ui:Card>

Examples

With action

Add the optional <twig:ui:Card:Action> inside the header to place an action in the top corner of the card, such as a button or a badge.

Notifications
You have 3 unread messages.

Your team mentioned you in 3 conversations today.

<twig:ui:Card class="w-full max-w-sm">
    <twig:ui:Card:Header>
        <twig:ui:Card:Title>Notifications</twig:ui:Card:Title>
        <twig:ui:Card:Description>You have 3 unread messages.</twig:ui:Card:Description>
        <twig:ui:Card:Action>
            <twig:ui:Button variant="outline" size="sm">
                <twig:ux:icon name="lucide:check-check" data-icon="inline-start" />
                Mark all as read
            </twig:ui:Button>
        </twig:ui:Card:Action>
    </twig:ui:Card:Header>
    <twig:ui:Card:Content>
        <p>Your team mentioned you in 3 conversations today.</p>
    </twig:ui:Card:Content>
</twig:ui:Card>

With header border

The footer is separated from the content by default. To separate the header too, add border-b; the matching padding is applied automatically.

Invoice #1042
Issued on March 4, 2026.
Subtotal
$240.00
Taxes
$48.00
Total
$288.00

Due in 14 days

<twig:ui:Card class="w-full max-w-sm">
    <twig:ui:Card:Header class="border-b border-neutral-950/10 dark:border-white/10">
        <twig:ui:Card:Title>Invoice #1042</twig:ui:Card:Title>
        <twig:ui:Card:Description>Issued on March 4, 2026.</twig:ui:Card:Description>
    </twig:ui:Card:Header>
    <twig:ui:Card:Content>
        <dl class="flex flex-col gap-2">
            <div class="flex justify-between">
                <dt class="text-neutral-500 dark:text-neutral-400">Subtotal</dt>
                <dd>$240.00</dd>
            </div>
            <div class="flex justify-between">
                <dt class="text-neutral-500 dark:text-neutral-400">Taxes</dt>
                <dd>$48.00</dd>
            </div>
            <div class="flex justify-between font-medium">
                <dt>Total</dt>
                <dd>$288.00</dd>
            </div>
        </dl>
    </twig:ui:Card:Content>
    <twig:ui:Card:Footer class="justify-between">
        <p class="text-neutral-500 dark:text-neutral-400">Due in 14 days</p>
        <twig:ui:Button size="sm">Pay now</twig:ui:Button>
    </twig:ui:Card:Footer>
</twig:ui:Card>

With image

Place an <img> as the first direct child for full-bleed media. The top padding is removed and the corners are rounded automatically. Serve a resized, compressed file and add loading="lazy" so the image does not slow the page down.

Preview of the HarmonyUI component library
Design without friction
Ship polished interfaces with ready-made Twig components.
<twig:ui:Card class="w-full max-w-sm">
    <img
        src="{{ 'media/home-hero.png'|imagine_filter('doc_demo') }}"
        alt="Preview of the HarmonyUI component library"
        loading="lazy"
        width="768"
        height="432"
        class="aspect-video w-full object-cover"
    />
    <twig:ui:Card:Header>
        <twig:ui:Card:Title>Design without friction</twig:ui:Card:Title>
        <twig:ui:Card:Description>Ship polished interfaces with ready-made Twig components.</twig:ui:Card:Description>
    </twig:ui:Card:Header>
    <twig:ui:Card:Footer>
        <twig:ui:Button variant="outline" class="w-full">
            Read the story
            <twig:ux:icon name="lucide:arrow-right" data-icon="inline-end" />
        </twig:ui:Button>
    </twig:ui:Card:Footer>
</twig:ui:Card>

Sizes

The size prop scales the paddings and gaps of every section at once: sm (12px), default (16px), lg (24px) and xl (32px). The sm size also shrinks the title.

Small
12px spacing and a smaller title.

Compact, for dense layouts.

Default
16px spacing.

The size to reach for first.

Large
24px spacing.

Roomier, for standalone sections.

Extra large
32px spacing.

Generous, for hero content.

<div class="grid w-full max-w-2xl gap-4 sm:grid-cols-2">
    <twig:ui:Card size="sm">
        <twig:ui:Card:Header>
            <twig:ui:Card:Title>Small</twig:ui:Card:Title>
            <twig:ui:Card:Description>12px spacing and a smaller title.</twig:ui:Card:Description>
        </twig:ui:Card:Header>
        <twig:ui:Card:Content>
            <p>Compact, for dense layouts.</p>
        </twig:ui:Card:Content>
    </twig:ui:Card>
    <twig:ui:Card>
        <twig:ui:Card:Header>
            <twig:ui:Card:Title>Default</twig:ui:Card:Title>
            <twig:ui:Card:Description>16px spacing.</twig:ui:Card:Description>
        </twig:ui:Card:Header>
        <twig:ui:Card:Content>
            <p>The size to reach for first.</p>
        </twig:ui:Card:Content>
    </twig:ui:Card>
    <twig:ui:Card size="lg">
        <twig:ui:Card:Header>
            <twig:ui:Card:Title>Large</twig:ui:Card:Title>
            <twig:ui:Card:Description>24px spacing.</twig:ui:Card:Description>
        </twig:ui:Card:Header>
        <twig:ui:Card:Content>
            <p>Roomier, for standalone sections.</p>
        </twig:ui:Card:Content>
    </twig:ui:Card>
    <twig:ui:Card size="xl">
        <twig:ui:Card:Header>
            <twig:ui:Card:Title>Extra large</twig:ui:Card:Title>
            <twig:ui:Card:Description>32px spacing.</twig:ui:Card:Description>
        </twig:ui:Card:Header>
        <twig:ui:Card:Content>
            <p>Generous, for hero content.</p>
        </twig:ui:Card:Content>
    </twig:ui:Card>
</div>

Custom spacing

The predefined sizes set the --card-spacing variable, so any other value is one utility class away: override the variable on the root and every section follows.

Custom spacing
This card overrides the variable to 40px.

Use a predefined size when one fits; reach for the variable when it does not.

<twig:ui:Card class="w-full max-w-sm [--card-spacing:--spacing(10)]">
    <twig:ui:Card:Header>
        <twig:ui:Card:Title>Custom spacing</twig:ui:Card:Title>
        <twig:ui:Card:Description>This card overrides the variable to 40px.</twig:ui:Card:Description>
    </twig:ui:Card:Header>
    <twig:ui:Card:Content>
        <p>Use a predefined size when one fits; reach for the variable when it does not.</p>
    </twig:ui:Card:Content>
</twig:ui:Card>

RTL

Set dir="rtl" on the root to render the card for right-to-left languages. The default styles use logical properties, so the header action and text alignment follow the reading direction.

ترقية خطتك
احصل على تحليلات متقدمة ومشاريع غير محدودة.

تمت الإشارة إليك في ٣ محادثات اليوم.

<twig:ui:Card dir="rtl" class="w-full max-w-sm">
    <twig:ui:Card:Header>
        <twig:ui:Card:Title>ترقية خطتك</twig:ui:Card:Title>
        <twig:ui:Card:Description>احصل على تحليلات متقدمة ومشاريع غير محدودة.</twig:ui:Card:Description>
        <twig:ui:Card:Action>
            <twig:ui:Button variant="ghost" size="icon-sm" aria-label="إغلاق">
                <twig:ux:icon name="lucide:x" />
            </twig:ui:Button>
        </twig:ui:Card:Action>
    </twig:ui:Card:Header>
    <twig:ui:Card:Content>
        <p>تمت الإشارة إليك في ٣ محادثات اليوم.</p>
    </twig:ui:Card:Content>
    <twig:ui:Card:Footer>
        <twig:ui:Button class="w-full">ترقية</twig:ui:Button>
    </twig:ui:Card:Footer>
</twig:ui:Card>

API Reference

<twig:ui:Card>

Prop Type Description
size 'sm' | 'default' | 'lg' | 'xl' The spacing scale of the card. Defaults to default
as string The HTML tag to render. Defaults to div
Block Description
content The card sections: header, content and footer

<twig:ui:Card:Header>

Wraps the title, description and optional action. A column is reserved for the action automatically when one is present.

Prop Type Description
as string The HTML tag to render. Defaults to div
Block Description
content The title, description and optional action

<twig:ui:Card:Title>

Prop Type Description
as string The HTML tag to render. Defaults to div
Block Description
content The card title

<twig:ui:Card:Description>

Prop Type Description
as string The HTML tag to render. Defaults to div
Block Description
content The card description

<twig:ui:Card:Action>

Placed in the top corner of the header, spanning the title and description rows.

Prop Type Description
as string The HTML tag to render. Defaults to div
Block Description
content The action, usually a <twig:ui:Button> or a badge

<twig:ui:Card:Content>

Prop Type Description
as string The HTML tag to render. Defaults to div
Block Description
content The main content of the card

<twig:ui:Card:Footer>

Rendered as a muted band separated from the content by a border.

Prop Type Description
as string The HTML tag to render. Defaults to div
Block Description
content The footer content, usually actions or secondary text