# Card

A bounded surface, with no shadow under it.

- Group: Surfaces
- Import: `import { Card } from '@misoto22/folio'`
- Page: https://ui.misoto22.com/components/card/
- Related: table, figure-band

## When to reach for it

A card that needs to read as raised is a plate, which separates by reversal instead of by blur.

## Anatomy

- **Box** (required) — A <div> with the --radius-lg corner and one of three grounds: outline, a hairline on the page ground and the default; plate, the one reversed feature surface; flat, no border at all, for a card whose grid already draws the rules between cells. It brings no padding, and it clips to its own corner.
- **Header** — CardHeader — a space-between row above a hairline, px-5 py-4: title against the start edge, one marker or action against the end.
- **Title** — CardTitle, in the editorial serif at --fs-item. An <h3> unless as says otherwise, and it reads --card-title rather than --ink directly, which is what keeps it legible when plate re-points that variable.
- **Body** — CardBody — the content well at p-5, --ink-2, relaxed leading.
- **Footer** — CardFooter — a quiet strip under a hairline, mono-meta at --ink-3-aa, for metadata or a secondary action.

## Best practices

### Do

- Pass as on CardTitle: h3 is right inside a section that owns an h2 and wrong nearly everywhere else, and a grid of twelve cards is otherwise twelve h3s with no heading above them to belong to.
- Use CardTitle inside a plate rather than your own heading — plate re-points --card-title to --on-feature, and a title that reads --ink directly came out at 1.25:1 on that ground: invisible, and invisible only on the one variant whose job is to look different.
- Add the padding yourself when you skip the sub-parts: the box has none of its own, so children dropped straight in sit against the border.
- Pass overflow-visible for the card that deliberately overhangs — a marker pinned to its edge, a control that breaks the outline. The box clips by default, because a card that rounds and does not clip lays a full-bleed image’s square corners over its round ones.

### Don’t

- A Card with an onClick is a div with an onClick — not focusable, not announced, unreachable by keyboard. Put a real control inside and let it stretch, so what is announced is a button and the whole card is still the target.
- Do not spend plate more than once on a screen: it is the system’s single reversed surface, and a band of plates is a band with no ground left to reverse against.

## Card

A bounded surface. No shadow, by law: depth in this system is a hairline and a change of ground, never a blur. A card that needs to read as raised is a `plate`, which separates by reversal instead. It clips to its own corner. A card that rounds and does not clip lays a full-bleed image's square corners over its round ones at all four corners, and the same goes for a filled first child — a defect that is invisible until the first card with an image at the top of it. Pass `overflow-visible` for the rarer card that deliberately overhangs. Compose with the named sub-parts, or drop children straight in when the card has no header or footer to speak of.

### Props

- `variant` — `'outline' | 'plate' | 'flat'` default `'outline'`. `outline` is a hairline box on the page ground — the default, and correct for a card sitting among other cards. `plate` fills with the one reversed surface and is for a card that IS the point of its band; use at most one per screen. `flat` drops the border entirely, for a card whose grid already draws the rules between cells.

Also accepts: `HTMLAttributes<HTMLDivElement>`.

## CardHeader

Header row — title on the left, an action or marker on the right.

## CardTitle

The card's title, in the editorial serif. An `<h3>` by default, which is right inside a section with its own `<h2>` and wrong nearly everywhere else — pass `as` rather than leaving a page with a heading order that jumps.

## CardBody

The content well.

## CardFooter

A quiet strip for metadata or secondary actions.

## Example — variants

```tsx
import { Badge, Card, CardBody, CardFooter, CardHeader, CardTitle } from '@misoto22/folio'

<div className="grid w-full gap-4 sm:grid-cols-2">
  <Card>
    <CardHeader>
      <CardTitle>Recent deploys</CardTitle>
      <Badge tone="success">green</Badge>
    </CardHeader>
    <CardBody>Twelve releases in the last thirty days, none rolled back.</CardBody>
    <CardFooter>Updated 4 minutes ago</CardFooter>
  </Card>
  <Card variant="plate">
    <CardHeader className="border-(--ink-3)">
      <CardTitle className="text-(--on-feature)">The reversed plate</CardTitle>
    </CardHeader>
    <CardBody className="text-(--on-feature)/80">
      One per screen. It separates by reversal, because this system has no blur to raise it with.
    </CardBody>
  </Card>
</div>
```

## Example — a real control

```tsx
import { Badge, Card, CardBody, CardHeader, CardTitle, Text } from '@misoto22/folio'

<Card className="relative w-full max-w-sm transition-colors duration-(--duration-fast) hover:border-(--ink)">
  <CardHeader>
    <CardTitle as="h3">
      <a href="#deployments" className="text-inherit no-underline after:absolute after:inset-0">
        api.misoto22.com
      </a>
    </CardTitle>
    <Badge tone="success">live</Badge>
  </CardHeader>
  <CardBody>
    <Text size="sm">Deployed from main four minutes ago, in 2m 14s.</Text>
  </CardBody>
</Card>
```

## Example — a ruled grid

```tsx
import { Card, CardBody, CardTitle, Heading, Text } from '@misoto22/folio'

<section className="w-full">
  <Heading level={2} size="item" className="mb-4">
    Revenue by region
  </Heading>
  <div className="grid gap-px overflow-hidden rounded-(--radius-lg) border border-(--rule) bg-(--rule) sm:grid-cols-3">
    {REGIONS.map((region) => (
      <Card key={region.name} variant="flat" className="rounded-none bg-(--paper)">
        <CardBody>
          <CardTitle as="h3">{region.name}</CardTitle>
          <Text size="sm" className="mt-2 tabular-nums">
            {region.orders} orders
          </Text>
          <Text size="sm" tone="strong" className="mt-1 tabular-nums">
            {region.revenue}
          </Text>
        </CardBody>
      </Card>
    ))}
  </div>
</section>
```
