Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Card

Displays a card with header, content, and footer.

Playground

Project settings
Update how this project appears across the workspace.
Pro
—size
—--card-spacing
—CardAction
—CardFooter
Usage
Compose a Card from CardHeader —which groups CardTitle, CardDescription and an optional CardAction pinned to the top end—, CardContent and CardFooter. Every part renders a plain div, so all sections are optional and accept any div props.
Card Title
Card Description
Card Action

Card Content

Card Footer

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

<Card>
  <CardHeader>
    <CardTitle>Card Title</CardTitle>
    <CardDescription>Card Description</CardDescription>
    <CardAction>Card Action</CardAction>
  </CardHeader>
  <CardContent>
    <p>Card Content</p>
  </CardContent>
  <CardFooter>
    <p>Card Footer</p>
  </CardFooter>
</Card>
With a form
The classic login card: a header with title, description and a CardAction link, form fields in CardContent, and stacked actions in a CardFooter with flex-col.
Login to your account
Enter your email below to login to your account
Forgot your password?
<Card className="w-full max-w-sm">
  <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">Email</Label>
          <Input id="email" type="email" placeholder="m@example.com" required />
        </div>
        <div className="grid gap-2">
          <div className="flex items-center">
            <Label htmlFor="password">Password</Label>
            <a
              href="#"
              className="ms-auto inline-block text-sm underline-offset-4 hover:underline"
            >
              Forgot your password?
            </a>
          </div>
          <Input id="password" 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>
Size
Use size="sm" for a compact card — it drops --card-spacing from 24px to 16px and the title from text-lg to text-sm.
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.

<Card size="sm" className="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
      "sm" for a more compact appearance.
    </p>
  </CardContent>
  <CardFooter>
    <Button variant="outline" size="sm" className="w-full">
      Action
    </Button>
  </CardFooter>
</Card>
Spacing
Beyond the size prop, the --card-spacing CSS variable controls the gap between sections and the inset of every part — set it with an arbitrary property like [--card-spacing:--spacing(8)]. Adding border-b to the header or border-t to the footer also restores their inner padding automatically.
Roomy card
This card overrides --card-spacing to 32px.

Every section inset and the gap between sections track the same variable, so the whole card scales together.

<Card className="w-full max-w-sm [--card-spacing:--spacing(8)]">
  <CardHeader className="border-b">
    <CardTitle>Roomy card</CardTitle>
    <CardDescription>
      This card overrides --card-spacing to 32px.
    </CardDescription>
  </CardHeader>
  <CardContent>
    <p>
      Every section inset and the gap between sections track the same
      variable, so the whole card scales together.
    </p>
  </CardContent>
</Card>
Edge to edge
Use negative margins with -mx-(--card-spacing) to make content run edge to edge while staying aligned with the card inset. When the edge-to-edge block sits above a footer, add -mb-(--card-spacing) to CardContent to remove the section gap, and border-t to the CardFooter so it restores its own padding above the actions.
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 the acceptable use policies of your organization.

<Card className="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>...</p>
    </div>
  </CardContent>
  <CardFooter className="justify-end gap-2 border-t">
    <Button variant="outline">Decline</Button>
    <Button>Accept</Button>
  </CardFooter>
</Card>
Image
Place an img as the first child of the card to build a cover — the card removes its own top padding and rounds the image corners automatically via has-[>img:first-child]:pt-0. The same applies to a trailing image and the bottom corners.
Event cover
Featured
Design systems meetup
A practical talk on component APIs, accessibility, and shipping faster.
<Card className="w-full max-w-sm">
  <img
    src="/images/event-cover.jpg"
    alt="Event cover"
    className="aspect-video w-full object-cover"
  />
  <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
Set dir="rtl" on the card or inherit it from a Direction provider — the CardAction column and logical spacing classes like ms-auto flip automatically.
تسجيل الدخول إلى حسابك
أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك
<Card dir="rtl" className="w-full max-w-sm">
  <CardHeader>
    <CardTitle>تسجيل الدخول إلى حسابك</CardTitle>
    <CardDescription>
      أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك
    </CardDescription>
    <CardAction>
      <Button variant="link">إنشاء حساب</Button>
    </CardAction>
  </CardHeader>
  <CardContent>
    <div className="grid gap-2">
      <Label htmlFor="email">البريد الإلكتروني</Label>
      <Input id="email" type="email" placeholder="m@example.com" required />
    </div>
  </CardContent>
  <CardFooter className="flex-col gap-2">
    <Button type="submit" className="w-full">
      تسجيل الدخول
    </Button>
  </CardFooter>
</Card>
API Reference
Every part renders a plain div and accepts all div props — only Card adds a prop of its own. Spacing is driven by the --card-spacing variable, which the size prop presets and any className can override.
PropTypeDefault
Card size"default" | "sm""default"
className (all parts)string—
--card-spacingCSS length--spacing(6) / --spacing(4) on sm

Tokens used

10 design tokens consumed by the Card component.

  • --card

    Card

    bg-card
  • --card-foreground

    Card Foreground

    text-card-foreground
  • --muted-foreground

    Muted Foreground

    text-muted-foreground
  • --foreground

    Foreground

    ring-foreground/10
  • --font-heading

    GT Alpina Condensed

    font-heading
  • --font-weight-medium

    Font weight Medium (500)

    font-medium
  • --text-lg

    Text Large (18px)

    text-lg
  • --text-sm

    Text Small (14px)

    text-smgroup-data-[size=sm]/card:text-sm
  • --radius-xl

    Radius 1.4× (14px)

    rounded-xlrounded-t-xlrounded-b-xl
  • --spacing

    Spacing unit (4px)

    [--card-spacing:--spacing(6)]data-[size=sm]:[--card-spacing:--spacing(4)]gap-(--card-spacing)py-(--card-spacing)px-(--card-spacing)