stream/ui
Browse the library

Card

A bordered surface with a hard shadow, composed from header, title, description, content, and footer parts.

stableCard.tsx

Examples

Composed card

Pro plan

popular

For teams shipping every day.

$19 / seat / month

card/basic.tsx
import { ArrowRight } from "lucide-react";
import { Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@streamuilib/react";

export default function CardBasic() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <div className="flex items-center justify-between gap-3">
          <CardTitle>Pro plan</CardTitle>
          <Badge tone="pink">popular</Badge>
        </div>
        <CardDescription>For teams shipping every day.</CardDescription>
      </CardHeader>
      <CardContent>
        <p className="text-3xl font-bold">
          $19<span className="text-sm font-normal text-ink-muted"> / seat / month</span>
        </p>
      </CardContent>
      <CardFooter>
        <Button className="w-full">
          Start trial <ArrowRight aria-hidden />
        </Button>
      </CardFooter>
    </Card>
  );
}

Tones

Soft tints keep large surfaces light.

paper

lime

pink

blue

violet

orange

card/tones.tsx
import { Card, CardContent, CardTitle, type CardTone } from "@streamuilib/react";

const tones: CardTone[] = ["paper", "lime", "pink", "blue", "violet", "orange"];

export default function CardTones() {
  return (
    <div className="grid w-full grid-cols-2 gap-4 sm:grid-cols-3">
      {tones.map((tone) => (
        <Card key={tone} tone={tone}>
          <CardContent>
            <CardTitle as="h4" className="font-mono text-sm">
              {tone}
            </CardTitle>
          </CardContent>
        </Card>
      ))}
    </div>
  );
}

Installation

bun add @streamuilib/react
usage.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@streamuilib/react";

<Card>
  <CardHeader>
    <CardTitle>Pro plan</CardTitle>
  </CardHeader>
  <CardContent>…</CardContent>
</Card>

API

Card
PropTypeDefaultDescription
tone"paper" | "lime" | "pink" | "blue" | "violet" | "orange""paper"Surface tint.
...propsComponentProps<"div">—Native div attributes.
CardTitle
PropTypeDefaultDescription
as"h2" | "h3" | "h4""h3"Heading level, to fit your page outline.
CardHeader, CardDescription, CardContent, CardFooter
PropTypeDefaultDescription
...propsnative element props—Plain layout parts; className is merged last.

Accessibility

  • CardTitle is a real heading, so cards show up in a screen reader's heading list. Pick the level that fits your page.
  • A card is a plain container. If the whole card should be clickable, put one link inside and stretch its hit area rather than wrapping everything in a link.

Source

packages/react/src/Card.tsx
import type { ComponentProps } from "react";
import { cn } from "./cn";

export type CardTone = "paper" | "lime" | "pink" | "blue" | "violet" | "orange";

const tones: Record<CardTone, string> = {
  paper: "bg-paper-raised",
  lime: "bg-lime-soft",
  pink: "bg-pink-soft",
  blue: "bg-blue-soft",
  violet: "bg-violet-soft",
  orange: "bg-orange-soft",
};

export type CardProps = ComponentProps<"div"> & {
  /** Surface tint. Soft tints keep large areas light. */
  tone?: CardTone;
};

/** A bordered surface with a hard shadow. Compose with the Card* parts. */
export function Card({ tone = "paper", className, ...props }: CardProps) {
  return (
    <div
      className={cn("flex flex-col border-2 border-ink text-ink shadow-brutal", tones[tone], className)}
      {...props}
    />
  );
}

export function CardHeader({ className, ...props }: ComponentProps<"div">) {
  return <div className={cn("flex flex-col gap-1.5 border-b-2 border-ink p-5", className)} {...props} />;
}

/** Renders an h3 by default; pass `as` to match your page's heading level. */
export function CardTitle({
  as: Heading = "h3",
  className,
  ...props
}: ComponentProps<"h3"> & { as?: "h2" | "h3" | "h4" }) {
  return <Heading className={cn("text-lg leading-tight font-bold tracking-tight", className)} {...props} />;
}

export function CardDescription({ className, ...props }: ComponentProps<"p">) {
  return <p className={cn("text-sm leading-relaxed text-ink-muted", className)} {...props} />;
}

export function CardContent({ className, ...props }: ComponentProps<"div">) {
  return <div className={cn("flex-1 p-5", className)} {...props} />;
}

export function CardFooter({ className, ...props }: ComponentProps<"div">) {
  return <div className={cn("flex flex-wrap items-center gap-3 border-t-2 border-ink p-5", className)} {...props} />;
}