---
title: Card
description: Group related content and actions on a neutral surface.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# Card

> Group related content and actions on a neutral surface.

Card groups related content and actions on a neutral surface. Its parts are structural and add no interactive behavior or ARIA relationships. `Card.Title` renders a `div` by default; choose a heading with `as` when the page needs one.

## Basic usage

```tsx
import { Card } from 'moraine'

export function Example() {
  return (
    <Card>
      <Card.Header>
        <Card.Title>Project</Card.Title>
      </Card.Header>
      <Card.Body>Project details</Card.Body>
    </Card>
  )
}
```

## Anatomy

```text
Card [component; slot=root]
├── Card.Header [part; slot=header]
│   ├── Card.Title [part; slot=title]
│   ├── Card.Description [part; slot=description]
│   └── Card.Action [part; slot=action]
├── Card.Body [part; slot=body]
└── Card.Footer [part; slot=footer]
```

## Usage

### Variants and sizes

Every variant has an outer border, and the header keeps the same appearance as the body. `outline` adds a top border to the footer. `subtle` adds the footer divider and a muted footer background. `none` keeps only the outer border and the anatomy spacing. The `sm`, `md`, and `lg` sizes change the density of the header, body, footer, title, and description together.

Compose the parts you need inside `Card`. Header and body provide horizontal insets, while the root spaces the sections. Direct children can still span the card's full width.

## Examples

### Structured content

```tsx
import { Button, Card } from 'moraine'

export function CardStructure() {
  return (
    <Card class="max-w-sm w-full">
      <Card.Header>
        <Card.Title as="h3">Project Deployment</Card.Title>
        <Card.Description>Configure automated build triggers.</Card.Description>
      </Card.Header>
      <Card.Body>
        <p class="text-sm text-muted-foreground">
          Deployments are pushed directly to the globally distributed edge CDN network.
        </p>
      </Card.Body>
      <Card.Footer class="gap-2 justify-end">
        <Button variant="ghost" size="xs">
          Cancel
        </Button>
        <Button size="xs">Deploy</Button>
      </Card.Footer>
    </Card>
  )
}
```

### Semantic heading

Card does not choose a heading level. Use `as="h2"` or another appropriate level for the surrounding page.

```tsx
import { Badge, Card } from 'moraine'

export function CardSemantics() {
  return (
    <Card as="article" class="max-w-sm w-full">
      <Card.Header>
        <Card.Title as="h3">Release v2.4.0 Available</Card.Title>
        <Card.Description>Published 2 hours ago by @release-bot</Card.Description>
        <Card.Action>
          <Badge>New</Badge>
        </Card.Action>
      </Card.Header>
      <Card.Body>
        <p class="text-sm text-muted-foreground">
          Includes performance optimizations, smaller bundle sizes, and refreshed component styles.
        </p>
      </Card.Body>
    </Card>
  )
}
```

### Full-bleed media

Place media directly inside `Card` to span its width. Apply clipping on the root only if the composition needs it.

```tsx
import { Button, Card } from 'moraine'

export function WithImage() {
  return (
    <Card class="max-w-sm w-full overflow-hidden">
      <img
        src="https://images.unsplash.com/photo-1604076850742-4c7221f3101b?q=80&w=1887&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
        alt="Landscape by mymind on Unsplash"
        class="w-full aspect-video object-cover brightness-60 grayscale"
      />
      <Card.Body>
        <Card.Title as="h3">Beautiful Landscape</Card.Title>
        <Card.Description class="mt-1">
          A full-bleed image can sit directly inside Card.
        </Card.Description>
      </Card.Body>
      <Card.Footer>
        <Button class="w-full">Open</Button>
      </Card.Footer>
    </Card>
  )
}
```

### Header action

```tsx
import { Button, Card } from 'moraine'

export function HeaderAction() {
  return (
    <Card class="w-lg">
      <Card.Header>
        <Card.Title as="h3">Meeting Notes</Card.Title>
        <Card.Description>Transcript summary from the latest client sync.</Card.Description>
        <Card.Action>
          <Button size="sm" variant="secondary">
            Transcribe
          </Button>
        </Card.Action>
      </Card.Header>
      <Card.Body>
        <ol class="text-sm pl-5 list-decimal opacity-85 flex flex-col gap-1.5">
          <li>Dashboard redesign should prioritize mobile layouts.</li>
          <li>Timeline target is six weeks with weekly milestones.</li>
          <li>Next review meeting is scheduled for Tuesday morning.</li>
        </ol>
      </Card.Body>
      <Card.Footer class="justify-end">
        <Button size="sm" variant="outline">
          Dismiss
        </Button>
        <Button size="sm">Save</Button>
      </Card.Footer>
    </Card>
  )
}
```

### Custom header

```tsx
import { Card } from 'moraine'

export function CustomHeader() {
  return (
    <Card class="max-w-sm">
      <Card.Header class="flex items-center justify-between">
        <div>
          <p class="text-xs text-muted-foreground">Storage</p>
          <p class="text-2xl font-semibold">72%</p>
        </div>
        <span class="text-success text-xs font-medium">12 GB free</span>
      </Card.Header>
      <Card.Body>
        <p class="text-sm text-muted-foreground">Archive old uploads to reclaim more space.</p>
      </Card.Body>
    </Card>
  )
}
```

## Props

### Card

Props for Card.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| size | 'sm' \| 'md' \| 'lg' \| undefined | 'md' | Density of all Card parts. |
| variant | 'outline' \| 'subtle' \| 'none' \| undefined | 'outline' | Footer border and background. The outer border is always present. |
| as | T \| undefined | 'div' | Element or component to render as. |
| children | JSX.Element \| undefined | — | Card parts and direct content. |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| classes | Classes \| undefined | — | Family slot class defaults for this instance. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
| styles | Styles \| undefined | — | Family slot style defaults for this instance. |

### Card.Header

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| as | T \| undefined | 'div' | — |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |

### Card.Title

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| as | T \| undefined | 'div' | — |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |

### Card.Description

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| as | T \| undefined | 'p' | — |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |

### Card.Action

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| as | T \| undefined | 'div' | — |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |

### Card.Body

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| as | T \| undefined | 'div' | — |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |

### Card.Footer

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| as | T \| undefined | 'div' | — |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
