# Card

> A compound surface with optional header, body, and footer slots. Reach for Card when a block of content needs its own visual frame.

- Category: data-display
- Status: stable (since 1.0.0)
- Tokens: --background-secondary, --background-tertiary, --foreground-primary, --foreground-secondary, --border-width-thin
- Playground: https://design.freecodecamp.org/playground#card
- npm dependencies: `react@>=18 <20`
- Registry dependencies: [theme](https://design.freecodecamp.org/registry/theme.md)
- Files:
  - `Card.tsx` → `src/ui/card/Card.tsx` (raw: https://design.freecodecamp.org/registry/card/Card.tsx)
  - `card.css` → `src/ui/card/card.css` (raw: https://design.freecodecamp.org/registry/card/card.css)

## Install (copy source)

1. Ensure the theme is installed once per project - tokens.css + base.css imported globally, fonts available. See https://design.freecodecamp.org/registry/theme.md and https://design.freecodecamp.org/registry/starter.md.
2. Copy the files below into `src/ui/card/` (adjust to your project layout) and import the CSS once from your global stylesheet, e.g. `@import './ui/card/card.css';`.
3. Colors, spacing and type come from tokens - tailor the component by editing the copied source; recolour by editing tokens.css, not the component CSS.

## Usage

Cards frame a self-contained chunk of content. They are compound: the
top-level `<Card>` is an `<article>`, and `Card.Header`, `Card.Body`, and
`Card.Footer` slot bands inside it.

## Usage

```tsx
import { Card } from './ui/card/Card';
<Card bordered>
  <Card.Header>
    <h3>Weekly streak</h3>
  </Card.Header>
  <Card.Body>7 days in a row.</Card.Body>
  <Card.Footer>Keep going!</Card.Footer>
</Card>;
```

## Props

`Card.Header`, `Card.Body`, and `Card.Footer` accept the same props as a
native `<div>`.

## Accessibility

The root renders as `<article>`. Provide a heading inside `Card.Header`
when the card appears in a list - assistive tech uses headings to navigate.
For collections, wrap cards in a `<ul>` / `<li>` so the count is exposed.

## Example

```tsx
import { Card } from './ui/card/Card';
import { Link } from './ui/link/Link';

<Card>
  <Card.Header>
    <span className="card__dot card__dot--purple" aria-hidden="true" />
    <p className="card__hours">300 HOURS</p>
  </Card.Header>
  <Card.Title>Responsive Web Design</Card.Title>
  <Card.Body>Build five certification projects...</Card.Body>
  <div className="progress">
    <div className="progress__bar progress__bar--purple" style={{ width: '62%' }} />
  </div>
  <Card.Footer>
    <span>62% complete</span>
    <Link href="/learn/rwd">Resume →</Link>
  </Card.Footer>
</Card>
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `bordered` | `boolean` | no | - |  |

## Source: Card.tsx

```tsx
import React, { forwardRef } from 'react';

export interface CardProps extends React.HTMLAttributes<HTMLElement> {
  bordered?: boolean;
}

type DivAttrs = React.HTMLAttributes<HTMLDivElement>;

const CardRoot = forwardRef<HTMLElement, CardProps>(
  ({ bordered, className = '', children, ...rest }, ref) => {
    const classes = ['card', bordered && 'card--bordered', className]
      .filter(Boolean)
      .join(' ');
    return (
      <article
        ref={ref as React.Ref<HTMLElement>}
        className={classes}
        {...rest}
      >
        {children}
      </article>
    );
  }
);
CardRoot.displayName = 'Card';

const CardHeader = forwardRef<HTMLElement, DivAttrs>(
  ({ className = '', children, ...rest }, ref) => (
    <header
      ref={ref as React.Ref<HTMLElement>}
      className={['card__header', className].filter(Boolean).join(' ')}
      {...rest}
    >
      {children}
    </header>
  )
);
CardHeader.displayName = 'Card.Header';

const CardTitle = forwardRef<
  HTMLHeadingElement,
  React.HTMLAttributes<HTMLHeadingElement>
>(({ className = '', children, ...rest }, ref) => (
  <h3
    ref={ref}
    className={['card__title', className].filter(Boolean).join(' ')}
    {...rest}
  >
    {children}
  </h3>
));
CardTitle.displayName = 'Card.Title';

const CardBody = forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className = '', children, ...rest }, ref) => (
  <p
    ref={ref}
    className={['card__body', className].filter(Boolean).join(' ')}
    {...rest}
  >
    {children}
  </p>
));
CardBody.displayName = 'Card.Body';

const CardFooter = forwardRef<HTMLElement, DivAttrs>(
  ({ className = '', children, ...rest }, ref) => (
    <footer
      ref={ref as React.Ref<HTMLElement>}
      className={['card__footer', className].filter(Boolean).join(' ')}
      {...rest}
    >
      {children}
    </footer>
  )
);
CardFooter.displayName = 'Card.Footer';

export const Card = Object.assign(CardRoot, {
  Header: CardHeader,
  Title: CardTitle,
  Body: CardBody,
  Footer: CardFooter
});
```

## Source: card.css

```css
.card {
  background: var(--background-secondary);
  border: 1px solid var(--border-strong);
  box-shadow: inset 0 1px 0 var(--surface-elevation-1);
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.card--bordered {
  background: var(--background-primary);
  border: var(--border-width-default) solid var(--background-quaternary);
}
.card__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--background-tertiary);
}
.card__title {
  font-size: var(--fs-lg);
  font-weight: 700;
  margin: 0;
}
.card__body {
  margin: 0;
  color: var(--foreground-secondary);
}
.card__footer {
  padding-top: 12px;
  border-top: 1px solid var(--background-tertiary);
  display: flex;
  gap: 8px;
  align-items: center;
  justify-content: space-between;
}
.card__hours {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--foreground-muted);
  margin: 0;
}
.card__dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  background: var(--foreground-quaternary);
}
.card__dot--purple {
  background: var(--purple-color);
}
.card__dot--success {
  background: var(--success-color);
}
.card__dot--danger {
  background: var(--danger-color);
}
```

## HTML / vanilla variant

```html
<article class="card">
  <header class="card__header">
    <span class="card__dot card__dot--purple"></span>
    <p class="card__hours">300 HOURS</p>
  </header>
  <h3 class="card__title">Responsive Web Design</h3>
  <p class="card__body">Build five certification projects...</p>
  <div class="progress"><div class="progress__bar progress__bar--purple" style="width:62%"></div></div>
</article>
```

Interactive behaviours for plain HTML come from the vanilla runtime (data-uikit-* attributes): https://design.freecodecamp.org/registry/vanilla.md - or download https://design.freecodecamp.org/cdn/uikit.global.js once and self-host it (do not hotlink).

## For coding agents

This library is distributed as copyable source, not an npm package. Start at https://design.freecodecamp.org/registry/starter.md, discover components via https://design.freecodecamp.org/llms.txt, and copy files into the consuming project. Keep token names intact; recolour by editing the copied tokens.css.
