GitHub

Card

A flexible container for grouping related content. Uses a compound component pattern — compose Header, Content, and Footer independently for full layout control.

Compound PatternDark Mode3 VariantsComposable

Install

$npx react-principles add card
01

Theme Preview

A profile card rendered with forced light and dark styling — independent of the current app theme.

Light
AJ

Alice Johnson

alice@example.com

AdminActive
Dark
AJ

Alice Johnson

alice@example.com

AdminActive
Variant
AJ

Alice Johnson

alice@example.com

AdminActive
trending_up
+12.5%

8,249

Monthly visitors

notifications

New comment

Bob left a reply on your post.

03

Code Snippet

src/ui/Card.tsx
import { Card } from "@/ui/Card";
import { Button } from "@/ui/Button";

<Card variant="elevated">
  <Card.Header>
    <Card.Title>Account Settings</Card.Title>
    <Card.Description>
      Manage your profile and preferences.
    </Card.Description>
  </Card.Header>
  <Card.Content>
    <p className="text-sm text-slate-600 dark:text-slate-400">
      Your account was last updated 3 days ago.
    </p>
  </Card.Content>
  <Card.Footer>
    <Button size="sm">Save changes</Button>
    <Button variant="ghost" size="sm">Cancel</Button>
  </Card.Footer>
</Card>

Flat exports seperti CardHeader, CardContent, dan lainnya tetap didukung untuk migrasi bertahap.

04

Copy-Paste (Single File)

Snippet ini self-contained dan bisa dipindahkan ke project lain tanpa setup alias atau util tambahan.

Card.tsx
import type { HTMLAttributes } from "react";
import { cn } from "@/lib/utils";

export type CardVariant = "default" | "elevated" | "flat";

export interface CardProps extends HTMLAttributes<HTMLDivElement> {
  variant?: CardVariant;
}

const CARD_VARIANT_CLASSES: Record<CardVariant, string> = {
  default: "bg-white dark:bg-[#161b22] border border-slate-200 dark:border-[#1f2937]",
  elevated: "bg-white dark:bg-[#161b22] border border-slate-200 dark:border-[#1f2937] shadow-lg shadow-slate-200/60 dark:shadow-black/30",
  flat: "bg-slate-50 dark:bg-[#0d1117] border border-transparent",
};

export function Card({ variant = "default", className, children, ...props }: CardProps) {
  return (
    <div className={cn("rounded-xl", CARD_VARIANT_CLASSES[variant], className)} {...props}>
      {children}
    </div>
  );
}

Card.Header = function CardHeader({ className, children, ...props }: HTMLAttributes<HTMLDivElement>) {
  return (
    <div className={cn("p-6 pb-4", className)} {...props}>
      {children}
    </div>
  );
}

Card.Title = function CardTitle({ className, children, ...props }: HTMLAttributes<HTMLHeadingElement>) {
  return (
    <h3 className={cn("text-base font-bold text-slate-900 dark:text-white leading-snug", className)} {...props}>
      {children}
    </h3>
  );
}

Card.Description = function CardDescription({ className, children, ...props }: HTMLAttributes<HTMLParagraphElement>) {
  return (
    <p className={cn("mt-1 text-sm text-slate-500 dark:text-slate-400 leading-relaxed", className)} {...props}>
      {children}
    </p>
  );
}

Card.Content = function CardContent({ className, children, ...props }: HTMLAttributes<HTMLDivElement>) {
  return (
    <div className={cn("px-6 pb-4", className)} {...props}>
      {children}
    </div>
  );
}

Card.Footer = function CardFooter({ className, children, ...props }: HTMLAttributes<HTMLDivElement>) {
  return (
    <div className={cn("px-6 pb-6 flex items-center gap-3", className)} {...props}>
      {children}
    </div>
  );
}
05

Props

All sub-components extend their corresponding HTML element attributes.

ComponentPropTypeDefaultDescription
Cardvariant"default" | "elevated" | "flat""default"Visual style — border only, shadow, or flat background.
CardclassNamestringAdditional CSS classes.
Card.HeaderclassNamestringSpacing wrapper for title + description.
Card.TitleclassNamestringRenders as h3. Bold, dark text.
Card.DescriptionclassNamestringRenders as p. Muted, secondary text.
Card.ContentclassNamestringMain body area with horizontal padding.
Card.FooterclassNamestringAction row with flex + gap. Usually holds buttons.
react-principles