# Card

> A white container with a hairline that groups one subject on a page — a header with a title and description, content and a footer — or makes a selectable option inside a form.

Source: https://raft-design.botiverse.build/components/card. Generated from the current release; where it differs from what you remember, this page is right.

A card groups one subject on a page: a title and a line of description, the content that belongs to it, and an optional footer note. It sits flat on the page, on `layer-card` with a `line-hairline` outline and `radius-container`, and casts no shadow. Its `option` variant is a selectable choice inside a form.

```tsx
import { Rocket } from "lucide-react";
import {
  Badge,
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardLeading,
  CardTitle,
  CardTrailing,
  Separator,
  Status,
  Text,
} from "@botiverse/raft-ui";
import type { CardProps } from "@botiverse/raft-ui";

type CardProjectSummaryCompositionProps = {
  variant?: CardProps["variant"];
};

export function CardProjectSummaryComposition({
  variant = "default",
}: CardProjectSummaryCompositionProps = {}) {
  return (
    <Card variant={variant} className="w-full max-w-md">
      <CardHeader>
        <CardLeading>
          <Rocket aria-hidden />
        </CardLeading>
        <CardTitle render={<h3 />}>Release readiness</CardTitle>
        <CardTrailing>
          <Badge variant="warning">1 blocker</Badge>
        </CardTrailing>
        <CardDescription>Public beta · Thursday at 10:00</CardDescription>
      </CardHeader>
      <CardContent className="grid gap-3.5">
        <div className="flex min-w-0 items-start gap-3 text-sm leading-5">
          <Status aria-hidden className="lead-dot" variant="success" />
          <div className="min-w-0 flex-1">
            <Text.Sans size="small">Component freeze</Text.Sans>
            <Text.Sans size="caption">48 components approved for release.</Text.Sans>
          </div>
          <Text.Mono className="mt-0.5 shrink-0" size="meta">
            Ready
          </Text.Mono>
        </div>

        <Separator className="data-[orientation=horizontal]:border-line-hairline" />

        <div className="flex min-w-0 items-start gap-3 text-sm leading-5">
          <Status aria-hidden className="lead-dot" variant="success" />
          <div className="min-w-0 flex-1">
            <Text.Sans size="small">Accessibility audit</Text.Sans>
            <Text.Sans size="caption">Keyboard and contrast checks passed.</Text.Sans>
          </div>
          <Text.Mono className="mt-0.5 shrink-0" size="meta">
            Ready
          </Text.Mono>
        </div>

        <Separator className="data-[orientation=horizontal]:border-line-hairline" />

        <div className="flex min-w-0 items-start gap-3 text-sm leading-5">
          <Status aria-hidden className="lead-dot" variant="warning" />
          <div className="min-w-0 flex-1">
            <Text.Sans size="small">Migration guide</Text.Sans>
            <Text.Sans size="caption">Waiting for platform review.</Text.Sans>
          </div>
          <Text.Mono className="mt-0.5 shrink-0" size="meta">
            Review
          </Text.Mono>
        </div>
      </CardContent>
      <CardFooter>
        <Text.Mono size="meta">Owner · Mira Chen</Text.Mono>
        <Text.Mono className="ml-auto" size="meta">
          Updated 4 min ago
        </Text.Mono>
      </CardFooter>
    </Card>
  );
}
```

## When to use

| Situation                                                            | Use                                                                                                                                                     |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| One subject on a page, with a title and its own content or controls  | Card                                                                                                                                                    |
| A setting that turns a feature on, with its configuration under it   | Card with `variant="inset"`                                                                                                                             |
| A choice among a few options in a form, where each option is a block | Card with `variant="option"`, holding a [Checkbox](https://raft-design.botiverse.build/components/checkbox.md) or a radio                                                                  |
| A group of rows: settings, members, agents                           | `ListRowGroup` ([ListRow](https://raft-design.botiverse.build/components/list-row.md))                                                                                                     |
| Properties of one item, label and value                              | [DescriptionList](https://raft-design.botiverse.build/components/description-list.md)                                                                                              |
| A part of a large form                                               | [FormSection](https://raft-design.botiverse.build/components/form-section.md)                                                                                                          |
| A column of the main area                                            | [Panel](https://raft-design.botiverse.build/components/panel.md)                                                                                                                              |
| A task, a file on a message, a person on hover                       | [TaskCard](https://raft-design.botiverse.build/components/task-card.md), [MessageAttachment](https://raft-design.botiverse.build/components/message-attachment.md), [PreviewCard](https://raft-design.botiverse.build/components/preview-card.md) |

## Anatomy

- `Card`: the container. Required.
- `CardHeader`: the top row. It lays out the parts below in a grid.
  - `CardLeading`: an icon, avatar or switch before the title, spanning the title and description rows. Optional.
  - `CardTitle`: the title. Optional, but most cards have one.
  - `CardTrailing`: a status, badge or switch at the end of the title row. Optional.
  - `CardDescription`: one line under the title, in `text-callout` and `foreground-muted`. Optional.
- `CardContent`: the body. Optional.
- `CardFooter`: a supporting note or a link, in `text-caption` and `foreground-hint`, with a hairline above it. Optional.

## Variants

| Variant             | Use for                                                                                                                                                             |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `variant="default"` | A card on its own, or one with little inside it                                                                                                                     |
| `variant="inset"`   | A card that holds many controls or nested components: a hairline separates the header and each `CardContent`                                                        |
| `variant="option"`  | An option inside a form: `radius-control`, the field border (1.5px `line-field`) and a title at the size of a field label, so it doesn't outrank the label above it |

```tsx
import { useState } from "react";
import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
  CardTrailing,
  Switch,
} from "@botiverse/raft-ui";

export function CardWorkspaceModeComposition() {
  const [enabled, setEnabled] = useState(true);

  return (
    <Card className="w-full max-w-md">
      <CardHeader>
        <CardTitle render={<span id="workspace-mode-label" />}>Workspace mode</CardTitle>
        <CardTrailing>
          <Switch
            id="workspace-mode"
            aria-labelledby="workspace-mode-label"
            checked={enabled}
            onCheckedChange={setEnabled}
          />
        </CardTrailing>
        <CardDescription>
          Use the Activity Bar, Sidebar, and draggable editor groups.
        </CardDescription>
      </CardHeader>
    </Card>
  );
}
```

```tsx
import { useState } from "react";
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
  Separator,
  Switch,
  Text,
} from "@botiverse/raft-ui";

export function CardDenseSettingsComposition() {
  const [externalLinks, setExternalLinks] = useState(true);
  const [downloads, setDownloads] = useState(true);
  const [sessionArchive, setSessionArchive] = useState(false);

  return (
    <Card variant="inset" className="w-full max-w-md">
      <CardHeader>
        <CardTitle render={<h3 />}>Workspace safeguards</CardTitle>
        <CardDescription>
          Apply shared defaults to links, downloads, and inactive sessions.
        </CardDescription>
      </CardHeader>

      <CardContent className="grid gap-4">
        <div className="flex min-w-0 items-start justify-between gap-4 leading-5">
          <div className="min-w-0">
            <Text.Sans render={<h4 id="external-links-label" />} size="small">
              External links
            </Text.Sans>
            <Text.Sans size="caption">Require approval before opening untrusted domains.</Text.Sans>
          </div>
          <Switch
            className="mt-[calc((1lh-1.875rem)/2)]"
            aria-labelledby="external-links-label"
            checked={externalLinks}
            onCheckedChange={setExternalLinks}
          />
        </div>

        <Separator />

        <div className="flex min-w-0 items-start justify-between gap-4 leading-5">
          <div className="min-w-0">
            <Text.Sans render={<h4 id="downloads-label" />} size="small">
              File downloads
            </Text.Sans>
            <Text.Sans size="caption">Block files that have not completed verification.</Text.Sans>
          </div>
          <Switch
            className="mt-[calc((1lh-1.875rem)/2)]"
            aria-labelledby="downloads-label"
            checked={downloads}
            onCheckedChange={setDownloads}
          />
        </div>

        <Separator />

        <div className="flex min-w-0 items-start justify-between gap-4 leading-5">
          <div className="min-w-0">
            <Text.Sans render={<h4 id="session-archive-label" />} size="small">
              Inactive sessions
            </Text.Sans>
            <Text.Sans size="caption">
              Archive sessions after thirty days without activity.
            </Text.Sans>
          </div>
          <Switch
            className="mt-[calc((1lh-1.875rem)/2)]"
            aria-labelledby="session-archive-label"
            checked={sessionArchive}
            onCheckedChange={setSessionArchive}
          />
        </div>
      </CardContent>
    </Card>
  );
}
```

```tsx
import { Card, CardHeader, CardLeading, CardTitle, Checkbox } from "@botiverse/raft-ui";

export function CheckboxSelectionContent() {
  return (
    <div className="grid min-w-64 gap-2">
      <Card variant="option" render={<label />}>
        <CardHeader>
          <CardLeading>
            <Checkbox defaultChecked aria-label="Select conversation" />
          </CardLeading>
          <CardTitle>Customer handoff</CardTitle>
        </CardHeader>
      </Card>
      <Card variant="option" render={<label />}>
        <CardHeader>
          <CardLeading>
            <Checkbox aria-label="Select message" />
          </CardLeading>
          <CardTitle>Message selection</CardTitle>
        </CardHeader>
      </Card>
    </div>
  );
}
```

## Content

- Write the title in sentence case: Release readiness ([Writing](https://raft-design.botiverse.build/content/writing.md#casing)).
- Write the description as one line on what the card is for or where it stands.
- Use the footer for a note about the whole card, such as where to read more. Put a card-level action at the end of `CardContent` instead.

## Accessibility

Card renders a `div` and adds no semantics.

- Render `CardTitle` as a heading at the level the page needs, with `render`: `render={<h3 />}`.
- When a `Switch` or `Checkbox` in `CardLeading` or `CardTrailing` is named by the title, give the title an `id` and point the control's `aria-labelledby` at it.
- For an `option` card, render the whole card as a `label` (`render={<label />}`) so a click anywhere on it toggles the control.

## Rules

- Keep cards flat. Only things that float cast a shadow ([Elevation](https://raft-design.botiverse.build/foundations/elevation.md)); the open comment card is lifted ([Comment](https://raft-design.botiverse.build/components/comment.md)).
- Don't put a card inside a card. An action inside a section is an ordinary row, with the description on the left and the button on the right ([Forms](https://raft-design.botiverse.build/patterns/forms.md#the-large-form-dialog)).
- Use `line-hairline` for every line inside a card; it is content, not a region ([Color](https://raft-design.botiverse.build/foundations/color.md#lines)).
- Don't attach a progress bar or a colored band to a card's edge ([Shape](https://raft-design.botiverse.build/foundations/shape.md#nothing-on-the-edge-of-a-rounded-container)).
- Apply a switch in a card at once, without a Save button ([Forms](https://raft-design.botiverse.build/patterns/forms.md#settings-that-apply-at-once)).

## API

### Card

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"option" \| "default" \| "inset"` | `"default"` |  |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### CardContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### CardDescription

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### CardFooter

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### CardHeader

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### CardLeading

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### CardTitle

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### CardTrailing

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

## Changes

- 0.10.0 (Breaking Changes): Field frames (`Input`, `Textarea`, `InputGroup`, `Select`, `Combobox`, `ComboboxSelect`, `Composer`, option `Card`, `SearchTrigger`) are drawn at 1px in `line-field` instead of 1.5px, on the phone too.
- 0.3.0 (Added): Added the composable `Card` component with `default` and `inset` variants and the final `CardHeader`, `CardLeading`, `CardTitle`, `CardDescription`, `CardTrailing`, `CardContent`, and `CardFooter` parts.
