# Card

Package: @commenda/pathfinder@0.1.0
Page: /docs/components/card
Figma: https://www.figma.com/design/dn7BkGYEfIyP0o2zfesfVh/The-System?node-id=938-2

```tsx
import { Badge } from '@commenda/pathfinder/badge';
import { Card } from '@commenda/pathfinder/card';

export const CardDefault = () => (
  <Card
    headerConfig={{
      title: 'Upcoming filings',
      headingLevel: 2,
      description: 'Due in the next 30 days',
      actionConfig: { label: 'View all', href: '#filings' },
    }}
  >
    Delaware annual report <Badge label="Due soon" tone="warning" />
  </Card>
);
```


## Installation [#installation]

```sh
pnpm --filter your-app add "@commenda/pathfinder@workspace:*"
```

Import `@commenda/pathfinder/styles.css` once after host CSS. Pathfinder ships compiled, prefixed Tailwind v4 CSS; the application needs no Tailwind scanning. Import from `@commenda/pathfinder/card`. Interactive callbacks belong in a client component in Next.js App Router.

## API reference [#api-reference]

| Prop | Type | Requirement | Behavior |
| --- | --- | --- | --- |
| `children` | `ReactNode` | required | Required section body |
| `headerConfig` | `CardHeaderConfig` | optional | Optional; heading level defaults to 3 |
| `footerConfig` | `CardFooterConfig` | optional | At least one action required |
| `elevation` | `"flat" \| "raised"` | optional | raised |
| `padding` | `"default" \| "none"` | optional | default: 16 body padding |
| `scrollConfig` | `{ maxHeight: NonNullable<CSSProperties["maxHeight"]>; label: string; }` | optional | Optional explicit, named scrolling region |
| `id` | `string` | optional | Stable HTML identifier for the component. |
| `className` | `string` | optional | Additional classes for the component root. |

### headerConfig fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `title` | `string` | Yes |  |
| `description` | `string` | No |  |
| `headingLevel` | `2 \| 3 \| 4 \| 5 \| 6` | No |  |
| `actionConfig` | `FeedbackAction` | No |  |

### footerConfig fields

Choose one shape; excluded fields cannot be combined.

#### Option 1

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `primaryAction` | `FeedbackAction` | Yes |  |
| `secondaryAction` | `FeedbackAction` | No |  |

#### Option 2

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `secondaryAction` | `FeedbackAction` | Yes |  |

Do not supply: `primaryAction`.

### scrollConfig fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `maxHeight` | `NonNullable<Property.MaxHeight<string \| number> \| undefined>` | Yes |  |
| `label` | `string` | Yes |  |

Root IDs, className, appropriate ARIA attributes, and typed refs are supported.

## Usage rules [#usage-rules]

Card keeps an arbitrary table, list, or form body as children, with direct header/footer configs. Header/footer actions use the established `FeedbackAction` contract: a label and either onClick or href. Primary and secondary action appearance follows the component design. Do not wrap a form/table in a card-wide link.

Body content grows naturally unless scrollConfig is supplied. It becomes a keyboard-reachable scrolling region when it actually overflows. The authored Figma examples contain different height caps; none is imposed globally. Raised cards sit on the canvas, flat cards sit within another surface.

### StatCard [#statcard]

| Prop         | Type                                 | Default / behavior            |
| ------------ | ------------------------------------ | ----------------------------- |
| label        | string                               | Required metric label         |
| value        | string or number                     | Required; zero is preserved   |
| meta         | string                               | Optional comparison/context   |
| changeConfig | `{ label: string; tone: BadgeTone }` | Optional change Badge         |
| elevation    | raised or flat                       | raised                        |
| linkConfig   | `{ href; target?; rel?; render? }`   | Optional native-link behavior |

The application formats currency/numbers and determines whether a change is positive: a negative cost change can be good. Only linked StatCards have a hover affordance. A static section Card remains a content container. Neither component fetches data or hides arbitrary children behind a fabricated loading state.

## Examples [#examples]

### Flat [#flat]


```tsx
import { Card } from '@commenda/pathfinder/card';

export const CardFlat = () => (
  <Card
    elevation="flat"
    headerConfig={{ title: 'Filing details', headingLevel: 2 }}
  >
    <p>Annual report · Delaware</p>
    <p>Due 1 March 2027</p>
  </Card>
);
```


### Footer [#footer]


```tsx
import { useState } from 'react';

import { Card } from '@commenda/pathfinder/card';

export const CardFooter = () => {
  const [saved, setSaved] = useState(false);

  return (
    <Card
      headerConfig={{ title: 'Filing reference', headingLevel: 2 }}
      footerConfig={{
        primaryAction: {
          label: saved ? 'Saved' : 'Save',
          onClick: () => {
            setSaved(true);
          },
        },
        secondaryAction: {
          label: 'Cancel',
          onClick: () => {
            setSaved(false);
          },
        },
      }}
    >
      <label>
        Reference{' '}
        <input aria-label="Filing reference" defaultValue="ANNUAL-2027" />
      </label>
    </Card>
  );
};
```


### Scrolling [#scrolling]


```tsx
import { Card } from '@commenda/pathfinder/card';

export const CardScrolling = () => (
  <Card
    padding="none"
    headerConfig={{ title: 'Upcoming filings', headingLevel: 2 }}
    scrollConfig={{ maxHeight: 160, label: 'Upcoming filing list' }}
  >
    <ul>
      {Array.from({ length: 12 }, (_, index) => (
        <li key={index}>Annual report {2027 + index}</li>
      ))}
    </ul>
  </Card>
);
```


### Stats [#stats]


```tsx
import { StatCard } from '@commenda/pathfinder/card';

export const CardStats = () => (
  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 16 }}>
    <StatCard
      label="Filed this quarter"
      value="$1,284,500"
      changeConfig={{ label: '+12%', tone: 'success' }}
      meta="vs last quarter"
    />
    <StatCard
      label="Upcoming filings"
      value={12}
      linkConfig={{ href: '#filings' }}
      meta="View all upcoming filings"
    />
    <StatCard label="Overdue" value={0} elevation="flat" />
  </div>
);
```


## Design rules [#design-rules]

Figma guidance and reviewed implementation decisions for this component. Rule IDs remain stable for source comments and AI references.

### Card and StatCard [#card-and-statcard]

Source: [938:223](https://www.figma.com/design/dn7BkGYEfIyP0o2zfesfVh/The-System?node-id=938-223), section 938:6, stat 938:182, padding-none 942:38. The Card documentation frame contains placeholder text; it is not a source of confirmed guidance.

- `SURFACE-CARD-01` — Source: white, 0.5 pearl-300 border, radius12. Raised on canvas, flat within surfaces. Section header title/description/one action, divider, body, optional footer. Header padding12/16/12; body16 or none. No arbitrary dashboard/business behavior.
- `SURFACE-CARD-02` — Pathfinder: natural body height by default. scrollConfig explicitly supplies maxHeight and region label; overflow becomes keyboard reachable. Caller-supplied dimensions are an intentional inline-style exception, not additional design tokens.
- `SURFACE-CARD-03` — Source/Pathfinder: static cards have no hover affordance. Section actions remain individual buttons/anchors; no card-wide anchor around arbitrary forms/tables. Footer config requires at least one action, using the existing FeedbackAction contract.
- `SURFACE-CARD-04` — Source/Pathfinder: StatCard is a separate small API with shared surface styles, padding16/gap8/Heading24 and optional Badge change. App formats values and chooses change meaning. linkConfig alone enables native navigation and authored hover/focus behavior; zero is a valid value.

### Source discrepancies and implementation decisions [#source-discrepancies-and-implementation-decisions]
