ComponentSite and product
Card
Content container with product and site variants.
$ npx context-ds add cardUsage
Use it for
Grouping related content. default in the product, ring on the site.
Avoid
- Gray card backgrounds
- Heavy drop shadows
- Cards inside cards
Dependencies: class-variance-authority
Example
examples/card.tsx
import { Button } from "@/components/ui/button";import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";export function Product() { return ( <Card className="w-full max-w-sm"> <CardHeader> <CardTitle>API usage</CardTitle> <CardDescription>Requests this billing period.</CardDescription> <CardAction> <Button variant="ghost" size="sm" asChild><a href="/preview/usage-card/Product">Details</a></Button> </CardAction> </CardHeader> <CardContent> <p className="font-data text-h4 tabular-nums">48,210</p> </CardContent> </Card> );}export function Site() { return ( <Card variant="ring" className="w-full max-w-md"> <CardTitle className="text-h4">Search</CardTitle> <CardDescription className="text-body">Search the web or the news and get ranked results with clean page content, not just links.</CardDescription> </Card> );}Source
components/ui/card.tsx
import * as React from "react";import { cva, type VariantProps } from "class-variance-authority";import { cn } from "@/lib/utils";// The product's card (.glass-card): white, its hairline drawn into the shadow. The site's card sits in a soft blue ring (the ring is// a spread shadow, so it adds no layout size).const cardVariants = cva("flex flex-col bg-surface text-fg", { variants: { variant: { default: "gap-4 rounded-card px-5 py-4 shadow-card", ring: "gap-4 rounded-card p-6 shadow-[0_0_0_1px_var(--ds-color-border-hairline),0_0_0_var(--ds-space-2)_var(--ds-color-ring-tint)] md:p-8", }, }, defaultVariants: { variant: "default" },});function Card({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof cardVariants>) { return <div data-slot="card" className={cn(cardVariants({ variant }), className)} {...props} />;}function CardHeader({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="card-header" className={cn("grid auto-rows-min grid-rows-[auto_auto] items-start gap-1 has-data-[slot=card-action]:grid-cols-[1fr_auto]", className)} {...props} />;}function CardTitle({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="card-title" className={cn("text-h5 leading-snug font-normal text-fg", className)} {...props} />;}function CardDescription({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="card-description" className={cn("text-body-sm leading-relaxed text-fg-muted", className)} {...props} />;}function CardAction({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="card-action" className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)} {...props} />;}function CardContent({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="card-content" className={cn("text-body-sm text-fg", className)} {...props} />;}function CardFooter({ className, ...props }: React.ComponentProps<"div">) { return <div data-slot="card-footer" className={cn("flex items-center gap-2", className)} {...props} />;}export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent, cardVariants };Reference: context-dashboard c0ddf7a src/app/globals.css; website src/styles/globals.css (.brand-glass-ring); src/components/sections/home/product-grid.tsx.