# Card

Groups related content and actions in one bordered block.

- **Import:** `import { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent } from "@booleanpress/ui/card"`
- **Page:** <https://ui.booleanpress.com/components/card> · @booleanpress/ui 0.1.0

## Usage

A card is a bordered block with optional header, content and footer parts. It is flat: it separates from the page by its border, with no shadow.

```tsx
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@booleanpress/ui/card"

export function Mailer() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Primary mailer</CardTitle>
        <CardDescription>Amazon SES</CardDescription>
      </CardHeader>
      <CardContent>Healthy</CardContent>
    </Card>
  )
}
```

The parts are `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardAction`, `CardContent` and `CardFooter`. Put a button in `CardAction` to place it at the end of the header. Add `border-b` to the header or `border-t` to the footer for a divider: the part then pads its own side to match. Every part is a `div` and takes `className`. A card does not scroll or collapse; for that, put a `ScrollArea` or `Collapsible` inside.

## Examples

### Basic

Title, description and an action in the header, text in the content and a button in the footer.

```tsx
import { Button } from "@booleanpress/ui/button"
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@booleanpress/ui/card"

export default function CardBasic() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Primary mailer</CardTitle>
        <CardDescription>Amazon SES, eu-west-1</CardDescription>
        <CardAction>
          <Button variant="outline" size="sm">
            Edit
          </Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        <p className="text-sm">1,840 emails sent in the last 7 days, 12 failed.</p>
      </CardContent>
      <CardFooter>
        <Button variant="secondary" size="sm">
          Send test email
        </Button>
      </CardFooter>
    </Card>
  )
}
```

### Statistic

A card with a header and content only, showing one number.

```tsx
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@booleanpress/ui/card"

export default function CardSimple() {
  return (
    <Card className="w-full max-w-xs">
      <CardHeader>
        <CardDescription>Delivered today</CardDescription>
        <CardTitle className="text-3xl tabular-nums">1,284</CardTitle>
      </CardHeader>
      <CardContent className="text-sm text-muted-foreground">98.7% of emails sent.</CardContent>
    </Card>
  )
}
```

### With dividers

`border-b` on the header and `border-t` on the footer draw the dividers; the parts keep their spacing.

```tsx
import { Button } from "@booleanpress/ui/button"
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@booleanpress/ui/card"

export default function CardWithDividers() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader className="border-b">
        <CardTitle>Delete API key</CardTitle>
        <CardDescription>Requests that use this key stop working at once.</CardDescription>
      </CardHeader>
      <CardContent className="text-sm">
        The key <code className="font-mono">smtp-prod-2</code> was last used 3 minutes ago.
      </CardContent>
      <CardFooter className="justify-end gap-2 border-t">
        <Button variant="outline">Cancel</Button>
        <Button variant="destructive">Delete key</Button>
      </CardFooter>
    </Card>
  )
}
```

## Accessibility

**Semantics.** Plain `div`s: a card has no role, so it is not a landmark or a group.

**Labels.** `CardTitle` is a `div`, not a heading. Where the card starts a section of the page, put a heading element (`h2`, `h3`) inside `CardTitle`. Give an icon-only button in `CardAction` an `aria-label`.

**Focus.** A card has no tab stop. Buttons and links inside it are tab stops in reading order. A card that is wholly clickable needs a real link or button inside it; do not put `onClick` on the card.

**Known limits.**

- The title is not a heading by default, so a screen-reader user cannot jump from card to card by heading. Add the heading element yourself where that navigation matters.
- A card has no state of its own (selected, loading, disabled). Show such a state with content or a `Badge`, never by colour alone.

### Keyboard

| Key | Behaviour |
| --- | --- |

## API

### Card

Renders a `div` and passes it every other prop.

### CardHeader

Renders a `div` and passes it every other prop.

### CardFooter

Renders a `div` and passes it every other prop.

### CardTitle

Renders a `div` and passes it every other prop.

### CardAction

Renders a `div` and passes it every other prop.

### CardDescription

Renders a `div` and passes it every other prop.

### CardContent

Renders a `div` and passes it every other prop.

Every part takes `className`, merged with its defaults by `cn()`, and `ref`, which reaches the element it renders.

**Data attributes:** `data-slot="card"` (Card), `data-slot="card-header"` (CardHeader), `data-slot="card-footer"` (CardFooter), `data-slot="card-title"` (CardTitle), `data-slot="card-action"` (CardAction), `data-slot="card-description"` (CardDescription), `data-slot="card-content"` (CardContent).

## Theming

`bg-card` and `text-card-foreground` set the surface; the border is `--border`. Do not add a shadow: the package's blocks are flat on purpose.

| Token | Used for |
| --- | --- |
| `--card` | background |
| `--card-foreground` | text |
| `--muted-foreground` | text |
