package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/card" "github.com/axadrn/shadcn-templ/v2/components/input" "github.com/axadrn/shadcn-templ/v2/components/label") templ CardDemo() { @card.Card(card.Props{Class: "w-full max-w-sm"}) { @card.Header() { @card.Title() { Login to your account } @card.Description() { Enter your email below to login to your account } @card.Action() { @button.Button(button.Props{Variant: button.VariantLink}) { Sign Up } } } @card.Content() { <form> <div class="flex flex-col gap-6"> <div class="grid gap-2"> @label.Label(label.Props{For: "email"}) { Email } @input.Input(input.Props{ ID: "email", Type: "email", Placeholder: "[email protected]", Attributes: templ.Attributes{"required": true}, }) </div> <div class="grid gap-2"> <div class="flex items-center"> @label.Label(label.Props{For: "password"}) { Password } <a href="#" class="ml-auto inline-block text-sm underline-offset-4 hover:underline"> Forgot your password? </a> </div> @input.Input(input.Props{ ID: "password", Type: "password", Attributes: templ.Attributes{"required": true}, }) </div> </div> </form> } @card.Footer(card.FooterProps{Class: "flex-col gap-2"}) { @button.Button(button.Props{Type: button.TypeSubmit, Class: "w-full"}) { Login } @button.Button(button.Props{Variant: button.VariantOutline, Class: "w-full"}) { Login with Google } } }}Installation
Usage
Composition
Use the following composition to build a Card:
Size
Use the Size: card.SizeSm prop to set the size of the card to small. The small size variant uses smaller spacing.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/card" "github.com/axadrn/shadcn-templ/v2/components/icon") templ CardSmall() { @card.Card(card.Props{Size: card.SizeSm, Class: "mx-auto w-full max-w-xs"}) { @card.Header() { @card.Title() { Scheduled reports } @card.Description() { Weekly snapshots. No more manual exports. } } @card.Content() { <ul class="grid gap-2 py-2 text-sm"> <li class="flex gap-2"> @icon.ChevronRight(icon.Props{Class: "mt-0.5 size-4 shrink-0 text-muted-foreground"}) <span>Choose a schedule (daily, or weekly).</span> </li> <li class="flex gap-2"> @icon.ChevronRight(icon.Props{Class: "mt-0.5 size-4 shrink-0 text-muted-foreground"}) <span>Send to channels or specific teammates.</span> </li> <li class="flex gap-2"> @icon.ChevronRight(icon.Props{Class: "mt-0.5 size-4 shrink-0 text-muted-foreground"}) <span>Include charts, tables, and key metrics.</span> </li> </ul> } @card.Footer(card.FooterProps{Class: "flex-col gap-2"}) { @button.Button(button.Props{Size: button.SizeSm, Class: "w-full"}) { Set up scheduled reports } @button.Button(button.Props{Variant: button.VariantOutline, Size: button.SizeSm, Class: "w-full"}) { See what's new } } }}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.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/card" "github.com/axadrn/shadcn-templ/v2/components/input" "github.com/axadrn/shadcn-templ/v2/components/label" "github.com/axadrn/shadcn-templ/v2/components/togglegroup") type spacingOption struct { Class string Label string Value string} var spacingOptions = []spacingOption{ {"[--card-spacing:--spacing(4)]", "16px", "4"}, {"[--card-spacing:--spacing(5)]", "20px", "5"}, {"[--card-spacing:--spacing(6)]", "24px", "6"}, {"[--card-spacing:--spacing(8)]", "32px", "8"},} templ CardSpacing() { <div class="mx-auto grid w-full max-w-sm gap-4" data-card-spacing-demo> @togglegroup.ToggleGroup(togglegroup.Props{ Variant: togglegroup.VariantOutline, Size: togglegroup.SizeSm, Class: "justify-center", }) { for i, option := range spacingOptions { @togglegroup.Item(togglegroup.ItemProps{ Value: option.Value, Pressed: i == 0, Attributes: templ.Attributes{"data-spacing-class": option.Class}, }) { { option.Label } } } } @card.Card(card.Props{Class: spacingOptions[0].Class}) { @card.Header() { @card.Title() { Login to your account } @card.Description() { Enter your email below to login to your account } @card.Action() { @button.Button(button.Props{Variant: button.VariantLink}) { Sign Up } } } @card.Content() { <form> <div class="flex flex-col gap-6"> <div class="grid gap-2"> @label.Label(label.Props{For: "email-spacing"}) { Email } @input.Input(input.Props{ ID: "email-spacing", Type: "email", Placeholder: "[email protected]", Attributes: templ.Attributes{"required": true}, }) </div> <div class="grid gap-2"> <div class="flex items-center"> @label.Label(label.Props{For: "password-spacing"}) { Password } <a href="#" class="ml-auto inline-block text-sm underline-offset-4 hover:underline"> Forgot your password? </a> </div> @input.Input(input.Props{ ID: "password-spacing", Type: "password", Attributes: templ.Attributes{"required": true}, }) </div> </div> </form> } @card.Footer(card.FooterProps{Class: "flex-col gap-2"}) { @button.Button(button.Props{Type: button.TypeSubmit, Class: "w-full"}) { Login } @button.Button(button.Props{Variant: button.VariantOutline, Class: "w-full"}) { Login with Google } } } </div> <script nonce={ templ.GetNonce(ctx) }> // The vanilla pendant of the demo's React state, the pressed toggle // swaps the spacing class on the card. document.addEventListener('toggle-change', (e) => { const demo = e.target.closest('[data-card-spacing-demo]'); if (!demo || !e.detail.pressed) return; const cardEl = demo.querySelector('[data-slot="card"]'); demo.querySelectorAll('[data-spacing-class]').forEach((t) => { cardEl.classList.remove(t.getAttribute('data-spacing-class')); }); cardEl.classList.add(e.target.getAttribute('data-spacing-class')); }); </script>}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 card.Content to remove the section gap.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/card") templ CardEdgeToEdge() { @card.Card(card.Props{Class: "mx-auto w-full max-w-sm"}) { @card.Header() { @card.Title() { Terms of Service } @card.Description() { Review the terms before accepting the agreement. } } @card.Content(card.ContentProps{Class: "-mb-(--card-spacing)"}) { <div class="-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's acceptable use policies. </p> </div> } @card.Footer(card.FooterProps{Class: "justify-end gap-2"}) { @button.Button(button.Props{Variant: button.VariantOutline}) { Decline } @button.Button() { Accept } } }}Image
Add an image before the card header to create a card with an image.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/badge" "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/card") templ CardImage() { @card.Card(card.Props{Class: "relative mx-auto w-full max-w-sm pt-0"}) { <div class="absolute inset-0 z-30 aspect-video bg-black/35"></div> <img src="/assets/img/aspect_ratio_placeholder.jpeg" alt="Event cover" class="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40" /> @card.Header() { @card.Action() { @badge.Badge(badge.Props{Variant: badge.VariantSecondary}) { Featured } } @card.Title() { Design systems meetup } @card.Description() { A practical talk on component APIs, accessibility, and shipping faster. } } @card.Footer() { @button.Button(button.Props{Class: "w-full"}) { View Event } } }}API Reference
Card
The Card component is the root container for card content.
Header
The card.Header component is used for a title, description, and optional action.
Title
The card.Title component is used for the card title.
Description
The card.Description component is used for helper text under the title.
Action
The card.Action component places content in the top-right of the header (for example, a button or a badge).
Content
The card.Content component is used for the main card body.
Footer
The card.Footer component is used for actions and secondary content at the bottom of the card.
