Aura Design System

Card

Displays a card with header, content, and footer sections.

Preview

Loading...
import {
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
  CardAction,
} from "@/components/ui/Card";
import { Button } from "@/components/ui/Button";
import { AspectRatio } from "@/components/ui/AspectRatio";

export function CardDemo() {
  return (
  <Card className="max-w-sm">
    <AspectRatio ratio={16 / 9}>
      <img
        src="https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80"
        alt="Photo by Drew Beamer"
        className="size-full object-cover"
      />
    </AspectRatio>
    <CardHeader>
      <CardTitle>Beautiful Landscape</CardTitle>
      <CardDescription>Captured in the mountains</CardDescription>
    </CardHeader>
    <CardContent>
      <p className="text-gray-12">
        A stunning view of nature's beauty captured at golden hour.
      </p>
    </CardContent>
    <CardFooter>
      <Button>View Details</Button>
    </CardFooter>
  </Card>
)

Installation

Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces

pnpm dlx shadcn@latest add @aura/card

Manual

Copy and paste the class names utility into your utils/class-names.ts file.

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Copy and paste the Card component into your components/ui/Card.tsx file.

/**
 * @description Displays a card with header, content, and footer sections.
 */
import * as React from "react";

import { cn } from "@/utils/class-names";

function Card({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card"
      className={cn(
        "bg-gray-2 border border-gray-6 rounded-md overflow-hidden",
        className
      )}
      {...props}
    />
  );
}

function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-header"
      className={cn("flex flex-col gap-0.5 p-1 pb-0", className)}
      {...props}
    />
  );
}

function CardTitle({ className, ...props }: React.ComponentProps<"h3">) {
  return (
    <h3
      data-slot="card-title"
      className={cn("text-gray-12 font-semibold leading-none", className)}
      {...props}
    />
  );
}

function CardDescription({ className, ...props }: React.ComponentProps<"p">) {
  return (
    <p
      data-slot="card-description"
      className={cn("text-gray-11", className)}
      {...props}
    />
  );
}

function CardAction({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-action"
      className={cn("flex items-center gap-0.5", className)}
      {...props}
    />
  );
}

function CardContent({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div data-slot="card-content" className={cn("p-1 pt-0", className)} {...props} />
  );
}

function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="card-footer"
      className={cn(
        "flex items-center gap-0.5 p-1 border-t border-gray-6",
        className
      )}
      {...props}
    />
  );
}

export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardAction,
  CardDescription,
  CardContent,
};

Usage

WithFooter

export const WithFooter = () => (
  <Card>
    <CardHeader>
      <CardTitle>Project Setup</CardTitle>
      <CardDescription>Configure your project settings</CardDescription>
    </CardHeader>
    <CardContent>
      <p className="text-gray-12">
        Your project has been created successfully.
      </p>
    </CardContent>
    <CardFooter>
      <Button>Continue</Button>
    </CardFooter>
  </Card>
)

WithActions

export const WithActions = () => (
  <Card>
    <CardHeader>
      <div className="flex items-start justify-between">
        <div className="flex flex-col gap-0.5">
          <CardTitle>Notifications</CardTitle>
          <CardDescription>You have 3 unread messages</CardDescription>
        </div>
        <CardAction>
          <Button variant="menu">Mark all read</Button>
        </CardAction>
      </div>
    </CardHeader>
    <CardContent>
      <div className="space-y-1">
        <p className="text-gray-12">New comment on your post</p>
        <p className="text-gray-11">2 minutes ago</p>
      </div>
    </CardContent>
  </Card>
)

SimpleCard

export const SimpleCard = () => (
  <Card>
    <CardContent>
      <p className="text-gray-12">A simple card with just content.</p>
    </CardContent>
  </Card>
)

MultipleCards

export const MultipleCards = () => (
  <div className="flex flex-col gap-1">
    <Card>
      <CardHeader>
        <CardTitle>First Card</CardTitle>
        <CardDescription>This is the first card</CardDescription>
      </CardHeader>
      <CardContent>
        <p className="text-gray-12">Content for the first card.</p>
      </CardContent>
    </Card>
    <Card>
      <CardHeader>
        <CardTitle>Second Card</CardTitle>
        <CardDescription>This is the second card</CardDescription>
      </CardHeader>
      <CardContent>
        <p className="text-gray-12">Content for the second card.</p>
      </CardContent>
    </Card>
    <Card>
      <CardHeader>
        <CardTitle>Third Card</CardTitle>
        <CardDescription>This is the third card</CardDescription>
      </CardHeader>
      <CardContent>
        <p className="text-gray-12">Content for the third card.</p>
      </CardContent>
    </Card>
  </div>
)

NestedCards

export const NestedCards = () => (
  <Card>
    <CardHeader>
      <CardTitle>Parent Card</CardTitle>
      <CardDescription>This card contains nested cards</CardDescription>
    </CardHeader>
    <CardContent>
      <div className="flex flex-col gap-1">
        <Card className="bg-gray-3">
          <CardContent>
            <p className="text-gray-12">Nested card 1</p>
          </CardContent>
        </Card>
        <Card className="bg-gray-3">
          <CardContent>
            <p className="text-gray-12">Nested card 2</p>
          </CardContent>
        </Card>
      </div>
    </CardContent>
  </Card>
)

InteractiveCard

export const InteractiveCard = () => (
  <Card className="cursor-pointer hover:border-gray-8 transition-colors">
    <CardHeader>
      <CardTitle>Clickable Card</CardTitle>
      <CardDescription>Hover to see the effect</CardDescription>
    </CardHeader>
    <CardContent>
      <p className="text-gray-12">
        This card has hover states for interactive experiences.
      </p>
    </CardContent>
  </Card>
)

CardGrid

export const CardGrid = () => (
  <div className="grid grid-cols-3 gap-1">
    {Array.from({ length: 6 }).map((_, i) => (
      <Card key={i}>
        <CardHeader>
          <CardTitle>Card {i + 1}</CardTitle>
        </CardHeader>
        <CardContent>
          <p className="text-gray-12">Grid item content</p>
        </CardContent>
      </Card>
    ))}
  </div>
)

ImageGrid

export const ImageGrid = () => (
  <div className="grid grid-cols-3 gap-1">
    {[
      {
        src: "https://images.unsplash.com/photo-1588345921523-c2dcdb7f1dcd?w=800&dpr=2&q=80",
        title: "Mountain View",
        description: "Scenic landscape",
      },
      {
        src: "https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d?w=800&dpr=2&q=80",
        title: "Workspace",
        description: "Modern office setup",
      },
      {
        src: "https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?w=800&dpr=2&q=80",
        title: "Technology",
        description: "Digital innovation",
      },
    ].map((item, i) => (
      <Card key={i}>
        <AspectRatio ratio={4 / 3}>
          <img
            src={item.src}
            alt={item.title}
            className="size-full object-cover"
          />
        </AspectRatio>
        <CardHeader>
          <CardTitle>{item.title}</CardTitle>
          <CardDescription>{item.description}</CardDescription>
        </CardHeader>
      </Card>
    ))}
  </div>
)