Card

Bordered content surface with header, title, description, content, and footer subcomponents.

moleculesSource
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@elirobinson/react/components/molecules/Card';

Styles: @elirobinson/react/styles/molecules/Card.css — already included when you import @elirobinson/react/styles.css.

FoodFamily

Kids Recipes

Simple, fun recipes designed for kids.

Show code
import { Badge } from '@elirobinson/react/components/atoms/Badge';
import { Button } from '@elirobinson/react/components/atoms/Button';
import {
  Card,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '@elirobinson/react/components/molecules/Card';

export default function Basic() {
  return (
    <div className="demo-col">
      <Card>
        <CardHeader>
          <div className="demo-row">
            <Badge>Food</Badge>
            <Badge>Family</Badge>
          </div>
          <CardTitle>Kids Recipes</CardTitle>
          <CardDescription>Simple, fun recipes designed for kids.</CardDescription>
        </CardHeader>
        <CardFooter>
          <Button variant="ghost" size="sm">
            View project
          </Button>
        </CardFooter>
      </Card>
    </div>
  );
}

When to use it

Use Card to group related content into one bordered surface — a portfolio tile, a pricing tier, a contact block. It's a layout primitive, not a status signal: for a message about the state of the page, reach for Alert; for a placeholder where a list of cards would normally go, use EmptyState.

CardHeader, CardTitle, CardDescription, CardContent, and CardFooter are plain layout slots — use the ones you need and skip the rest. A card doesn't require content and a footer both.

With content and actions

Get in touch

Tell me what you need — no contracts required.

Practical AI consulting and tech support, from $150/hr.

Show code
import { Button } from '@elirobinson/react/components/atoms/Button';
import {
  Card,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from '@elirobinson/react/components/molecules/Card';

export default function WithContent() {
  return (
    <div className="demo-col">
      <Card>
        <CardHeader>
          <CardTitle>Get in touch</CardTitle>
          <CardDescription>Tell me what you need — no contracts required.</CardDescription>
        </CardHeader>
        <CardContent>
          <p>Practical AI consulting and tech support, from $150/hr.</p>
        </CardContent>
        <CardFooter>
          <Button variant="accent">Hire me</Button>
          <Button variant="secondary">Learn more</Button>
        </CardFooter>
      </Card>
    </div>
  );
}

Props

No props of its own beyond the inherited HTML attributes.

Also accepts all HTMLAttributes<HTMLDivElement> props.

CardHeader

No props of its own beyond the inherited HTML attributes.

Also accepts all HTMLAttributes<HTMLDivElement> props.

CardTitle

No props of its own beyond the inherited HTML attributes.

Also accepts all HTMLAttributes<HTMLHeadingElement> props.

CardDescription

No props of its own beyond the inherited HTML attributes.

Also accepts all HTMLAttributes<HTMLParagraphElement> props.

CardContent

No props of its own beyond the inherited HTML attributes.

Also accepts all HTMLAttributes<HTMLDivElement> props.

CardFooter

No props of its own beyond the inherited HTML attributes.

Also accepts all HTMLAttributes<HTMLDivElement> props.

Accessibility

  • Card and its subcomponents render plain <div>s (and CardTitle an <h3>, CardDescription a <p>) with no implicit ARIA role — a card is a visual grouping, not a landmark.
  • CardTitle always renders an <h3>. It isn't configurable, so check that an <h3> is the right level in the surrounding heading outline before reaching for it — inside a page that's already past <h3>, write your own heading element instead.
  • Only Card itself forwards a ref, to the outer <div>. CardHeader, CardTitle, CardDescription, CardContent, and CardFooter are plain functions with no ref forwarding.
  • Keyboard: a Card has no interaction of its own. Any buttons or links placed inside it (as in CardFooter) keep their own native keyboard behavior.

Do

  • Put the primary action in CardFooter as a real Button or link.
  • Keep CardTitle to a short name — a product, a plan, a project — and put detail in CardDescription.
  • Use Badge inside CardHeader for category tags above the title.
  • Give a Card a fixed maxWidth in a grid so cards align instead of stretching unevenly.

Don't

  • Wrap an entire page section in a Card — it's a content surface, not a page layout tool.
  • Nest a Card inside a Card; flatten the hierarchy instead.
  • Make the whole card clickable with a div onClick — wrap the title or an explicit link so keyboard users can reach it.
  • Skip CardHeader and put a raw heading directly in CardContent — the header slot carries the spacing that makes the title read as a title.