# Card (/docs/card)





`Card` is a flexible surface that combines a `variant` (filled, light, outline, gradient) with a `color` prop to express semantic meaning — the same (variant × color) model used by `Button`.

```tsx
import { Card } from '@42-central/ui-react/card';

<Card variant="light" color="green" padding="none">
  <Card.Header>
    <Card.Title>Title</Card.Title>
    <Card.Description>Supporting text.</Card.Description>
  </Card.Header>
  <Card.Content>Body content.</Card.Content>
  <Card.Footer>
    <Button size="sm">Action</Button>
  </Card.Footer>
</Card>
```

## Playground [#playground]

<story.Playground />

### Full prop browser [#full-prop-browser]

<story.WithControl />

## Variants [#variants]

`variant` controls the visual treatment. Combine with `color` to express intent.

<Preview align="block" code="&#x22;<Card variant=\&#x22;default\&#x22;>Default</Card>\n<Card variant=\&#x22;filled\&#x22; color=\&#x22;brand\&#x22;>Filled</Card>\n<Card variant=\&#x22;light\&#x22; color=\&#x22;brand\&#x22;>Light</Card>\n<Card variant=\&#x22;outline\&#x22; color=\&#x22;brand\&#x22;>Outline</Card>\n<Card variant=\&#x22;gradient\&#x22; color=\&#x22;brand\&#x22;>Gradient border</Card>&#x22;">
  <div className="grid gap-3 sm:grid-cols-3">
    <Card variant="default" padding="sm">
      Default
    </Card>

    <Card variant="filled" color="brand" padding="sm">
      Filled
    </Card>

    <Card variant="light" color="brand" padding="sm">
      Light
    </Card>

    <Card variant="outline" color="brand" padding="sm">
      Outline
    </Card>

    <Card variant="gradient" color="brand" padding="sm">
      Gradient border
    </Card>
  </div>
</Preview>

`default` is independent of `color` — navy text in light theme, white text in dark theme, matching the 42 UI Kit Figma's card chrome exactly (confirmed against Figma's own light-mode test frame). The surface itself is a 4% white overlay (Figma's own `bg-primary` token, Figma's dark-theme elevation tint) rather than an opaque fill, used in both themes here — it lightens whatever's behind it rather than imposing a flat hue. `outline` shares the same tint with a solid-color border on top.

## Gradient border [#gradient-border]

The `gradient` variant uses CSS `::before` / `::after` pseudo-elements. `::before` shows only the 1 px border strip via `mask-composite: exclude`; `::after` paints a 15 % tinted fill behind the content.

<Preview code="&#x22;<Card variant=\&#x22;gradient\&#x22; color=\&#x22;brand\&#x22;>Brand</Card>\n<Card variant=\&#x22;gradient\&#x22; color=\&#x22;purple\&#x22;>Purple</Card>\n<Card variant=\&#x22;gradient\&#x22; color=\&#x22;teal\&#x22;>Teal</Card>&#x22;">
  <div className="flex gap-3">
    <Card variant="gradient" color="brand" padding="sm">
      Brand
    </Card>

    <Card variant="gradient" color="purple" padding="sm">
      Purple
    </Card>

    <Card variant="gradient" color="teal" padding="sm">
      Teal
    </Card>
  </div>
</Preview>

## Clickable card [#clickable-card]

Use `asChild` with a link to make the whole card a single click target. No wrapper needed.

<Preview code="&#x22;<Card asChild variant=\&#x22;light\&#x22; color=\&#x22;brand\&#x22;>\n  <a href=\&#x22;/dashboard\&#x22;>Open dashboard →</a>\n</Card>&#x22;">
  <Card variant="light" color="brand" padding="md">
    <a href="#">
      Open dashboard →
    </a>
  </Card>
</Preview>

## Padding [#padding]

<Preview align="block" code="&#x22;<Card padding=\&#x22;sm\&#x22;>Compact</Card>\n<Card padding=\&#x22;md\&#x22;>Default</Card>\n<Card padding=\&#x22;lg\&#x22;>Roomy</Card>\n<Card padding=\&#x22;none\&#x22;>\n  <Card.Header><Card.Title>Header</Card.Title></Card.Header>\n  <Card.Content>Body content.</Card.Content>\n  <Card.Footer><Button size=\&#x22;sm\&#x22;>OK</Button></Card.Footer>\n</Card>&#x22;">
  <div className="grid gap-3 sm:grid-cols-2">
    <Card padding="sm">
      Compact
    </Card>

    <Card padding="md">
      Default
    </Card>

    <Card padding="lg">
      Roomy
    </Card>

    <Card padding="none">
      <Card.Header>
        <Card.Title>Header</Card.Title>
      </Card.Header>

      <Card.Content>
        Body content.
      </Card.Content>

      <Card.Footer>
        <Button size="sm">OK</Button>
      </Card.Footer>
    </Card>
  </div>
</Preview>

## Dividers [#dividers]

`Card.Header`, `Card.Description`, and `Card.Footer` each take a `withBorder` boolean (default `false`) that draws a separator on the edge facing the content — bottom for `Header`, top for `Description` / `Footer`. Use it when a section needs visual separation from its neighbor, e.g. a footer with actions set apart from the body above it.

<Preview code="&#x22;<Card padding=\&#x22;none\&#x22;>\n  <Card.Header withBorder>\n    <Card.Title>Header</Card.Title>\n  </Card.Header>\n  <Card.Content>Body content.</Card.Content>\n  <Card.Footer withBorder>\n    <Button size=\&#x22;sm\&#x22;>OK</Button>\n  </Card.Footer>\n</Card>&#x22;">
  <Card padding="none">
    <Card.Header>
      <Card.Title>
        Header
      </Card.Title>
    </Card.Header>

    <Card.Content>
      Body content.
    </Card.Content>

    <Card.Footer>
      <Button size="sm">
        OK
      </Button>
    </Card.Footer>
  </Card>
</Preview>

`Card.Content` has no `withBorder` — it sits between the two, so `Header` / `Footer` already own that edge.

## Subcomponents [#subcomponents]

<TypeTable
  type="{
  Card: {
    type: 'component',
    description: 'Outer surface. Takes `variant`, `color`, and `padding`. Takes its own `testId`.',
  },
  'Card.Header': {
    type: 'component',
    description: 'Top region. `withBorder` (default false) adds a bottom border separator. Takes its own `testId`.',
  },
  'Card.Title': {
    type: 'component',
    description: 'Heading rendered as an `h3`. Semibold, normal tracking. Takes its own `testId`.',
  },
  'Card.Description': {
    type: 'component',
    description: 'Muted supporting text below the title. `withBorder` (default false) adds a top border separator. Takes its own `testId`.',
  },
  'Card.Content': {
    type: 'component',
    description: 'Main content region. Adds horizontal/vertical padding. Takes its own `testId`.',
  },
  'Card.Footer': {
    type: 'component',
    description: 'Bottom region. Right-aligns children by default. `withBorder` (default false) adds a top border separator. Takes its own `testId`.',
  },
}"
/>

Every part renders `testId` as `data-testid` on its own DOM node — see [Testing](/docs/testing).

## API [#api]

<TypeTable
  type="{
  variant: {
    type: &#x22;'default' | 'filled' | 'light' | 'outline' | 'gradient'&#x22;,
    default: &#x22;'default'&#x22;,
    description: 'Visual treatment.',
  },
  color: {
    type: 'Color',
    description: 'Accent palette. Drives CSS slot vars (--c-solid, --c-soft, --c-text). No effect on `default` variant.',
  },
  padding: {
    type: &#x22;'none' | 'sm' | 'md' | 'lg'&#x22;,
    default: &#x22;'md'&#x22;,
    description: 'Inner spacing. Use `none` when composing with subcomponents.',
  },
  asChild: {
    type: 'boolean',
    default: 'false',
    description: 'Render children as the root element (Ark UI polymorphism).',
  },
  testId: {
    type: 'string',
    description: 'Renders as `data-testid` on the root element. Every subcomponent (`Card.Header`, `Card.Title`, …) takes its own `testId` too — see [Testing](/docs/testing).',
  },
}"
/>
