> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Card - Web

> Cards are containers that group related content together.

> [!NOTE]
>
> For development, import `Card` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=3-4752).

![](https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/shared/card-example.png?fit=max\&auto=format\&n=uz2PQSvO75TRhQ38\&q=85\&s=41539f11e7108933d4777ff8d769feb1)

## Anatomy

The Card consists of two primary elements that work together to group related content together.

![Anatomy of the Card
component](https://mintcdn.com/servicetitan/Ni0bXIw9diilEZPZ/images/docs/web/components/card/design/anatomy-of-the-card-component.png?fit=max\&auto=format\&n=Ni0bXIw9diilEZPZ\&q=85\&s=3de34975496ff6500ded1de4dcba92b5)

1. Container
2. Content

## Options

The Card supports flexible content, padding, and background configurations to accommodate various grouping scenarios.

### Content

card-children

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-children?file=src/App.tsx)

```tsx lines theme={null}
import { Card, Flex, Checkbox, Text, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="4" style={{ maxWidth: "30rem" }}>
      <Card>This is a Card that just has text in it.</Card>

      <Flex>
        <Card flex={1} padding="large">
          <Checkbox.Group legend="Group label">
            <Checkbox label="Option 1" />
            <Checkbox label="Option 2" defaultChecked />
            <Checkbox label="Option 3" defaultChecked />
          </Checkbox.Group>
        </Card>
      </Flex>

      <Card flexDirection="column">
        <Flex gap={4} direction="column">
          <Flex direction="column">
            <Text variant="headline" size="medium" el="h2">
              Headline text
            </Text>
            <Text variant="body">
              Description of the lorem ipsum dolar emit molestiae reprehenderit
              iste ipsam et velit ab. Eum est et corporis quia voluptatem.
            </Text>
          </Flex>
          <Button>Get started</Button>
        </Flex>
      </Card>
    </Flex>
  );
}

export default App;
```

### Padding

card-padding

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-padding?file=src/App.tsx)

```tsx lines theme={null}
import { Card, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex gap={6} direction="column" alignItems="flex-start">
      <Card padding="0">This card has no padding applied to it.</Card>
      <Card padding="xsmall">This card has 4px padding applied to it.</Card>
      <Card padding="small">This card has 8px padding applied to it.</Card>
      <Card padding="medium">This card has 16px padding applied to it.</Card>
      <Card padding="large">This card has 24px padding applied to it.</Card>
    </Flex>
  );
}

export default App;
```

| Padding option | Value |
| - | - |
| 0 | 0px / 0rem |
| xSmall | 4px / 0.25rem |
| Small | 8px / 0.5rem |
| Medium (default) | 16px / 1rem |
| Large | 24px / 1.5rem |

### AI Mark

ai-mark-card

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-card?file=src/App.tsx)

```tsx lines theme={null}
import { Card, List, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Card flexDirection="column" style={{ maxWidth: "500px" }}>
      <Text variant="headline" el="h2" aiMark>
        Daily Job Summary
      </Text>
      <Text>
        You have four jobs on the board for the next two days: two drain
        cleanouts (Oak St., Riverside Ave.), a water-heater diagnostic on Maple,
        and a leak under a kitchen sink on Cedar.
      </Text>
      <br />
      <Text variant="headline" el="h3" size="small">
        High Priority Jobs
      </Text>
      <List>
        <List.Item>Drain Cleanout (Oak St.)</List.Item>
        <List.Item>Leak Under a Kitchen Sink (Cedar)</List.Item>
      </List>
    </Card>
  );
}

export default App;
```

Use the [AI Mark](https://anvil.servicetitan.com/docs/components/ai-mark/code) with [`Text`](https://anvil.servicetitan.com/docs/components/text/design) `variant="headline"` when the card title should indicate AI-generated content.

### Backgrounds

card-background

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-background?file=src/App.tsx)

```tsx lines theme={null}
import { Card, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex gap={6} direction="column">
      <Card>
        Card with a <br />
        default background
      </Card>
      <Card background="strong">
        Card with a <br />
        strong background
      </Card>
      <Card background="stronger">
        Card with a <br />
        stronger background
      </Card>
    </Flex>
  );
}

export default App;
```

## Behavior

The Card responds to interactivity requirements while maintaining consistent content grouping.

## Usage Guidelines

Use the Card when a self-contained group needs its own surface to stand apart from the rest of the page: a metric tile in a row of peer tiles, a chart, or a group with no section heading above it. Most page content does not need a Card. Structure pages with a Layout, section headings, and spacing, and leave the Card out of page sections, whole content areas, a lone Data Table, and other Cards.

| Standard | Requirement |
| - | - |
| DoPeer tiles | Sibling blocks of equal weight sit side by side, such as KPI or metric tiles. Use one Card per tile, never one Card around the whole row. |
| DoCharts | A chart is a self-contained unit. Wrap each chart in one Card and place the chart title inside that Card. |
| DoIsolated groups without a section heading | A group needs a distinct surface and has no section heading above it on the page. A short label inside the Card, such as "On-hand value", is not a section heading. |
| DoGroup related content | Group related content and actions within a single container; don't add unrelated content to a Card; see [Group related content](#group-related-content). |
| Don'tPage sections | A headed text, list, or form section on a page does not need a Card. Introduce the section with a heading and separate it from the next section with spacing; see [Structure page sections with headings, not Cards](#structure-page-sections-with-headings%2C-not-cards). |
| Don'tWhole content areas | Do not wrap Page content, a Layout Item, or a full row of tiles in a single Card. The page already provides the surface. |
| Don'tA lone Data Table | Place the Data Table directly in the page content. The Data Table has its own border and background. |
| Don'tContent inside another Card | Do not nest Cards. Use headings, body text, and a Divider to structure the content of a single Card; see [Nesting cards within cards](#nesting-cards-within-cards). |
| Don'tInteractive Cards | Don't make the Card itself interactive — use the [Interactive Card](https://anvil.servicetitan.com/docs/components/interactive-card/web.md) component instead; see [Adding interactive elements to a Card](#adding-interactive-elements-to-a-card). |
| Don'tEvery block "to be safe" | When a Card is not clearly one of the cases in [Usage Guidelines](#usage-guidelines), leave it out. Extra Cards add borders and padding that flatten the page hierarchy. |

### Alternatives

#### Section headings vs Card

Use a headline Text with spacing to group page content. This is the default way to structure a page: inside a Page, section headings start at `h2` because the Page Header renders the `h1`. Use a Card only when the group must stand apart on its own surface.

#### Divider vs Card

Use a [Divider](https://anvil.servicetitan.com/docs/components/divider/web.md) to separate adjacent sections when whitespace alone does not provide enough grouping, such as in high-density layouts and lists.

#### Details vs Card

Use [Details](https://anvil.servicetitan.com/docs/components/details/web.md) to show or hide supplementary information in a collapsible section instead of placing it in a Card.

#### Page Panel vs Card

Use a [Page](https://anvil.servicetitan.com/docs/components/page/web.md#panel) Panel for content that sits beside the main content, such as details for a selected item or a live activity feed, instead of a column of Cards.

#### Edit Card vs Card

Use an [Edit Card](https://anvil.servicetitan.com/docs/components/edit-card/web.md) when a summary of grouped settings needs a toggleable editing state.

#### Interactive Card vs Card

Use Interactive Card when the card itself needs an interaction. If no interaction is needed, or only an inner element needs an interaction, use Card instead.

### How to Use

#### Structure page sections with headings, not Cards

Introduce each page section with a heading and separate sections with spacing. Do not wrap each section in a Card.

card-page-sections-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-page-sections-do?file=src/App.tsx)

```tsx lines theme={null}
import { Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="6" style={{ maxWidth: "30rem" }}>
      <Flex direction="column" gap="2">
        <Text variant="headline" el="h2">
          Customer
        </Text>
        <Text>Sophia Rodriguez</Text>
        <Text>(813) 364-0534</Text>
      </Flex>
      <Flex direction="column" gap="2">
        <Text variant="headline" el="h2">
          Job details
        </Text>
        <Text>
          Water heater diagnostic at 1420 Maple Ave., scheduled for July 12,
          2025, at 3:00 pm.
        </Text>
      </Flex>
    </Flex>
  );
}

export default App;
```

> [!TIP]
>
> **Do**

card-page-sections-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-page-sections-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Card, Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="4" style={{ maxWidth: "30rem" }}>
      <Card flexDirection="column" gap="2">
        <Text variant="headline" el="h2">
          Customer
        </Text>
        <Text>Sophia Rodriguez</Text>
        <Text>(813) 364-0534</Text>
      </Card>
      <Card flexDirection="column" gap="2">
        <Text variant="headline" el="h2">
          Job details
        </Text>
        <Text>
          Water heater diagnostic at 1420 Maple Ave., scheduled for July 12,
          2025, at 3:00 pm.
        </Text>
      </Card>
    </Flex>
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

#### Group related content

Group related content and actions within a single container. Don't add unrelated content to a card.

card-related-content-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-related-content-do?file=src/App.tsx)

```tsx lines theme={null}
import { Card, Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Card flexDirection="column">
      <Flex direction="column" gap="3">
        <Text variant="headline" el="h1" size="small">
          Contact methods
        </Text>
        <Flex direction="column" gap="1">
          <Text variant="eyebrow" size="small">
            Phone
          </Text>
          <Text>(813) 364-0534</Text>
        </Flex>
        <Flex direction="column" gap="1">
          <Text variant="eyebrow" size="small">
            Fax
          </Text>
          <Text>(813) 364-0530</Text>
        </Flex>
        <Flex direction="column" gap="1">
          <Text variant="eyebrow" size="small">
            Email
          </Text>
          <Text>email.address123@gmail.com</Text>
        </Flex>
      </Flex>
    </Card>
  );
}

export default App;
```

> [!TIP]
>
> **Do**

#### Nesting cards within cards

Don't nest cards within cards. Use headings, body text, and actions to provide structure to your layout.

card-nesting-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-nesting-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Card, Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Card flexDirection="column">
      <Flex direction="column" gap="3">
        <Text variant="headline" el="h1" size="small">
          Contact methods
        </Text>
        <Flex direction="column" gap="1">
          <Text variant="eyebrow" size="small">
            Phone
          </Text>
          <Text>(813) 364-0534</Text>
        </Flex>
        <Flex direction="column" gap="1">
          <Text variant="eyebrow" size="small">
            Fax
          </Text>
          <Text>(813) 364-0530</Text>
        </Flex>
        <Flex direction="column" gap="1">
          <Text variant="eyebrow" size="small">
            Email
          </Text>
          <Text>email.address123@gmail.com</Text>
        </Flex>

        <Card flexDirection="column" padding="small">
          <Text variant="headline" el="h2" size="small">
            Robert Smith
          </Text>
          <Text>Founder</Text>
        </Card>
      </Flex>
    </Card>
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

#### Adding interactive elements to a Card

Inner content of a Card can add interactive elements. Do not make the Card itself interactive, use the [Interactive Card](https://anvil.servicetitan.com/docs/components/interactive-card/web.md) component instead.

## Content

Content within the Card should be clearly organized and focused on a single purpose.

* Place a heading inside the Card to make its purpose clear, following the heading structure of the page. Do not repeat the same heading as a section heading above the Card.
* Avoid too many call-to-actions in a single card.
* Cards should have an intentional focus, avoid overloading a single card with too much or unrelated information.
* Cards of similar importance should be structured in the same way.

## Code

**Implementation**

card-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Card, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Card flexDirection="column" background="strong">
      <Text variant="headline" el="h2" size="large">
        Basic Card
      </Text>
      <Text>
        This is the text within this example card. Make sure to always add{" "}
        <code>flexDirection=&quot;column&quot;</code> to the <code>Card</code>{" "}
        to make the text stack vertically.
      </Text>
    </Card>
  );
}

export default App;
```

## Common Examples

See [Usage Guidelines](https://anvil.servicetitan.com/docs/components/card/design#usage-guidelines) for when to use a `Card`. For a clickable card, use [`InteractiveCard`](https://anvil.servicetitan.com/docs/components/interactive-card).

```tsx theme={null}
import { Card, Text } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Card flexDirection="column">
      <Text variant="headline" el="h2">
        Card Header
      </Text>
      <Text>Card body text.</Text>
    </Card>
  );
}
```

### Page sections without a Card

Place page sections directly in a `Layout.Item`, with a `Text` headline per section and `gap` between them. See [When not to use](https://anvil.servicetitan.com/docs/components/card/design#when-not-to-use).

```tsx theme={null}
import { Flex, Layout, Text } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Layout>
      <Layout.Item span={12}>
        <Flex direction="column" gap="6">
          <Flex direction="column" gap="2">
            <Text variant="headline" el="h2">
              Customer
            </Text>
            <Text>Sophia Rodriguez</Text>
          </Flex>
          <Flex direction="column" gap="2">
            <Text variant="headline" el="h2">
              Job details
            </Text>
            <Text>Water heater diagnostic, July 12, 2025, at 3:00 pm.</Text>
          </Flex>
        </Flex>
      </Layout.Item>
    </Layout>
  );
}
```

### Tiling cards

Use one `Card` per tile. Place tiles in a `Layout` with a `Layout.Item` per `Card` and equal `span` values. Use `span={3}` for four tiles per row. Never wrap the row in one `Card`. Each tile is a `Card` with `flexDirection="column"`, an eyebrow `Text`, and a large value `Text`. See [When to Use](https://anvil.servicetitan.com/docs/components/card/design#usage-guidelines).

```tsx theme={null}
import { Card, Layout, Text } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Layout>
      <Layout.Item span={3}>
        <Card flexDirection="column">
          <Text variant="eyebrow">Open jobs</Text>
          <Text variant="headline" size="large">
            128
          </Text>
        </Card>
      </Layout.Item>
      <Layout.Item span={3}>
        <Card flexDirection="column">
          <Text variant="eyebrow">Completed</Text>
          <Text variant="headline" size="large">
            94
          </Text>
        </Card>
      </Layout.Item>
      <Layout.Item span={3}>
        <Card flexDirection="column">
          <Text variant="eyebrow">Overdue</Text>
          <Text variant="headline" size="large">
            12
          </Text>
        </Card>
      </Layout.Item>
      <Layout.Item span={3}>
        <Card flexDirection="column">
          <Text variant="eyebrow">Unassigned</Text>
          <Text variant="headline" size="large">
            7
          </Text>
        </Card>
      </Layout.Item>
    </Layout>
  );
}
```

### Background Color

Use the `background` prop to control the background color of the card.

card-background

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-background?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Card, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex gap={6} direction="column">
      <Card>
        Card with a <br />
        default background
      </Card>
      <Card background="strong">
        Card with a <br />
        strong background
      </Card>
      <Card background="stronger">
        Card with a <br />
        stronger background
      </Card>
    </Flex>
  );
}

export default App;
```

### Padding

Anvil provides five options to pass to the `padding` prop to determine the spacing between the card border and content.

card-padding

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-padding?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Card, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex gap={6} direction="column" alignItems="flex-start">
      <Card padding="0">This card has no padding applied to it.</Card>
      <Card padding="xsmall">This card has 4px padding applied to it.</Card>
      <Card padding="small">This card has 8px padding applied to it.</Card>
      <Card padding="medium">This card has 16px padding applied to it.</Card>
      <Card padding="large">This card has 24px padding applied to it.</Card>
    </Flex>
  );
}

export default App;
```

### Flex Direction

Use the `flexDirection` prop to determine which direction the `children` are displayed. The parent `div` of an Anvil `Card` uses `display: inline-flex`, so note that the orientation of the `children` could vary based on the user's locale and settings.

card-flexdirection-row

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-flexdirection-row?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Card } from "@servicetitan/anvil2";

function App() {
  return (
    <Card flexDirection="row" style={{ height: 100, gap: 8, width: "100%" }}>
      <div
        style={{ flex: 2, background: "var(--background-color-strongest)" }}
      />
      <div
        style={{ flex: 1, background: "var(--background-color-strongest)" }}
      />
    </Card>
  );
}

export default App;
```

card-flexdirection-column

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-flexdirection-column?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Card } from "@servicetitan/anvil2";

function App() {
  return (
    <Card flexDirection="column" style={{ height: 100, gap: 8, width: "100%" }}>
      <div
        style={{ flex: 2, background: "var(--background-color-strongest)" }}
      />
      <div
        style={{ flex: 1, background: "var(--background-color-strongest)" }}
      />
    </Card>
  );
}

export default App;
```

### Creating sections with Divider

To create sections within a `Card`, use a `padding` of `0` along with the `Divider`, then give each child element its own padding as needed.

card-divider

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/card-divider?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Card, Text, Divider } from "@servicetitan/anvil2";

function App() {
  return (
    <Card padding="0" flexDirection="column">
      <Text className="p-3">Section 1. Lorem ipsum dolor sit amet.</Text>

      <Divider />

      <Text className="p-3">Section 2. Lorem ipsum dolor sit amet.</Text>
    </Card>
  );
}

export default App;
```

### AI Mark

Place the [AI Mark](https://anvil.servicetitan.com/docs/components/ai-mark/code) inline with a card title by setting `aiMark` on [`Text`](https://anvil.servicetitan.com/docs/components/text/react-native) with `variant="headline"`. Use `true` for the sparkle only, or pass tooltip or popover configuration for more detail.

> [!NOTE]
>
> When a card is wrapped in `ButtonCompound`, avoid interactive AI Mark configurations (tooltip or popover triggers) inside the card if that would nest interactive elements incorrectly. For clickable cards that need an interactive mark, use the [Interactive Card](https://anvil.servicetitan.com/docs/components/interactive-card) pattern and validate with accessibility testing.

ai-mark-card

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-card?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Card, List, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Card flexDirection="column" style={{ maxWidth: "500px" }}>
      <Text variant="headline" el="h2" aiMark>
        Daily Job Summary
      </Text>
      <Text>
        You have four jobs on the board for the next two days: two drain
        cleanouts (Oak St., Riverside Ave.), a water-heater diagnostic on Maple,
        and a leak under a kitchen sink on Cedar.
      </Text>
      <br />
      <Text variant="headline" el="h3" size="small">
        High Priority Jobs
      </Text>
      <List>
        <List.Item>Drain Cleanout (Oak St.)</List.Item>
        <List.Item>Leak Under a Kitchen Sink (Cedar)</List.Item>
      </List>
    </Card>
  );
}

export default App;
```

### Interactive Cards

If the card needs to have an action applied to it, please use the [InteractiveCard component](https://anvil.servicetitan.com/docs/components/interactive-card) instead.

## React Accessibility

* Since the `Card` component can accept any valid HTML `div` props, all relevant `aria-*` props are supported.
* When creating an interactive card, the `ButtonCompound` component wraps the rendered card in a `button` element, and will use the content of the card to determine its label. If the card includes complex content, it may be helpful to use `aria-labelledby` or `aria-label`. Always test using a screenreader to ensure the label makes sense and doesn't repeat information.

For more guidance on creating accessible components, see [building accessible custom components best practices](https://anvil.servicetitan.com/docs/resources/contributing/accessible-custom-components.md).

**Card Props**

```tsx theme={null}
<Card background="strong" padding="medium">
  Card content
</Card>
```

## `Card` Props

In addition to the props listed below, the `Card` component can accept any valid HTML `div` props and layout utility props (including `flexDirection`).

**background** `"strong" | "stronger"`

Uses the default app background color if no value is provided.

**padding** `"0" | "xsmall" | "small" | "medium" | "large"` (default: medium)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.