codefast/ui

Command Palette

Search for a command to run...

Source
Layout

Card

Elevated surface for grouping related content. Compose Header, Content, and Footer slots freely.

Examples

Spacing

Use negative margins with -mx-(--card-spacing) to make content go edge to edge while keeping it aligned with the card inset. When the edge-to-edge content sits above a footer, use -mb-(--card-spacing) on CardContent to remove the section gap.

Terms of Service
Review the terms before accepting the agreement.

These terms govern your use of the workspace, including access to shared documents, project files, and collaboration tools.

You are responsible for the content you upload and for ensuring that your team has the appropriate permissions to view or edit it.

We may update features or limits as the service evolves. When those changes materially affect your workflow, we will notify your workspace administrators.

By continuing, you agree to keep your account credentials secure and to follow your organization's acceptable use policies.

37 lines
import { Button } from "@codefast/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@codefast/ui/card";

export function CardEdgeToEdge() {
  return (
    <Card className="mx-auto w-full max-w-sm">
      <CardHeader>
        <CardTitle>Terms of Service</CardTitle>
        <CardDescription>Review the terms before accepting the agreement.</CardDescription>
      </CardHeader>
      <CardContent className="-mb-(--card-spacing)">
        <div className="-mx-(--card-spacing) max-h-48 space-y-4 overflow-y-scroll border-t bg-muted/50 px-(--card-spacing) py-4 text-sm leading-relaxed">
          <p>
            These terms govern your use of the workspace, including access to shared documents, project files, and
            collaboration tools.
          </p>
          <p>
            You are responsible for the content you upload and for ensuring that your team has the appropriate
            permissions to view or edit it.
          </p>
          <p>
            We may update features or limits as the service evolves. When those changes materially affect your workflow,
            we will notify your workspace administrators.
          </p>
          <p>
            By continuing, you agree to keep your account credentials secure and to follow your organization&apos;s
            acceptable use policies.
          </p>
        </div>
      </CardContent>
      <CardFooter className="justify-end gap-2">
        <Button variant="outline">Decline</Button>
        <Button>Accept</Button>
      </CardFooter>
    </Card>
  );
}

Image

Add an image before the card header to create a card with an image.

Event cover
Featured
Design systems meetup
A practical talk on component APIs, accessibility, and shipping faster.
30 lines
import { Badge } from "@codefast/ui/badge";
import { Button } from "@codefast/ui/button";
import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from "@codefast/ui/card";
import { Image } from "@unpic/react";

export function CardImage() {
  return (
    <Card className="relative mx-auto w-full max-w-sm pt-0">
      <div className="absolute inset-0 z-30 aspect-video bg-black/35" />
      <Image
        alt="Event cover"
        className="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40"
        height={360}
        layout="constrained"
        src="https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=640&q=80"
        width={640}
      />
      <CardHeader>
        <CardAction>
          <Badge variant="secondary">Featured</Badge>
        </CardAction>
        <CardTitle>Design systems meetup</CardTitle>
        <CardDescription>A practical talk on component APIs, accessibility, and shipping faster.</CardDescription>
      </CardHeader>
      <CardFooter>
        <Button className="w-full">View Event</Button>
      </CardFooter>
    </Card>
  );
}

RTL

Right-to-left layout support for languages such as Arabic and Hebrew.

Translations are AI-generated for demonstration and may be imperfect.

تسجيل الدخول إلى حسابك
أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك
95 lines
import { Button } from "@codefast/ui/button";
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@codefast/ui/card";
import { Input } from "@codefast/ui/input";
import { Label } from "@codefast/ui/label";

import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";

const translations: Translations = {
  en: {
    dir: "ltr",
    values: {
      title: "Login to your account",
      description: "Enter your email below to login to your account",
      signUp: "Sign Up",
      email: "Email",
      emailPlaceholder: "m@example.com",
      password: "Password",
      forgotPassword: "Forgot your password?",
      login: "Login",
      loginWithGoogle: "Login with Google",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      title: "تسجيل الدخول إلى حسابك",
      description: "أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك",
      signUp: "إنشاء حساب",
      email: "البريد الإلكتروني",
      emailPlaceholder: "m@example.com",
      password: "كلمة المرور",
      forgotPassword: "نسيت كلمة المرور؟",
      login: "تسجيل الدخول",
      loginWithGoogle: "تسجيل الدخول باستخدام Google",
    },
  },
  he: {
    dir: "rtl",
    values: {
      title: "התחבר לחשבון שלך",
      description: "הזן את האימייל שלך למטה כדי להתחבר לחשבון שלך",
      signUp: "הירשם",
      email: "אימייל",
      emailPlaceholder: "m@example.com",
      password: "סיסמה",
      forgotPassword: "שכחת את הסיסמה?",
      login: "התחבר",
      loginWithGoogle: "התחבר עם Google",
    },
  },
};

export function CardRtl() {
  const { dir, t } = useTranslation(translations, "ar");

  return (
    <Card className="w-full max-w-sm" dir={dir}>
      <CardHeader>
        <CardTitle>{t.title}</CardTitle>
        <CardDescription>{t.description}</CardDescription>
        <CardAction>
          <Button variant="link">{t.signUp}</Button>
        </CardAction>
      </CardHeader>
      <CardContent>
        <form>
          <div className="flex flex-col gap-6">
            <div className="grid gap-2">
              <Label htmlFor="email-rtl">{t.email}</Label>
              <Input id="email-rtl" type="email" placeholder={t.emailPlaceholder} required />
            </div>
            <div className="grid gap-2">
              <div className="flex items-center">
                <Label htmlFor="password-rtl">{t.password}</Label>
                <a href="/" className="ms-auto inline-block text-sm underline-offset-4 hover:underline">
                  {t.forgotPassword}
                </a>
              </div>
              <Input id="password-rtl" type="password" required />
            </div>
          </div>
        </form>
      </CardContent>
      <CardFooter className="flex-col gap-2">
        <Button type="submit" className="w-full">
          {t.login}
        </Button>
        <Button variant="outline" className="w-full">
          {t.loginWithGoogle}
        </Button>
      </CardFooter>
    </Card>
  );
}

Size

Use the size='sm' prop to set the size of the card to small. The small size variant uses smaller spacing.

Small Card
This card uses the small size variant.

The card component supports a size prop that can be set to "sm" for a more compact appearance.

21 lines
import { Button } from "@codefast/ui/button";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@codefast/ui/card";

export function CardSmall() {
  return (
    <Card size="sm" className="mx-auto w-full max-w-sm">
      <CardHeader>
        <CardTitle>Small Card</CardTitle>
        <CardDescription>This card uses the small size variant.</CardDescription>
      </CardHeader>
      <CardContent>
        <p>The card component supports a size prop that can be set to &quot;sm&quot; for a more compact appearance.</p>
      </CardContent>
      <CardFooter>
        <Button variant="outline" size="sm" className="w-full">
          Action
        </Button>
      </CardFooter>
    </Card>
  );
}

Spacing

In addition to the size prop, you can use the --card-spacing CSS variable to control the spacing between sections and the inset of card parts.

Login to your account
Enter your email below to login to your account
93 lines
import { Button } from "@codefast/ui/button";
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@codefast/ui/card";
import { Input } from "@codefast/ui/input";
import { Label } from "@codefast/ui/label";
import { ToggleGroup, ToggleGroupItem } from "@codefast/ui/toggle-group";
import * as React from "react";

const spacingOptions = [
  {
    className: "[--card-spacing:--spacing(4)]",
    label: "16px",
    value: "4",
  },
  {
    className: "[--card-spacing:--spacing(5)]",
    label: "20px",
    value: "5",
  },
  {
    className: "[--card-spacing:--spacing(6)]",
    label: "24px",
    value: "6",
  },
  {
    className: "[--card-spacing:--spacing(8)]",
    label: "32px",
    value: "8",
  },
];

export function CardSpacing() {
  const [spacing, setSpacing] = React.useState("4");
  const selectedSpacing = spacingOptions.find((option) => option.value === spacing);

  return (
    <div className="mx-auto grid w-full max-w-sm gap-4">
      <ToggleGroup
        type="single"
        value={spacing}
        onValueChange={(value) => {
          if (value) {
            setSpacing(value);
          }
        }}
        variant="outline"
        size="sm"
        className="justify-center"
      >
        {spacingOptions.map((option) => (
          <ToggleGroupItem key={option.value} value={option.value}>
            {option.label}
          </ToggleGroupItem>
        ))}
      </ToggleGroup>
      <Card className={selectedSpacing?.className}>
        <CardHeader>
          <CardTitle>Login to your account</CardTitle>
          <CardDescription>Enter your email below to login to your account</CardDescription>
          <CardAction>
            <Button variant="link">Sign Up</Button>
          </CardAction>
        </CardHeader>
        <CardContent>
          <form>
            <div className="flex flex-col gap-6">
              <div className="grid gap-2">
                <Label htmlFor="email-spacing">Email</Label>
                <Input id="email-spacing" type="email" placeholder="m@example.com" required />
              </div>
              <div className="grid gap-2">
                <div className="flex items-center">
                  <Label htmlFor="password-spacing">Password</Label>
                  <a href="/" className="ms-auto inline-block text-sm underline-offset-4 hover:underline">
                    Forgot your password?
                  </a>
                </div>
                <Input id="password-spacing" type="password" required />
              </div>
            </div>
          </form>
        </CardContent>
        <CardFooter className="flex-col gap-2">
          <Button type="submit" className="w-full">
            Login
          </Button>
          <Button variant="outline" className="w-full">
            Login with Google
          </Button>
        </CardFooter>
      </Card>
    </div>
  );
}

Usage

The minimal import and composition — see Examples below for styled, real-world variants.

15 lines
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@codefast/ui/card";

export function CardUsage() {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Card Title</CardTitle>
        <CardDescription>Card description goes here.</CardDescription>
      </CardHeader>
      <CardContent>
        <p>Card content goes here.</p>
      </CardContent>
    </Card>
  );
}

Anatomy

How the parts nest — every slot the component exposes, in composition order.

Card
├── CardHeader
│ ├── CardTitle
│ ├── CardDescription
│ └── CardAction
├── CardContent
└── CardFooter

Features

  • Two sizes (default, sm) via the --card-spacing CSS variable, adjustable per-instance without a new size value.
  • CardHeader switches to a 2-column grid automatically when a CardAction is present, and reserves a second row when a CardDescription follows the title.
  • A leading or trailing <img> direct child gets its corners rounded to match the card automatically.

API reference

Props for each part of the component. All native element props are also forwarded.

Card

The surface. Every part below is a styled <div> you can omit or reorder.

classNamestring

Compose width, shadow, and spacing — the card owns no layout of its own.

CardHeader

Hosts CardTitle, CardDescription, and an optional CardAction.

childrenReactNode

CardTitle, CardDescription, and an optional CardAction.

CardFooter

The bottom bar, set off from CardContent by a border and muted background.

childrenReactNode

Footer actions or supplementary content.

CardContent

The main body of the card.

childrenReactNode

The card's primary content.

Accessibility

Built to be keyboard-navigable and screen-reader friendly out of the box.

  • Card is a presentational container with no implicit role — structure content with real headings.
  • When a whole card is clickable, wrap it in a single link/button rather than nesting interactives.
  • Use CardTitle as a real heading level that fits the surrounding document outline.

Guidelines

Conventions that keep usage consistent across an app.

Do

  • Group genuinely related content; one idea per card.
  • Keep actions in CardFooter or CardAction so they’re easy to find.

Don’t

  • Don’t nest cards inside cards — it muddies hierarchy.
  • Don’t put more than one primary action in a single card.

Explore further

Ready to integrate?

Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.