Component reference
CardContent
The body of a card.
Preview
CardContent has no story of its own; this one draws it as part of Card.
Project and embedded checks
One project record whose internal checks share a reading surface; standalone items can use their own surface. Actions only echo locally.
render: () => (
<Measure>
<Stack gap="relaxed" data-card-example>
<Card as="article" aria-labelledby="card-project-title">
<CardHeader>
<CardTitle id="card-project-title">Component checks before release</CardTitle>
<CardDescription>
Meowland · This check keeps two changes that need your confirmation.
</CardDescription>
<CardAction>
<IconButton
label="View project record"
data-card-action="Selected this project record"
>
<Icon name="eye" />
</IconButton>
</CardAction>
</CardHeader>
<CardContent>
<Card as="section" aria-labelledby="card-reading-title">
<CardHeader>
<CardTitle as="h3" id="card-reading-title">
Message reading and long code previews
</CardTitle>
<CardDescription>
Body text wrapping and local code scrolling are handled separately.
</CardDescription>
<CardAction>
<Status state="warning" label="Pending" />
</CardAction>
</CardHeader>
<CardContent>
<p>
Read a full reply on a narrow screen, then decide whether to adopt the new
paragraph spacing.
</p>
</CardContent>
<CardFooter>
<Button
variant="quiet"
size="small"
data-card-action="Opened the reading check record"
>
View check record
</Button>
</CardFooter>
</Card>
<Card as="section" aria-labelledby="card-recovery-title">
<CardHeader>
<CardTitle as="h3" id="card-recovery-title">
Draft recovery after continuous input and a failed submit
</CardTitle>
<CardDescription>
The submitted content and the latest draft are kept separately; editing can
continue after a failure.
</CardDescription>
</CardHeader>
<CardContent>
<p>
When reviewing, use long input and confirm the error message does not cover the
primary action.
</p>
<Card variant="surface">
<CardHeader>
<CardTitle as="h4">A separate verification environment</CardTitle>
<CardDescription>
This resource needs its own boundary, so the surface is explicit.
</CardDescription>
</CardHeader>
</Card>
</CardContent>
</Card>
</CardContent>
<CardFooter>
<Button data-card-action="Saved the local check conclusion">
Save check conclusion
</Button>
<Button variant="quiet" data-card-action="Kept the record to continue later">
Continue later
</Button>
</CardFooter>
</Card>
<Card as="section" aria-labelledby="card-reference-title">
<CardHeader>
<CardTitle id="card-reference-title">Related references</CardTitle>
<CardDescription>
Standalone items keep their own boundary and are not merged into the project record
above.
</CardDescription>
</CardHeader>
<CardContent>
<p>
The theme only changes visual expression; content structure and actions stay
available.
</p>
</CardContent>
</Card>
<Note role="status" data-card-feedback>
Actions only report back to the local example.
</Note>
</Stack>
</Measure>
)Installation
Usage
Taken from the examples on this page.
import { CardContent } from "meowtail";
<CardContent>
<p>
Read a full reply on a narrow screen, then decide whether to adopt the new
paragraph spacing.
</p>
</CardContent>Examples
Other stories that draw CardContent.
Title, actions, and real container width
A wide card and a narrow sidebar in the same window share the title layout; nested cards, intrinsic button cards, and optional descriptions keep their native structure.
render: () => (
<Measure width="wide">
<Stack gap="relaxed">
<Card data-card-responsive="wide">
<CardHeader>
<CardTitle>
Workspace settings and access rules to verify — A long workspace configuration title
</CardTitle>
<CardDescription>
Changes keep existing content; save after the review is complete.
</CardDescription>
<CardAction>
<Button>View full configuration</Button>
<Button variant="quiet">Copy configuration link</Button>
</CardAction>
</CardHeader>
<CardContent>
<Card data-card-responsive="nested">
<CardHeader>
<CardTitle>Default nested check item</CardTitle>
<CardDescription>
Embedded relationships still share the parent surface.
</CardDescription>
</CardHeader>
<CardContent>
<Field label="Kept draft" htmlFor="card-responsive-draft">
<Input id="card-responsive-draft" />
</Field>
</CardContent>
</Card>
</CardContent>
</Card>
<div style={{ width: "300px", maxWidth: "100%" }}>
<Card data-card-responsive="sidebar">
<CardHeader>
<CardAction>
<Button>View details</Button>
<Button variant="quiet">Copy link</Button>
</CardAction>
<CardTitle>A long sidebar card title with meaningful information</CardTitle>
<CardDescription>
A narrow region in the same wide window; actions precede the title in the DOM.
</CardDescription>
</CardHeader>
</Card>
</div>
<Inline align="start">
<Card as="button" data-card-responsive="button">
<CardHeader>
<CardTitle>Header-only button card</CardTitle>
<CardDescription>Content decides the natural width.</CardDescription>
</CardHeader>
</Card>
<Card data-card-responsive="intrinsic">
<CardHeader>
<CardTitle>Intrinsic inline card</CardTitle>
<CardDescription>No hand-written width.</CardDescription>
</CardHeader>
</Card>
</Inline>
<Card data-card-responsive="title-only">
<CardHeader>
<CardTitle>No empty description row when there is only a title</CardTitle>
<CardAction>
<Button>Check results</Button>
</CardAction>
</CardHeader>
</Card>
</Stack>
</Measure>
)Preserve existing content
Light defaults to editable and dark to loading; input nodes and drafts stay unchanged across the switch. Actions sit outside the boundary.
render: () => (
<SchemePair>
{(scheme) => {
const active = signal(scheme === "dark");
const appended = signal(false);
const id = scheme === "light" ? "skeleton-boundary-demo" : "skeleton-boundary-other";
return (
<Stack gap="normal">
<SkeletonBoundary id={id} active={active} label={copy.loading}>
<Card>
<CardHeader>
<CardTitle>{copy.project}</CardTitle>
</CardHeader>
<CardContent>
<Stack gap="normal">
<Inline align="center">
<img
src={image}
width={96}
height={72}
alt="Project cover"
style={{ borderRadius: "10px" }}
/>
<Stack gap="compact">
<Inline align="center" gap="compact">
<Icon name="folder" />
<span>Design resources</span>
</Inline>
<span>{copy.note}</span>
</Stack>
</Inline>
<Field label={copy.name} htmlFor={`${id}-name`}>
<Input id={`${id}-name`} value={copy.project} />
</Field>
{mapSignal(appended, (value) =>
value ? <p data-testid={`${id}-extra`}>{copy.extra}</p> : null,
)}
</Stack>
</CardContent>
</Card>
</SkeletonBoundary>
<ActionRow>
<Button
data-testid={`${id}-start`}
variant="outline"
onClick={() => {
active.value = true;
}}
>
{copy.start}
</Button>
<Button
data-testid={`${id}-finish`}
variant="outline"
onClick={() => {
active.value = false;
}}
>
{copy.finish}
</Button>
<Button
data-testid={`${id}-append`}
variant="quiet"
onClick={() => {
appended.value = true;
}}
>
{copy.append}
</Button>
</ActionRow>
</Stack>
);
}}
</SchemePair>
)API reference
Props
Also takes className, children and the attributes and events of its root element.
DOM parts
Registry
The component is registered as card-content in the meowtail component registry.