Card
---import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/starwind/card";import { Button } from "@/components/starwind/button";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, } from "@/components/starwind/select";---
<Card class="w-[360px]"> <CardHeader> <CardTitle>Create project</CardTitle> <CardDescription>Deploy your new project in one-click.</CardDescription> </CardHeader> <CardContent class="flex flex-col gap-4"> <div class="flex w-full flex-col gap-2"> <Label for="project-name">Name</Label> <Input type="text" id="project-name" placeholder="Name of your project" /> </div> <div class="flex w-full flex-col gap-2"> <Label for="project-framework">Framework</Label> <Select name="framework" required> <SelectTrigger id="project-framework" class="w-full"> <SelectValue placeholder="Select" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Frameworks</SelectLabel> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="next">Next.js</SelectItem> <SelectItem value="svelte">SvelteKit</SelectItem> <SelectItem value="solid">SolidStart</SelectItem> </SelectGroup> </SelectContent> </Select> </div> </CardContent> <CardFooter class="flex justify-between"> <Button variant="outline">Cancel</Button> <Button type="submit">Deploy</Button> </CardFooter></Card>import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/starwind/card";import { Button } from "@/components/starwind/button";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, } from "@/components/starwind/select";
export function Example() { return ( <Card className="w-[360px]"> <CardHeader> <CardTitle>Create project</CardTitle> <CardDescription>Deploy your new project in one-click.</CardDescription> </CardHeader> <CardContent className="flex flex-col gap-4"> <div className="flex w-full flex-col gap-2"> <Label htmlFor="project-name">Name</Label> <Input type="text" id="project-name" placeholder="Name of your project" /> </div> <div className="flex w-full flex-col gap-2"> <Label htmlFor="project-framework">Framework</Label> <Select name="framework" required> <SelectTrigger id="project-framework" className="w-full"> <SelectValue placeholder="Select" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Frameworks</SelectLabel> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="next">Next.js</SelectItem> <SelectItem value="svelte">SvelteKit</SelectItem> <SelectItem value="solid">SolidStart</SelectItem> </SelectGroup> </SelectContent> </Select> </div> </CardContent> <CardFooter className="flex justify-between"> <Button variant="outline">Cancel</Button> <Button type="submit">Deploy</Button> </CardFooter> </Card> );}Installation
pnpx starwind@latest add cardnpx starwind@latest add cardyarn dlx starwind@latest add cardUsage
---import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/starwind/card";import { Button } from "@/components/starwind/button";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, } from "@/components/starwind/select";---
<Card class="w-[360px]"> <CardHeader> <CardTitle>Create project</CardTitle> <CardDescription>Deploy your new project in one-click.</CardDescription> </CardHeader> <CardContent class="flex flex-col gap-4"> <div class="flex w-full flex-col gap-2"> <Label for="project-name">Name</Label> <Input type="text" id="project-name" placeholder="Name of your project" /> </div> <div class="flex w-full flex-col gap-2"> <Label for="project-framework">Framework</Label> <Select name="framework" required> <SelectTrigger id="project-framework" class="w-full"> <SelectValue placeholder="Select" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Frameworks</SelectLabel> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="next">Next.js</SelectItem> <SelectItem value="svelte">SvelteKit</SelectItem> <SelectItem value="solid">SolidStart</SelectItem> </SelectGroup> </SelectContent> </Select> </div> </CardContent> <CardFooter class="flex justify-between"> <Button variant="outline">Cancel</Button> <Button type="submit">Deploy</Button> </CardFooter></Card>import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/starwind/card";import { Button } from "@/components/starwind/button";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, } from "@/components/starwind/select";
export function Example() { return ( <Card className="w-[360px]"> <CardHeader> <CardTitle>Create project</CardTitle> <CardDescription>Deploy your new project in one-click.</CardDescription> </CardHeader> <CardContent className="flex flex-col gap-4"> <div className="flex w-full flex-col gap-2"> <Label htmlFor="project-name">Name</Label> <Input type="text" id="project-name" placeholder="Name of your project" /> </div> <div className="flex w-full flex-col gap-2"> <Label htmlFor="project-framework">Framework</Label> <Select name="framework" required> <SelectTrigger id="project-framework" className="w-full"> <SelectValue placeholder="Select" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Frameworks</SelectLabel> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="next">Next.js</SelectItem> <SelectItem value="svelte">SvelteKit</SelectItem> <SelectItem value="solid">SolidStart</SelectItem> </SelectGroup> </SelectContent> </Select> </div> </CardContent> <CardFooter className="flex justify-between"> <Button variant="outline">Cancel</Button> <Button type="submit">Deploy</Button> </CardFooter> </Card> );}Composition
Use the parts that match the content in your card:
Card├── CardHeader│ ├── CardTitle│ ├── CardDescription│ └── CardAction├── CardContent└── CardFooterForm Submission
You can easily add a standard html form submission to your card. Here is a full example with the javascript submission code.
Tip
Open up your browser’s dev tools and view the console to see the form data.
---import { Button } from "@/components/starwind/button";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue,} from "@/components/starwind/select";---
<Card class="w-[360px]"> <CardHeader> <CardTitle>Create project</CardTitle> <CardDescription>Deploy your new project in one-click.</CardDescription> </CardHeader> <form id="create-project-form"> <CardContent class="flex flex-col gap-4"> <div class="flex w-full flex-col gap-2"> <Label for="create-project-name">Name</Label> <Input type="text" id="create-project-name" name="name" placeholder="Name of your project" required /> </div> <div class="flex w-full flex-col gap-2"> <Label for="create-project-framework">Framework</Label> <Select name="framework" required> <SelectTrigger id="create-project-framework" class="w-full"> <SelectValue placeholder="Select" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Frameworks</SelectLabel> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="next">Next.js</SelectItem> <SelectItem value="svelte">SvelteKit</SelectItem> <SelectItem value="solid">SolidStart</SelectItem> </SelectGroup> </SelectContent> </Select> </div> </CardContent> <CardFooter class="mt-6 flex justify-between"> <Button type="button" variant="outline">Cancel</Button> <Button type="submit">Deploy</Button> </CardFooter> </form></Card>
<script> const setupForm = () => { const form = document.getElementById("create-project-form") as HTMLFormElement; if (!form) return;
form.addEventListener("submit", (e) => { e.preventDefault();
console.log( "Form submission data:", Object.fromEntries(new FormData(form).entries()), ); }); };
setupForm();
document.addEventListener("astro:after-swap", setupForm);</script>import type { FormEvent } from "react";import { Button } from "@/components/starwind/button";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue,} from "@/components/starwind/select";
export function Example() { function handleSubmit(event: FormEvent<HTMLFormElement>) { event.preventDefault(); console.log( "Form submission data:", Object.fromEntries(new FormData(event.currentTarget).entries()), ); }
return ( <Card className="w-[360px]"> <CardHeader> <CardTitle>Create project</CardTitle> <CardDescription>Deploy your new project in one-click.</CardDescription> </CardHeader> <form onSubmit={handleSubmit}> <CardContent className="flex flex-col gap-4"> <div className="flex w-full flex-col gap-2"> <Label htmlFor="create-project-name">Name</Label> <Input type="text" id="create-project-name" name="name" placeholder="Name of your project" required /> </div> <div className="flex w-full flex-col gap-2"> <Label htmlFor="create-project-framework">Framework</Label> <Select name="framework" required> <SelectTrigger id="create-project-framework" className="w-full"> <SelectValue placeholder="Select" /> </SelectTrigger> <SelectContent> <SelectGroup> <SelectLabel>Frameworks</SelectLabel> <SelectItem value="astro">Astro</SelectItem> <SelectItem value="next">Next.js</SelectItem> <SelectItem value="svelte">SvelteKit</SelectItem> <SelectItem value="solid">SolidStart</SelectItem> </SelectGroup> </SelectContent> </Select> </div> </CardContent> <CardFooter className="mt-6 flex justify-between"> <Button type="button" variant="outline">Cancel</Button> <Button type="submit">Deploy</Button> </CardFooter> </form> </Card> );}Size
Card supports sm and md sizes. It defaults to md when size is omitted; use size="sm" for tighter spacing.
---import { Button } from "@/components/starwind/button";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";---
<Card size="sm" class="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 "sm" for a more compact appearance. </p> </CardContent> <CardFooter> <Button variant="outline" size="sm" class="w-full">Action</Button> </CardFooter></Card>import { Button } from "@/components/starwind/button";import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";
export function Example() { 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 "sm" for a more compact appearance. </p> </CardContent> <CardFooter> <Button variant="outline" size="sm" className="w-full">Action</Button> </CardFooter> </Card> </> );}Spacing
Use the --card-spacing CSS variable to control both the spacing between sections and the inset of Card parts.
---import { Button } from "@/components/starwind/button";import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { ToggleGroup, ToggleGroupItem } from "@/components/starwind/toggle-group";
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" },] as const;---
<div data-card-spacing-demo class="mx-auto grid w-full max-w-sm gap-4"> <ToggleGroup data-card-spacing-controls defaultValue={["5"]} variant="outline" size="sm" spacing={2} class="mx-auto" aria-label="Card spacing" > {spacingOptions.map((option) => ( <ToggleGroupItem value={option.value}>{option.label}</ToggleGroupItem> ))} </ToggleGroup>
<Card data-card-spacing-preview class="[--card-spacing:--spacing(5)]"> <CardHeader> <CardTitle>Login to your account</CardTitle> <CardDescription>Enter your email below to login to your account.</CardDescription> <CardAction> <Button variant="ghost" size="sm">Sign Up</Button> </CardAction> </CardHeader> <CardContent> <form class="flex flex-col gap-6"> <div class="grid gap-2"> <Label for="card-spacing-email">Email</Label> <Input id="card-spacing-email" type="email" placeholder="m@example.com" required /> </div> <div class="grid gap-2"> <div class="flex items-center"> <Label for="card-spacing-password">Password</Label> <a href="#" class="ml-auto text-sm underline-offset-4 hover:underline"> Forgot your password? </a> </div> <Input id="card-spacing-password" type="password" required /> </div> </form> </CardContent> <CardFooter class="flex-col gap-2"> <Button type="button" class="w-full">Login</Button> <Button type="button" variant="outline" class="w-full">Login with Google</Button> </CardFooter> </Card></div>
<script> const spacingClasses = [ "[--card-spacing:--spacing(4)]", "[--card-spacing:--spacing(5)]", "[--card-spacing:--spacing(6)]", "[--card-spacing:--spacing(8)]", ];
const setupCardSpacingDemos = () => { document.querySelectorAll<HTMLElement>("[data-card-spacing-demo]").forEach((demo) => { if (demo.dataset.cardSpacingReady === "true") return;
const controls = demo.querySelector<HTMLElement>("[data-card-spacing-controls]"); const card = demo.querySelector<HTMLElement>("[data-card-spacing-preview]"); if (!controls || !card) return;
controls.addEventListener("starwind:value-change", (event) => { const [value] = (event as CustomEvent<{ value: string[] }>).detail.value; const nextClass = spacingClasses.find((className) => className.includes(`spacing(${value})`), ); if (!nextClass) return;
card.classList.remove(...spacingClasses); card.classList.add(nextClass); });
demo.dataset.cardSpacingReady = "true"; }); };
setupCardSpacingDemos(); document.addEventListener("astro:after-swap", setupCardSpacingDemos);</script>import { useState } from "react";import { Button } from "@/components/starwind/button";import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";import { Input } from "@/components/starwind/input";import { Label } from "@/components/starwind/label";import { ToggleGroup, ToggleGroupItem } from "@/components/starwind/toggle-group";
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" },] as const;
export function Example() { const [spacing, setSpacing] = useState("5"); const cardClassName = spacingOptions.find((option) => option.value === spacing)?.className;
return ( <div className="mx-auto grid w-full max-w-sm gap-4"> <ToggleGroup value={[spacing]} onValueChange={(values) => setSpacing(values[0] ?? spacing)} variant="outline" size="sm" spacing={2} className="mx-auto" aria-label="Card spacing" > {spacingOptions.map((option) => ( <ToggleGroupItem key={option.value} value={option.value}> {option.label} </ToggleGroupItem> ))} </ToggleGroup>
<Card className={cardClassName}> <CardHeader> <CardTitle>Login to your account</CardTitle> <CardDescription>Enter your email below to login to your account.</CardDescription> <CardAction> <Button variant="ghost" size="sm">Sign Up</Button> </CardAction> </CardHeader> <CardContent> <form className="flex flex-col gap-6"> <div className="grid gap-2"> <Label htmlFor="card-spacing-email">Email</Label> <Input id="card-spacing-email" type="email" placeholder="m@example.com" required /> </div> <div className="grid gap-2"> <div className="flex items-center"> <Label htmlFor="card-spacing-password">Password</Label> <a href="#" className="ml-auto text-sm underline-offset-4 hover:underline"> Forgot your password? </a> </div> <Input id="card-spacing-password" type="password" required /> </div> </form> </CardContent> <CardFooter className="flex-col gap-2"> <Button type="button" className="w-full">Login</Button> <Button type="button" variant="outline" className="w-full">Login with Google</Button> </CardFooter> </Card> </div> );}Image
Add an image before the card header to create a card with an image. Use the CardAction component to position a badge or other action in the header.
---import { Button } from "@/components/starwind/button";import { Badge } from "@/components/starwind/badge";import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";---
<Card class="relative mx-auto w-full max-w-sm"> <img src="https://images.unsplash.com/photo-1540575467063-178a50c2df87?w=600&h=400&fit=crop" alt="Event cover" class="aspect-video w-full object-cover" /> <CardHeader> <CardAction> <Badge size="sm" variant="primary">Featured</Badge> </CardAction> <CardTitle>Design systems meetup</CardTitle> <CardDescription> A practical talk on component APIs, accessibility, and shipping faster. </CardDescription> </CardHeader> <CardFooter> <Button class="w-full">View Event</Button> </CardFooter></Card>import { Button } from "@/components/starwind/button";import { Badge } from "@/components/starwind/badge";import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/starwind/card";
export function Example() { return ( <> <Card className="relative mx-auto w-full max-w-sm"> <img src="https://images.unsplash.com/photo-1540575467063-178a50c2df87?w=600&h=400&fit=crop" alt="Event cover" className="aspect-video w-full object-cover" /> <CardHeader> <CardAction> <Badge size="sm" variant="primary">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> </> );}API Reference
Styled Component API
These props are added or materially changed by the installed styled component. Standard HTML attributes remain available through the inherited interfaces noted below. Expand a prop to see named type definitions and framework-specific imports. Follow the Primitive and Runtime links for lower-level behavior props.
Card
Inherits div attributes.
Contains the following additional props:
size "sm" | "md" "md"
- Description
- Selects the component's visual size.
- Classification
- Styled variant
Changelog
View version history v2.3.1 9 releases
v2.3.1
- Named the generated aggregate default export so React and Astro tooling can identify the installed component cleanly.
v2.3.0
- Renamed the standard Card size from
defaulttomd. Omittingsizekeeps the same medium spacing.
v2.2.0
- Added the overridable
--card-spacingCSS variable and default and small presets for coordinating section gaps and part insets consistently in Astro and React.
v2.1.0
- Added contract-generated Astro and React implementations while preserving the component’s existing public API and styling.
v2.0.2
- Refactor tailwind variants functions into separate
variants.tsfile
v2.0.0
- Added
sizeprop to Card component ("default"|"sm") - Added
CardActioncomponent for positioning actions in card headers - Added support for images within cards with automatic rounded corners
- Update styling to more closely match the shadcn/ui card component
v1.3.0
- style updates
v1.2.0
- Add a
data-slotattribute to all components to enable global styling updates
v1.1.0
tailwind-variantsnow implemented. This usestailwind-mergeunder the hood to merge Tailwind classes without style conflicts, allowing you to override any existing classes using the “class” prop.