Card
UI container for grouping content with header, footer, etc.

AW

SH

TK
Danone’s Alpro Path to Cutting-Edge Composable Web
A while back, they challenged the norm for enterprises and replaced monolithic architecture with a modern tech stack. But they didn't stop there.
Read more Categories
Marketing UI Design AI
---import Avatar from '@bejamas/ui/components/Avatar.astro';import Badge from '@bejamas/ui/components/Badge.astro';import Button from '@bejamas/ui/components/Button.astro';import Card from '@bejamas/ui/components/Card.astro';import Date from '@bejamas/ui/components/Date.astro';import Separator from '@bejamas/ui/components/Separator.astro';---
<Card class="w-full max-w-sm"> <Card part="media" class="m-1"> <img src="https://images.unsplash.com/photo-1578637387939-43c525550085?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2340" alt="Everest" class="w-full h-full object-cover" /> </Card> <Card part="header"> <div class="flex items-center gap-2 justify-between"> <div class="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale"> <Avatar> <Avatar part="image"> <img src="https://github.com/withastro.png" alt="@withastro" /> </Avatar> <Avatar part="fallback"> AW </Avatar> </Avatar> <Avatar> <Avatar part="image"> <img src="https://github.com/shadcn.png" alt="@shadcn" /> </Avatar> <Avatar part="fallback"> SH </Avatar> </Avatar> <Avatar> <Avatar part="image"> <img src="https://github.com/thomkrupa.png" alt="@thomkrupa" /> </Avatar> <Avatar part="fallback"> TK </Avatar> </Avatar> </div> <Date date="2025-08-24T12:00:00Z" class="text-sm text-muted-foreground" /> </div> </Card>
<Card part="content" class="space-y-5"> <Card part="title">Danone’s Alpro Path to Cutting-Edge Composable Web</Card> <Card part="description">A while back, they challenged the norm for enterprises and replaced monolithic architecture with a modern tech stack. But they didn't stop there.</Card> <Button variant="outline" as="a" href="#">Read more</Button> </Card>
<Separator />
<Card part="footer"> <p class="text-sm text-muted-foreground">Categories</p> <div class="flex items-center gap-2"> <Badge variant="secondary">Marketing</Badge> <Badge variant="secondary">UI Design</Badge> <Badge variant="secondary">AI</Badge> </div> </Card></Card>Installation
Section titled “Installation” bunx bejamas add card npx bejamas add card pnpm dlx bejamas add card yarn dlx bejamas add card---/** * @component Card * @title Card * @description UI container for grouping content with header, footer, etc. * @figmaUrl https://www.figma.com/design/4SCvaPvZEhlTSiDyfAonrx/-1--Bejamas-UI---Design-System?node-id=54-1310 * * @preview * * <Card class="w-full max-w-sm"> * <Card part="media" class="m-1"> * <img src="https://images.unsplash.com/photo-1578637387939-43c525550085?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2340" alt="Everest" class="w-full h-full object-cover" /> * </Card> * <Card part="header"> * <div class="flex items-center gap-2 justify-between"> * <div class="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale"> * <Avatar> * <Avatar part="image"> * <img src="https://github.com/withastro.png" alt="@withastro" /> * </Avatar> * <Avatar part="fallback"> * AW * </Avatar> * </Avatar> * <Avatar> * <Avatar part="image"> * <img src="https://github.com/shadcn.png" alt="@shadcn" /> * </Avatar> * <Avatar part="fallback"> * SH * </Avatar> * </Avatar> * <Avatar> * <Avatar part="image"> * <img src="https://github.com/thomkrupa.png" alt="@thomkrupa" /> * </Avatar> * <Avatar part="fallback"> * TK * </Avatar> * </Avatar> * </div> * <Date date="2025-08-24T12:00:00Z" class="text-sm text-muted-foreground" /> * </div> * </Card> * * <Card part="content" class="space-y-5"> * <Card part="title">Danone’s Alpro Path to Cutting-Edge Composable Web</Card> * <Card part="description">A while back, they challenged the norm for enterprises and replaced monolithic architecture with a modern tech stack. But they didn't stop there.</Card> * <Button variant="outline" as="a" href="#">Read more</Button> * </Card> * * <Separator /> * * <Card part="footer"> * <p class="text-sm text-muted-foreground">Categories</p> * <div class="flex items-center gap-2"> * <Badge variant="secondary">Marketing</Badge> * <Badge variant="secondary">UI Design</Badge> * <Badge variant="secondary">AI</Badge> * </div> * </Card> * </Card> * * @usage * * Update the import path to match your project setup. * * ```astro * --- * import Card from '@/components/ui/Card.astro'; * --- * ``` * * ```astro * <Card> * <Card part="header"> * <Card part="title">Card Title</Card> * <Card part="description">Card Description</Card> * <Card part="action"> * <button>Action</button> * </Card> * </Card> * <Card part="content"> * Card content goes here. * </Card> * <Card part="footer"> * <button>OK</button> * </Card> * </Card> * ```
* @examples * * ### Background image * * <Card class="relative aspect-[320/390] bg-gray-100 dark:bg-gray-800 w-full max-w-[320px]">
* * <Card part="header"> * <h3 class="text-xl font-medium leading-none">Jacky good boi</h3> * <p class="text-sm text-muted-foreground">loves long walks</p> * </Card> * * <Card part="footer"> * <p class="text-sm">3 years old</p> * <Button variant="secondary">Adopt me</Button> * </Card> * * <Card part="media"> * <img * alt="Happy pet" * aria-hidden="true" * class="pointer-events-none absolute inset-0 h-full w-full select-none object-cover" * src="/demo/doggo.png" * /> * <div * aria-hidden="true" * class="pointer-events-none absolute bottom-0 left-0 right-0 h-[64px]" * > * <div * class="absolute inset-0 h-[200%] backdrop-blur-sm" * style={{ * WebkitMaskImage: "linear-gradient(to top, black 80%, transparent)", * maskImage: "linear-gradient(to top, black 80%, transparent)", * maskRepeat: "no-repeat", * maskSize: "100% 100%", * }} * /> * </div> * </Card> * </Card> * * ### Orientation * * <Card class="w-full max-w-xl gap-0 pb-2" orientation="horizontal" as="a" href="#"> * <Card part="media" class="m-1 h-full max-w-[240px]"> * <img src="https://images.unsplash.com/photo-1578637387939-43c525550085?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2340" alt="Everest" class="w-full h-full object-cover" /> * </Card> * <div class="flex flex-1 flex-col gap-3"> * <Card part="header" class="gap-y-6 pt-6 block"> * <div class="flex items-center gap-2 justify-between mb-4"> * <div class="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale"> * <Avatar> * <Avatar part="image"> * <img src="https://github.com/withastro.png" alt="@withastro" /> * </Avatar> * <Avatar part="fallback">AW</Avatar> * </Avatar> * <Avatar> * <Avatar part="image"> * <img src="https://github.com/shadcn.png" alt="@shadcn" /> * </Avatar> * <Avatar part="fallback"> * SH * </Avatar> * </Avatar> * <Avatar> * <Avatar part="image"> * <img src="https://github.com/thomkrupa.png" alt="@thomkrupa" /> * </Avatar> * <Avatar part="fallback"> * TK * </Avatar> * </Avatar> * </div> * <Date date="2025-08-24T12:00:00Z" class="text-sm text-muted-foreground" /> * </div> * <Card part="title">Danone’s Alpro Path to Cutting-Edge Composable Web</Card> * </Card> * </div> * </Card> * * ### Ghost * * <Card variant="ghost" class="max-w-[320px]"> * <Card part="media"> * <img * alt="Happy pet" * aria-hidden="true" * src="https://images.unsplash.com/photo-1759223293091-f4c757c79f6c?q=80&w=2340&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" * /> * </Card> * <Card part="header"> * <h3 class="text-xl font-medium leading-none">New breakthrough in AI</h3> * <p class="text-sm text-muted-foreground"><span class="font-medium">Aug 10, 2025</span> · 10 min read</p> * </Card> * </Card> * * ### Form * * <Card class="w-full max-w-sm"> * <Card part="header"> * <Card part="title">Login to your account</Card> * <Card part="description">Enter your email below to login to your account</Card> * </Card> * * <Card part="content"> * <form> * <div class="flex flex-col gap-6"> * <div class="grid gap-2"> * <Label for="email">Email</Label> * <Input type="email" placeholder="m@acme.com" required /> * </div> * <div class="grid gap-2"> * <Label for="password">Password</Label> * <Input type="password" placeholder="Password" required /> * <div class="flex items-center"> * <a href="#" class="ml-auto text-sm underline-offset-4 hover:underline">Forgot your password?</a> * </div> * </div> * </div> * </form> * </Card> * * <Card part="footer" class="flex-col gap-2"> * <Button type="submit" class="w-full">Login</Button> * <Button variant="outline" class="w-full">Login with Google</Button> * </Card> * </Card> * * ### Overlay * * <Card variant="glass" class="max-w-[320px] w-full h-[240px]"> * <Card part="media" class="absolute inset-0 h-full w-full object-cover"> * <img src="https://images.unsplash.com/photo-1527430253228-e93688616381?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2834" alt="Happy pet" /> * </Card> * <Card part="header" class="z-10 mt-auto"> * <Card part="title" class="text-white">Thom Krupa</Card> * <Card part="description" class="text-white">Co-founder at Bejamas</Card> * </Card> * <Card part="overlay" variant="glass"> * <p>Thom Krupa is a co-founder at Bejamas, a company that builds web applications.</p> * </Card> * </Card> */
import { cva } from "class-variance-authority";import { cn } from "@bejamas/ui/lib/utils";import type { HTMLAttributes } from "astro/types";
type CardRootVariant = "default" | "ghost";type CardOverlayVariant = "default" | "glass";
type CardPart = | "root" | "media" | "header" | "title" | "description" | "action" | "content" | "footer" | "overlay";
/** * Root — main card container */type CardRootProps = { part?: "root"; variant?: "default" | "ghost"; orientation?: "vertical" | "horizontal";};
/** * Root-level slotted parts * (plugged into the root via `slot=""`) */type CardMediaProps = { part: "media"; variant?: never; class?: string;};
type CardHeaderProps = { part: "header"; variant?: never; class?: string;};
type CardContentProps = { part: "content"; variant?: never; class?: string;};
type CardFooterProps = { part: "footer"; variant?: never; class?: string;};
/** * Nested primitives (inside header / content) * They DO NOT use `slot`; they just render markup, * exactly like shadcn's CardTitle, CardDescription, CardAction. */type CardTitleProps = { part: "title"; variant?: never; class?: string;};
type CardDescriptionProps = { part: "description"; variant?: never; class?: string;};
type CardActionProps = { part: "action"; variant?: never; class?: string;};
/** * Overlay layer */type CardOverlayProps = { part: "overlay"; variant?: "default" | "glass"; class?: string;};
// This is what Astro LS “reads” for propstype Props = ( | CardRootProps | CardMediaProps | CardHeaderProps | CardContentProps | CardFooterProps | CardTitleProps | CardDescriptionProps | CardActionProps | CardOverlayProps) & Omit<HTMLAttributes<"div">, "part">;
const props = Astro.props as Props;const { part: rawPart, class: className = "", variant, ...rest } = props;
const part: CardPart = rawPart ?? "root";
/** * Variants */
const cardVariants = cva( "group/card relative bg-card overflow-clip text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm group/card [&:has(>_[data-slot=card-media]:first-child_img)]:pt-0 [a]:hover:bg-accent/5 [a]:transition-colors duration-100", { variants: { variant: { default: "", ghost: "border-transparent bg-transparent shadow-none", }, }, defaultVariants: { variant: "default", }, },);
const mediaVariants = cva( "z-0 rounded-lg overflow-hidden [&_img]:size-full [&_img]:object-cover",);
const headerVariants = cva( "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",);
const titleVariants = cva("leading-none font-medium text-lg text-balance");
const descriptionVariants = cva("text-muted-foreground text-sm");
const actionVariants = cva( "col-start-2 row-span-2 row-start-1 self-start justify-self-end",);
const contentVariants = cva("px-6");
const footerVariants = cva( "flex items-center justify-between px-6 [.border-t]:pt-6",);---
{ part === "root" && ( <div {...rest} data-slot="card" class={cn( cardVariants({ variant: (variant as CardRootVariant) ?? "default", }), className, )} > <slot /> </div> )}
{ part === "media" && ( <div {...rest} data-slot="card-media" class={cn(mediaVariants(), className)} > <slot /> </div> )}
{ part === "header" && ( <div {...rest} data-slot="card-header" class={cn(headerVariants(), className)} > <slot /> </div> )}
{ part === "title" && ( <div {...rest} data-slot="card-title" class={cn(titleVariants(), className)} > <slot /> </div> )}
{ part === "description" && ( <div {...rest} data-slot="card-description" class={cn(descriptionVariants(), className)} > <slot /> </div> )}
{ part === "action" && ( <div {...rest} data-slot="card-action" class={cn(actionVariants(), className)} > <slot /> </div> )}
{ part === "content" && ( <div {...rest} data-slot="card-content" class={cn(contentVariants(), className)} > <slot /> </div> )}
{ part === "footer" && ( <div {...rest} data-slot="card-footer" class={cn(footerVariants(), className)} > <slot /> </div> )}
{ part === "overlay" && ( <div {...rest} data-slot="card-overlay" class={cn( "inset-0 absolute z-10 opacity-0 p-6 group-hover/card:opacity-100 transition-opacity duration-300 ease-out", (variant as CardOverlayVariant) === "glass" ? "bg-primary/70 backdrop-blur-sm text-primary-foreground" : "bg-primary text-primary-foreground", className, )} > <slot /> </div> )}Update the import path to match your project setup.
---import Card from '@/components/ui/Card.astro';---<Card> <Card part="header"> <Card part="title">Card Title</Card> <Card part="description">Card Description</Card> <Card part="action"> <button>Action</button> </Card> </Card> <Card part="content"> Card content goes here. </Card> <Card part="footer"> <button>OK</button> </Card></Card>Examples
Section titled “Examples”Background image
Section titled “Background image”Jacky good boi
loves long walks
3 years old
---import Button from '@bejamas/ui/components/Button.astro';import Card from '@bejamas/ui/components/Card.astro';---
<Card class="relative aspect-[320/390] bg-gray-100 dark:bg-gray-800 w-full max-w-[320px]">
<Card part="header"> <h3 class="text-xl font-medium leading-none">Jacky good boi</h3> <p class="text-sm text-muted-foreground">loves long walks</p> </Card>
<Card part="footer"> <p class="text-sm">3 years old</p> <Button variant="secondary">Adopt me</Button> </Card>
<Card part="media"> <img alt="Happy pet" aria-hidden="true" class="pointer-events-none absolute inset-0 h-full w-full select-none object-cover" src="/demo/doggo.png" /> <div aria-hidden="true" class="pointer-events-none absolute bottom-0 left-0 right-0 h-[64px]" > <div class="absolute inset-0 h-[200%] backdrop-blur-sm" style={{ WebkitMaskImage: "linear-gradient(to top, black 80%, transparent)", maskImage: "linear-gradient(to top, black 80%, transparent)", maskRepeat: "no-repeat", maskSize: "100% 100%", }} /> </div> </Card></Card>Orientation
Section titled “Orientation”
AW

SH

TK
Danone’s Alpro Path to Cutting-Edge Composable Web
---import Avatar from '@bejamas/ui/components/Avatar.astro';import Card from '@bejamas/ui/components/Card.astro';import Date from '@bejamas/ui/components/Date.astro';---
<Card class="w-full max-w-xl gap-0 pb-2" orientation="horizontal" as="a" href="#"> <Card part="media" class="m-1 h-full max-w-[240px]"> <img src="https://images.unsplash.com/photo-1578637387939-43c525550085?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2340" alt="Everest" class="w-full h-full object-cover" /> </Card> <div class="flex flex-1 flex-col gap-3"> <Card part="header" class="gap-y-6 pt-6 block"> <div class="flex items-center gap-2 justify-between mb-4"> <div class="*:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:grayscale"> <Avatar> <Avatar part="image"> <img src="https://github.com/withastro.png" alt="@withastro" /> </Avatar> <Avatar part="fallback">AW</Avatar> </Avatar> <Avatar> <Avatar part="image"> <img src="https://github.com/shadcn.png" alt="@shadcn" /> </Avatar> <Avatar part="fallback"> SH </Avatar> </Avatar> <Avatar> <Avatar part="image"> <img src="https://github.com/thomkrupa.png" alt="@thomkrupa" /> </Avatar> <Avatar part="fallback"> TK </Avatar> </Avatar> </div> <Date date="2025-08-24T12:00:00Z" class="text-sm text-muted-foreground" /> </div> <Card part="title">Danone’s Alpro Path to Cutting-Edge Composable Web</Card> </Card> </div></Card>New breakthrough in AI
Aug 10, 2025 · 10 min read
---import Card from '@bejamas/ui/components/Card.astro';---
<Card variant="ghost" class="max-w-[320px]"> <Card part="media"> <img alt="Happy pet" aria-hidden="true" src="https://images.unsplash.com/photo-1759223293091-f4c757c79f6c?q=80&w=2340&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" /> </Card> <Card part="header"> <h3 class="text-xl font-medium leading-none">New breakthrough in AI</h3> <p class="text-sm text-muted-foreground"><span class="font-medium">Aug 10, 2025</span> · 10 min read</p> </Card></Card>Login to your account
Enter your email below to login to your account
---import Button from '@bejamas/ui/components/Button.astro';import Card from '@bejamas/ui/components/Card.astro';import Input from '@bejamas/ui/components/Input.astro';import Label from '@bejamas/ui/components/Label.astro';---
<Card class="w-full max-w-sm"> <Card part="header"> <Card part="title">Login to your account</Card> <Card part="description">Enter your email below to login to your account</Card> </Card>
<Card part="content"> <form> <div class="flex flex-col gap-6"> <div class="grid gap-2"> <Label for="email">Email</Label> <Input type="email" placeholder="m@acme.com" required /> </div> <div class="grid gap-2"> <Label for="password">Password</Label> <Input type="password" placeholder="Password" required /> <div class="flex items-center"> <a href="#" class="ml-auto text-sm underline-offset-4 hover:underline">Forgot your password?</a> </div> </div> </div> </form> </Card>
<Card part="footer" class="flex-col gap-2"> <Button type="submit" class="w-full">Login</Button> <Button variant="outline" class="w-full">Login with Google</Button> </Card></Card>Overlay
Section titled “Overlay”Thom Krupa
Co-founder at Bejamas
Thom Krupa is a co-founder at Bejamas, a company that builds web applications.
---import Card from '@bejamas/ui/components/Card.astro';---
<Card variant="glass" class="max-w-[320px] w-full h-[240px]"> <Card part="media" class="absolute inset-0 h-full w-full object-cover"> <img src="https://images.unsplash.com/photo-1527430253228-e93688616381?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2834" alt="Happy pet" /> </Card> <Card part="header" class="z-10 mt-auto"> <Card part="title" class="text-white">Thom Krupa</Card> <Card part="description" class="text-white">Co-founder at Bejamas</Card> </Card> <Card part="overlay" variant="glass"> <p>Thom Krupa is a co-founder at Bejamas, a company that builds web applications.</p> </Card></Card>