Vela UI Docsvenizia.ai

Card

A bordered surface that groups a header, content and footer, with an optional action slot.

npx shadcn@latest add @velaui/card
npx shadcn@latest add @velaui/card

Examples

  • Default
  • Small

View code

Card · Default

Installation

pnpm dlx velaui add @velaui/card
pnpm dlx velaui add @velaui/card

Code

card-default.tsx
import { Button } from "@/components/ui/button";
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";

export default function Example() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Storage</CardTitle>
        <CardDescription>18.4 GB of 50 GB used</CardDescription>
        <CardAction>
          <Button variant="ghost" size="sm">
            Manage
          </Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        Files, backups and shared folders count toward your quota. Archived
        projects do not.
      </CardContent>
      <CardFooter className="justify-end gap-2">
        <Button variant="outline" size="sm">
          Details
        </Button>
        <Button size="sm">Upgrade</Button>
      </CardFooter>
    </Card>
  );
}

View code

Card · Small

Installation

pnpm dlx velaui add @velaui/card
pnpm dlx velaui add @velaui/card

Code

card-small.tsx
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";

export default function Example() {
  return (
    <div className="grid w-full max-w-md grid-cols-2 gap-3">
      <Card size="sm">
        <CardHeader>
          <CardDescription>Active users</CardDescription>
          <CardTitle>1,284</CardTitle>
        </CardHeader>
        <CardContent className="text-text-muted">+12% this week</CardContent>
      </Card>
      <Card size="sm">
        <CardHeader>
          <CardDescription>Open tickets</CardDescription>
          <CardTitle>37</CardTitle>
        </CardHeader>
        <CardContent className="text-text-muted">5 need a reply</CardContent>
      </Card>
    </div>
  );
}

Usage

import { Button } from "@/components/ui/button";
import {
  Card,
  CardAction,
  CardContent,
  CardDescription,
  CardFooter,
  CardHeader,
  CardTitle,
} from "@/components/ui/card";

export default function Example() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Storage</CardTitle>
        <CardDescription>18.4 GB of 50 GB used</CardDescription>
        <CardAction>
          <Button variant="ghost" size="sm">
            Manage
          </Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        Files, backups and shared folders count toward your quota. Archived
        projects do not.
      </CardContent>
      <CardFooter className="justify-end gap-2">
        <Button variant="outline" size="sm">
          Details
        </Button>
        <Button size="sm">Upgrade</Button>
      </CardFooter>
    </Card>
  );
}

Props

Card

PropTypeDefault
size"sm" | "default""default"

Takes these and every prop of CardProps.

CardHeader

Takes every prop of CardHeaderProps.

CardFooter

Takes every prop of CardFooterProps.

CardTitle

Takes every prop of CardTitleProps.

CardAction

Takes every prop of CardActionProps.

CardDescription

Takes every prop of CardDescriptionProps.

CardContent

Takes every prop of CardContentProps.

Dependencies

npm
none besides React
Installs with it
nothing else

Source

components/ui/card.tsx
import * as React from "react";

import { cn } from "@/lib/utils";

export type CardProps = React.ComponentProps<"div"> & { size?: "default" | "sm" };

function Card({
  className,
  size = "default",
  ...props
}: CardProps) {
  return (
    <div
      data-slot="card"
      data-size={size}
      className={cn(
        "group/card flex flex-col gap-4 overflow-hidden rounded-xl border border-border-default bg-bg-main py-(--card-spacing) text-sm text-text-strong shadow-[0px_0px_1px_0px_rgba(10,10,10,0.04),0px_0.5px_2px_-1px_rgba(10,10,10,0.12),0px_2px_4px_-2px_rgba(10,10,10,0.08)] [--card-spacing:--spacing(6)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
        className,
      )}
      {...props}
    />
  );
}

export type CardHeaderProps = React.ComponentProps<"div">;

function CardHeader({ className, ...props }: CardHeaderProps) {
  return (
    <div
      data-slot="card-header"
      className={cn(
        "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
        className,
      )}
      {...props}
    />
  );
}

export type CardTitleProps = React.ComponentProps<"div">;

function CardTitle({ className, ...props }: CardTitleProps) {
  return (
    <div
      data-slot="card-title"
      className={cn(
        "text-base leading-snug font-semibold text-text-strong group-data-[size=sm]/card:text-sm",
        className,
      )}
      {...props}
    />
  );
}

export type CardDescriptionProps = React.ComponentProps<"div">;

function CardDescription({ className, ...props }: CardDescriptionProps) {
  return (
    <div
      data-slot="card-description"
      className={cn("text-sm text-text-muted", className)}
      {...props}
    />
  );
}

export type CardActionProps = React.ComponentProps<"div">;

function CardAction({ className, ...props }: CardActionProps) {
  return (
    <div
      data-slot="card-action"
      className={cn(
        "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
        className,
      )}
      {...props}
    />
  );
}

export type CardContentProps = React.ComponentProps<"div">;

function CardContent({ className, ...props }: CardContentProps) {
  return (
    <div
      data-slot="card-content"
      className={cn("px-(--card-spacing)", className)}
      {...props}
    />
  );
}

export type CardFooterProps = React.ComponentProps<"div">;

function CardFooter({ className, ...props }: CardFooterProps) {
  return (
    <div
      data-slot="card-footer"
      className={cn(
        "flex items-center rounded-b-xl border-t border-border-default bg-bg-surface-1 p-(--card-spacing)",
        className,
      )}
      {...props}
    />
  );
}

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