Content Only
Body alone, when there is nothing to title.
<Card>
<Card.Body>Just content, no header or footer.</Card.Body>
</Card>Groups related content onto one bounded surface.
<Card>
<Card.Header>
<Card.Title>Card title</Card.Title>
<Card.Description>A brief description</Card.Description>
</Card.Header>
<Card.Body>Related content</Card.Body>Do not use a Card as a modal surface.
Don’t
<Card>Confirm deletion</Card>Do
<Card>
<Card.Body>Inline summary</Card.Body>
</Card>pnpm add @usefragments/uiThe npm package stays managed by your package manager. Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.
Import Card once, then compose its parts with dot notation.
Card.HeaderCard.TitleCard.DescriptionCard.BodyCard.FooterQuiet surface with a title and description.
<Card>
<Card.Header>
<Card.Title>Card title</Card.Title>
<Card.Description>A brief description</Card.Description>
</Card.Header>
<Card.Body>Related content</Card.Body>
</Card>Body alone, when there is nothing to title.
<Card>
<Card.Body>Just content, no header or footer.</Card.Body>
</Card>Footer parks the actions below the content.
<Card>
<Card.Header>
<Card.Title>Card with footer</Card.Title>
<Card.Description>Complete card layout</Card.Description>
</Card.Header>
<Card.Body>Main content area.</Card.Body>A metric tile is the same surface: the figure on the display step, tabular.
<Card>
<Card.Body>
<Text as="p" type="display" tabularNums>
94%
</Text>
<Text as="p" color="secondary">A 40px divided head with the line below and no fill; the body owns its own spacing.
<Card padding="none">
<Card.Header divided>
<Card.Title>Overview</Card.Title>
</Card.Header>
<Card.Body padding="md">Panel content</Card.Body>
</Card>The only state chrome: the edge in the danger ink, no wash.
<Card tone="danger">
<Card.Header>
<Card.Title>Merge held</Card.Title>
<Card.Description>Two blocking findings need a decision.</Card.Description>
</Card.Header>
</Card>render makes the card a real link or button: gated hover tint, press scale, focus ring. Inside a button the parts draw as spans.
<Card render={<a href="#details" />}>
<Card.Header>
<Card.Title>Open details</Card.Title>
<Card.Description>The whole card is one link</Card.Description>
</Card.Header>
</Card>The chosen card: the selection wash, and the hairline turns into the ring.
<Card selected render={<button type="button" aria-pressed="true" />}>
<Card.Header>
<Card.Title>Team plan</Card.Title>
<Card.Description>Chosen</Card.Description>
</Card.Header>
</Card>The as prop matches the surrounding document semantics.
<Card as="section" aria-labelledby="billing-card-title">
<Card.Header>
<Card.Title id="billing-card-title">Billing summary</Card.Title>
</Card.Header>
<Card.Body>Section semantics support the surrounding document outline.</Card.Body>
</Card>Pick the heading level the page outline expects.
<Card padding="none">
<Card.Header divided>
<Card.Title as="h4">Nested panel</Card.Title>
</Card.Header>
<Card.Body padding="md">Panel content</Card.Body>
</Card>Long titles and dense text wrap without breaking the surface.
<Card>
<Card.Header>
<Card.Title>
A detailed localized account recovery policy for administrators across multiple
workspaces
</Card.Title>Prop | Type | Default | Description |
|---|---|---|---|
as | "div" | "article" | "section" | undefineddivarticlesection | article | Root element tag for a static card. |
Body.childrenRequired | React.ReactNode | Not set | — |
Body.padding | CardPadding | undefinedmdnone | Not set | Inner padding for the body alone, with `padding="none"` on the root. Omit when the root already owns padding. |
childrenRequired | React.ReactNode | Not set | — |
Description.childrenRequired | React.ReactNode | Not set | — |
Footer.childrenRequired | React.ReactNode | Not set | — |
Header.childrenRequired | React.ReactNode | Not set | — |
Header.divided | boolean | undefined | Not set | A 40px head with the hairline below it and no fill, for a card with `padding="none"`. Title cluster and trailing controls pin to opposite edges. |
padding | CardPadding | undefinedmdnone | md | Inner padding: `md` is the compact surface inset (12); `none` lets a divided header and `Card.Body` own their inset (a table sheet). |
render | useRender.RenderProp<Record<string, unknown>> | undefined | Not set | Makes the card one interactive element: pass a real link or button, `render={<a href="/repos/web" />}` or `render={<button type="button" />}`. It gains the hover tint, the press scale and the focus ring. A button holds phrasing content only, so any render other than a plain `<a>` draws every part as a `span` (Title loses its heading level). Keep other controls out of an interactive card. |
selected | boolean | undefined | false | Marks the card as the chosen one: the selection wash, and the hairline turns into the selection ring. Pair it with the ARIA state that fits the element (`aria-pressed` on a button, `aria-current` on a link). |
Title.as | "h2" | "h3" | "h4" | "h5" | "h6" | undefinedh2h3h4h5h6 | Not set | Semantic heading level. |
Title.childrenRequired | React.ReactNode | Not set | — |
tone | CardTone | undefineddangerneutral | neutral | `danger` draws the edge in the danger ink, for a card whose content is held (a blocked merge). There is no wash; the words inside say why. |