Card
Overview
A container that groups related content and actions into a bordered, shadowed surface. SCard is a UI-only component: it composes the admitted Aria collapsible primitives (CollapsibleRoot / CollapsibleContent / CollapsibleTrigger) for the only real behavior a card has — collapsing — and owns its own header/title/description/footer chrome, styled by the cardVariants recipe (8 slots, 6 sizes, scrollable/split flags).
Use it for dashboards, profile blocks, settings panels, or any content that benefits from a titled, sectioned container. Prefer list or table for repetitive data rows, and popover/dialog for floating or modal surfaces.
The card is collapsible by default — the content area animates open/closed and can be driven with v-model:open. There is no Aria card family: the chrome nodes are presentation-only, so they live in the UI layer and keep data-vean-card-* attributes for styling and tests.
Usage
Features
- 🧩 UI-only over admitted primitives — the collapsible behavior comes from the Aria
collapsiblefamily;SCardowns the chrome and injects thecardVariantsclasses throughprovideCollapsibleUi - 🧱 Composite structure —
header/title/description/content/footerplustitle-leading/title-trailing/extraslots - 🔽 Collapsible — the content animates open/closed (
CollapsibleRoot/CollapsibleContent), controlled withv-model:open/defaultOpen - ➗ Split sections —
splitaddsdivide-ydividers between title/content/footer - 📜 Scrollable content —
scrollablemakes the content area scroll when it exceeds the card height - 🎨 6 sizes — xs–2xl
sizematchingThemeSize - 🎛️ Per-part control —
title/descriptionas props or slots;headerProps/contentProps/footerProps/titlePropsforwarded to each part - ♿ Accessible by default — title renders as a real
<h3>, the collapsible trigger exposesaria-expanded, andaxe-corereports zero violations
Component family
SCard— the entry component; rendersCollapsibleRoot(defaultOpen: true) plus theheader/title-root/title/description/content/footernodes, and feeds thecardVariantsrecipe toprovideCollapsibleUiSCardCollapsibleTrigger— the collapsible trigger bound to the card state; renders a chevronSButtonIconby default and exposes theopenstate through its slot props
Demos
Basic
Title
Card description
Title Slot
Title
TrailingOnly Content
Collapsible
Title
API
Aria Layer
9Card
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<CardUi> | — | Per-slot class overrides for the component. |
scrollable | boolean | false | If true, the content will be scrollable when the root has height and content is taller than the root |
split | boolean | false | If true, the card will add divider between title and content and footer |
title | string | — | Title text rendered by the component. |
titleAs | AsTag | 'h3' | Heading element the title renders as.
A card is embedded at many depths, so the heading level cannot be a
constant: the default `h3` lands right under the page `<h1>` on surfaces
whose card holds the first heading, which is an invalid heading order.
Pass the level that matches the surrounding outline. |
description | string | — | Description text rendered by the component. |
headerProps | CardHeaderProps | — | Properties forwarded to the header element. |
contentProps | CardContentProps | — | Properties forwarded to the content element. |
footerProps | CardFooterProps | — | Properties forwarded to the footer element. |
titleRootProps | CardTitleRootProps | — | Properties forwarded to the title root element. |
titleProps | CardTitleProps | — | Properties forwarded to the title element. |
descriptionProps | CardDescriptionProps | — | Properties forwarded to the description element. |
defaultOpen | boolean | — | The open state of the collapsible when it is initially rendered.
Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the collapsible. Can be bound with `v-model`. |
disabled | boolean | — | When `true`, prevents the user from interacting with the collapsible. |
unmountOnHide | boolean | — | When `true`, the element will be unmounted on closed state. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean | Event handler called when the open state of the collapsible changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | — | Custom content for the default slot. |
header | — | Custom content for the header slot. |
title | — | Custom content for the title slot. |
title-leading | — | Custom content for the title leading slot. |
title-trailing | — | Custom content for the title trailing slot. |
extra | — | Custom content for the extra slot. |
footer | — | Custom content for the footer slot. |
description | — | Custom content for the description slot. |
CardCollapsibleTrigger
Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabledCollapsible | boolean | — | When `true`, prevents the user from toggling the collapsible. |
type | ButtonType | 'button' | The type of the button element. Can be one of 'button', 'submit', or 'reset'. |
disabled | boolean | — | Whether the component is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
CardContent
Props
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | boolean | — | Used to force mounting when more control is needed.
Useful when controlling animation with Vue animation libraries. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
CardDescription
CardFooter
CardHeader
CardRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | — | The open state of the collapsible when it is initially rendered.
Use when you do not need to control its open state. |
open | boolean | — | The controlled open state of the collapsible. Can be bound with `v-model`. |
disabled | boolean | — | When `true`, prevents the user from interacting with the collapsible. |
unmountOnHide | boolean | — | When `true`, the element will be unmounted on closed state. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean | Event handler called when the open state of the collapsible changes. |
CardTitle
CardTitleRoot
Notes
Architecture and benchmark differences
Card is the UI-only exemplar of the Aria admission rule: its only real logic is collapsing, which the admitted collapsible family already provides, so no Aria card family exists. SCard owns the structure orchestration (header/footer visibility, default title/description) and hands the recipe’s root / content / trigger slots to provideCollapsibleUi, so CollapsibleRoot / CollapsibleContent / CollapsibleTrigger resolve their own classes from cardVariants. This mirrors shadcn/ui’s composition-first approach, unlike Ant Design, Element Plus, Mantine and Naive UI which ship a single styled card with title/extra/actions props. VeanUI makes the card collapsible by default through the CollapsibleRoot primitive, a deliberate extension most libraries do not offer on a card; split and scrollable are toggled via recipe variants rather than layout props.
| Capability | VeanUI | shadcn/ui | Ant Design Card | Element Plus Card | Mantine Card | Naive UI Card |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | ✅ | — | — | — | — |
| Header / title / description | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Footer | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Extra (actions) slot | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| Collapsible content | ✅ | — | — | — | — | — |
| Split / divider | ✅ | — | ✅ | ✅ | ✅ | — |
| Scrollable content | ✅ | — | ✅ | — | — | ✅ |
| Size variants (6) | ✅ | — | ✅ | ✅ | ✅ | ✅ |
Per-part *Props channels | ✅ | ✅ | — | — | — | — |
— = unsupported or a different interaction model.
Cautions
- The card is collapsible and defaults to open (
defaultOpen: true). When collapsed, the content (and footer) is removed from the flow; footer visibility depends on the open state. - The title renders as a fixed
<h3>. If your page’s heading hierarchy differs, wrap or restyle the title so it does not skip levels. - The content node carries
tabindex="-1"so a scrollable body can receive programmatic focus; it is not an interactive control by itself. Override it throughcontentProps. SCardCollapsibleTriggerrenders an icon-only button, so passaria-label(e.g.aria-label="Toggle card content") to give it an accessible name.splitappliesdivide-y; combine it with asizeto control the vertical rhythm.scrollableonly takes effect when the card has a constrained height.- The content padding adjusts automatically based on
data-header-visible/data-footer-visible(and the collapsibledata-state) so a bare content-only card still looks balanced.
Roadmap
No blocking gaps identified for the core card API. A card-level interactive action bar / CardActions shortcut and hover-lift style variants are evaluated enhancements tracked in docs/roadmap/README.md.
FAQ
How do I build a card with a title and description?
Pass title/description props (or the matching slots):
<SCard title="Settings" description="Manage your preferences">
<p>Body content</p>
</SCard>How do I add actions to the header?
Use the extra slot (or title-leading/title-trailing around the title):
<SCard title="Profile">
<template #extra><SButton variant="pure">Edit</SButton></template>
<p>Body content</p>
</SCard>How do I make a collapsible card?
Bind open with v-model (or set default-open):
<SCard v-model:open="open" title="Collapsible">
<p>This content can collapse.</p>
</SCard>How do I make the content scrollable?
Keep the card height constrained and set scrollable:
<SCard scrollable class="h-80" title="Long list">
<p v-for="i in 50" :key="i">Row {{ i }}</p>
</SCard>How do I split the sections with dividers?
Set split to add divide-y dividers between header/content/footer:
<SCard split title="Divided">
<template #footer><SButton>Save</SButton></template>
<p>Body content</p>
</SCard>