Surface container with optional title, action, and footer.
Introduction
Cards are surface containers for grouping related information—product summaries, settings panels, or dialog content. They can stand alone on a page or be composed inside a Modal for structured dialogs with title, body, and footer actions.
Bridge UI Card exposes slots for the header (title, action), default body content, and an optional footer separated by a divider.
Import
import { Card } from "@bridge-ui/vue/Components/Card";import { Card } from "@bridge-ui/react/Components/Card";Basic usage
Provide a title for the header and use children (or the default slot in Vue) for the body content.
<script setup lang="ts">
import { Card } from "@bridge-ui/vue/Components/Card";
</script>
<template>
<div class="w-full">
<Card title="Card title">Card body content goes here.</Card>
</div>
</template>
import { Card } from "@bridge-ui/react/Components/Card";
export default function CardBasic() {
return (
<div className="w-full">
<Card title="Card title">Card body content goes here.</Card>
</div>
);
}
Variants
Set the visual style with the variant prop. Available variants: elevated (default), outlined, flat, tonal, plain, and text. Shadow is only applied when variant is elevated.
<script setup lang="ts">
import { Card } from "@bridge-ui/vue/Components/Card";
</script>
<template>
<div class="flex w-full flex-col gap-4">
<Card variant="elevated" title="elevated card">
Surface using the elevated variant.
</Card>
<Card variant="outlined" title="outlined card">
Surface using the outlined variant.
</Card>
<Card variant="flat" title="flat card">
Surface using the flat variant.
</Card>
<Card variant="tonal" title="tonal card">
Surface using the tonal variant.
</Card>
</div>
</template>
import { Card } from "@bridge-ui/react/Components/Card";
export default function CardVariants() {
return (
<div className="flex w-full flex-col gap-4">
<Card variant="elevated" title="elevated card">
Surface using the elevated variant.
</Card>
<Card variant="outlined" title="outlined card">
Surface using the outlined variant.
</Card>
<Card variant="flat" title="flat card">
Surface using the flat variant.
</Card>
<Card variant="tonal" title="tonal card">
Surface using the tonal variant.
</Card>
</div>
);
}
Padding
Control internal spacing with the padding prop: none, small, medium (default), or large.
<script setup lang="ts">
import { Card } from "@bridge-ui/vue/Components/Card";
const bodyText =
"Card body content. Use this area for descriptions, forms, or any main content.";
</script>
<template>
<div class="flex flex-col gap-4">
<Card padding="none" title='padding="none"'>{{ bodyText }}</Card>
<Card padding="small" title='padding="small"'>{{ bodyText }}</Card>
<Card padding="medium" title='padding="medium"'>{{ bodyText }}</Card>
<Card padding="large" title='padding="large"'>{{ bodyText }}</Card>
</div>
</template>
import { Card } from "@bridge-ui/react/Components/Card";
const bodyText =
"Card body content. Use this area for descriptions, forms, or any main content.";
const CardPadding = () => (
<div className="flex flex-col gap-4">
<Card padding="none" title='padding="none"'>
{bodyText}
</Card>
<Card padding="small" title='padding="small"'>
{bodyText}
</Card>
<Card padding="medium" title='padding="medium"'>
{bodyText}
</Card>
<Card padding="large" title='padding="large"'>
{bodyText}
</Card>
</div>
);
export default CardPadding;
Rounded
Use the rounded prop to control border radius. Available values: none, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, and full (defaults vary by component).
<script setup lang="ts">
import { Card } from "@bridge-ui/vue/Components/Card";
</script>
<template>
<div class="grid w-full grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
<Card rounded="none" title="rounded=none">Short body content.</Card>
<Card rounded="xs" title="rounded=xs">Short body content.</Card>
<Card rounded="sm" title="rounded=sm">Short body content.</Card>
<Card rounded="md" title="rounded=md">Short body content.</Card>
<Card rounded="lg" title="rounded=lg">Short body content.</Card>
<Card rounded="xl" title="rounded=xl">Short body content.</Card>
<Card rounded="2xl" title="rounded=2xl">Short body content.</Card>
<Card rounded="full" title="rounded=full">Short body content.</Card>
</div>
</template>
import { Card } from "@bridge-ui/react/Components/Card";
export default function CardRounded() {
return (
<div className="grid w-full grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
<Card rounded="none" title='rounded="none"'>
Short body content.
</Card>
<Card rounded="xs" title='rounded="xs"'>
Short body content.
</Card>
<Card rounded="sm" title='rounded="sm"'>
Short body content.
</Card>
<Card rounded="md" title='rounded="md"'>
Short body content.
</Card>
<Card rounded="lg" title='rounded="lg"'>
Short body content.
</Card>
<Card rounded="xl" title='rounded="xl"'>
Short body content.
</Card>
<Card rounded="2xl" title='rounded="2xl"'>
Short body content.
</Card>
<Card rounded="full" title='rounded="full"'>
Short body content.
</Card>
</div>
);
}
Action slot
Add header controls with the action slot. In React, use slots.action; in Vue, use the #action slot.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { Card } from "@bridge-ui/vue/Components/Card";
import { MoreHorizontal } from "@lucide/vue";
</script>
<template>
<div class="w-full">
<Card title="Card with actions">
Use the action slot for header controls.
<template #action>
<Button
size="sm"
density="mini"
variant="flat"
aria-label="More options"
>
<MoreHorizontal class="size-4" />
</Button>
</template>
</Card>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { Card } from "@bridge-ui/react/Components/Card";
import { MoreHorizontal } from "lucide-react";
export default function CardAction() {
return (
<div className="w-full">
<Card
title="Card with actions"
slots={{
action: (
<Button
size="sm"
density="mini"
variant="flat"
aria-label="More options"
>
<MoreHorizontal className="size-4" />
</Button>
),
}}
>
Use the action slot for header controls.
</Card>
</div>
);
}
Footer slot
The footer slot renders below the body, separated by a divider (unless borderless is set). Use it for secondary actions such as Cancel and Confirm buttons.
<script setup lang="ts">
import { Button } from "@bridge-ui/vue/Components/Button";
import { Card } from "@bridge-ui/vue/Components/Card";
</script>
<template>
<div class="w-full">
<Card title="Confirm changes">
Are you sure you want to proceed?
<template #footer>
<div class="flex justify-end gap-2">
<Button size="sm" variant="flat">Cancel</Button>
<Button size="sm" color="primary">Confirm</Button>
</div>
</template>
</Card>
</div>
</template>
import { Button } from "@bridge-ui/react/Components/Button";
import { Card } from "@bridge-ui/react/Components/Card";
export default function CardFooter() {
return (
<div className="w-full">
<Card
title="Confirm changes"
slots={{
footer: (
<div className="flex justify-end gap-2">
<Button size="sm" variant="flat">
Cancel
</Button>
<Button size="sm" color="primary">
Confirm
</Button>
</div>
),
}}
>
Are you sure you want to proceed?
</Card>
</div>
);
}
Customization
Fine-tune appearance with padding, rounded, shadow, borderless, and the classes prop to target individual parts (root, header, title, body, footer).
<script setup lang="ts">
import { Card } from "@bridge-ui/vue/Components/Card";
</script>
<template>
<div class="w-full">
<Card
borderless
shadow="lg"
rounded="2xl"
padding="large"
variant="tonal"
title="Styled card"
:classes="{
body: 'leading-relaxed',
title: 'text-xl font-bold tracking-tight',
}"
>
Customize padding, rounded corners, shadow, and part classes.
</Card>
</div>
</template>
import { Card } from "@bridge-ui/react/Components/Card";
export default function CardCustomization() {
return (
<div className="w-full">
<Card
borderless
shadow="lg"
rounded="2xl"
padding="large"
variant="tonal"
title="Styled card"
classes={{
body: "leading-relaxed",
title: "text-xl font-bold tracking-tight",
}}
>
Customize padding, rounded corners, shadow, and part classes.
</Card>
</div>
);
}
Related components
Accessibility
Card is a presentational container and does not set ARIA roles by default. When the card heading summarizes the content, use the title prop or #title slot so the heading is rendered as a semantic heading element.
If the card contains interactive controls, keep a logical heading order and ensure buttons or links inside the card have accessible names.
Anatomy
The Card component is composed of a root surface with optional header, body, and footer regions:
<div> <!-- root -->
<header> <!-- header -->
<h3> <!-- title -->
<div> <!-- action slot -->
</header>
<div> <!-- body (default slot / children) -->
<footer> <!-- footer slot -->
</div>Target each part with the classes prop: root, header, title, body, and footer.
API
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | — | The card heading. |
| variant | CardVariant | "elevated" | Visual style of the card. |
| borderless | boolean | false | Removes header and footer borders. |
| padding | CardPadding | "medium" | Padding for header, body, and footer. |
| rounded | CardRounded | "sm" | Border radius. |
| shadow | CardShadow | "sm" | Shadow size. Only applied when variant is elevated. |
| classes | CardClasses | — | Classes for root, header, title, body, footer. |
| customProps | CardCustomProps | — | Props forwarded to internal parts. Root HTML attributes stay on the component top level. |
| slots | CardSlots | — | React slots: header, title, action, footer. Vue slots: header, title, action, default, footer. |