mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2026-05-14 02:20:40 +00:00
12 KiB
12 KiB
Card Component API
A versatile card component for organizing and displaying content with optional header, title, description, content, and footer sections.
Installation
# Cargo.toml
[dependencies]
shadcn-ui-leptos-card = "0.7"
use shadcn_ui_leptos_card::Card;
Import
// Default theme
use shadcn_ui_leptos_card::{
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter,
CardVariant,
InteractiveCard
};
// New York theme
use shadcn_ui_leptos_card::{
Card as CardNewYork,
CardHeader as CardHeaderNewYork,
// ... other components
};
// Signal-managed variant
use shadcn_ui_leptos_card::{
SignalManagedCard,
SignalManagedCardState
};
Component API
Card
The main card container component.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
MaybeProp<String> |
None |
Additional CSS classes |
id |
MaybeProp<String> |
None |
Unique identifier |
style |
Signal<Style> |
None |
Inline styles |
variant |
MaybeProp<CardVariant> |
Default |
Visual style variant |
interactive |
Signal<bool> |
false |
Enable hover/click effects |
children |
Option<Children> |
None |
Card content |
CardHeader
Header section containing title and description.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
MaybeProp<String> |
None |
Additional CSS classes |
id |
MaybeProp<String> |
None |
Unique identifier |
style |
Signal<Style> |
None |
Inline styles |
children |
Option<Children> |
None |
Header content |
CardTitle
Title text within the card header.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
MaybeProp<String> |
None |
Additional CSS classes |
id |
MaybeProp<String> |
None |
Unique identifier |
style |
Signal<Style> |
None |
Inline styles |
level |
MaybeProp<u8> |
2 |
Heading level (h1-h6) |
children |
Option<Children> |
None |
Title text |
CardDescription
Description text within the card header.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
MaybeProp<String> |
None |
Additional CSS classes |
id |
MaybeProp<String> |
None |
Unique identifier |
style |
Signal<Style> |
None |
Inline styles |
children |
Option<Children> |
None |
Description text |
CardContent
Main content area of the card.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
MaybeProp<String> |
None |
Additional CSS classes |
id |
MaybeProp<String> |
None |
Unique identifier |
style |
Signal<Style> |
None |
Inline styles |
children |
Option<Children> |
None |
Content |
CardFooter
Footer section for actions or metadata.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
MaybeProp<String> |
None |
Additional CSS classes |
id |
MaybeProp<String> |
None |
Unique identifier |
style |
Signal<Style> |
None |
Inline styles |
children |
Option<Children> |
None |
Footer content |
CardVariant
pub enum CardVariant {
Default, // Standard card styling
Destructive, // Red border/background for errors
Warning, // Yellow border/background for warnings
Success, // Green border/background for success
}
Usage Examples
Basic Card
use leptos::prelude::*;
use shadcn_ui_leptos_card::*;
#[component]
pub fn MyComponent() -> impl IntoView {
view! {
<Card>
<CardContent>
<p>"Basic card content"</p>
</CardContent>
</Card>
}
}
Complete Card Structure
view! {
<Card>
<CardHeader>
<CardTitle>"Card Title"</CardTitle>
<CardDescription>
"A brief description of the card's content"
</CardDescription>
</CardHeader>
<CardContent>
<p>"Main content goes here."</p>
</CardContent>
<CardFooter>
<button>"Action"</button>
</CardFooter>
</Card>
}
Content Only Card
view! {
<Card>
<CardContent class="p-6">
<h3 class="text-lg font-semibold">"Simple Card"</h3>
<p class="mt-2">"Just content, no header or footer."</p>
</CardContent>
</Card>
}
Interactive Card
#[component]
pub fn ClickableCard() -> impl IntoView {
let (clicked, set_clicked) = signal(false);
view! {
<Card
interactive=Signal::derive(|| true)
on:click=move |_| set_clicked.update(|n| *n = !n)
class="cursor-pointer"
>
<CardHeader>
<CardTitle>"Click Me!"</CardTitle>
</CardHeader>
<CardContent>
<p>{move || if clicked.get() { "Clicked!" } else { "Not clicked" }}</p>
</CardContent>
</Card>
}
}
Variant Cards
view! {
<div class="grid gap-4">
<Card variant=CardVariant::Default>
<CardHeader>
<CardTitle>"Default"</CardTitle>
</CardHeader>
</Card>
<Card variant=CardVariant::Success>
<CardHeader>
<CardTitle>"Success"</CardTitle>
<CardDescription>"Operation completed"</CardDescription>
</CardHeader>
</Card>
<Card variant=CardVariant::Warning>
<CardHeader>
<CardTitle>"Warning"</CardTitle>
<CardDescription>"Please review this"</CardDescription>
</CardHeader>
</Card>
<Card variant=CardVariant::Destructive>
<CardHeader>
<CardTitle>"Error"</CardTitle>
<CardDescription>"Something went wrong"</CardDescription>
</CardHeader>
</Card>
</div>
}
Profile Card
#[component]
pub fn ProfileCard() -> impl IntoView {
view! {
<Card class="max-w-sm">
<CardHeader>
<div class="flex items-center space-x-4">
<div class="h-12 w-12 rounded-full bg-primary" />
<div>
<CardTitle class="text-lg">"John Doe"</CardTitle>
<CardDescription>"Software Engineer"</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
<p class="text-sm">
"Passionate about building great user experiences \
and writing clean, maintainable code."
</p>
</CardContent>
<CardFooter>
<div class="flex space-x-2">
<button class="text-sm">"View Profile"</button>
<button class="text-sm">"Contact"</button>
</div>
</CardFooter>
</Card>
}
}
Stats Card
#[component]
pub fn StatsCard() -> impl IntoView {
view! {
<Card>
<CardHeader>
<CardDescription>"Total Revenue"</CardDescription>
<CardTitle class="text-3xl">"$45,231.89"</CardTitle>
</CardHeader>
<CardContent>
<div class="text-xs text-muted-foreground">
"+20.1% from last month"
</div>
</CardContent>
</Card>
}
}
Custom Styled Card
view! {
<Card
class="bg-gradient-to-br from-purple-500 to-pink-500 text-white border-0"
>
<CardContent class="p-8">
<h2 class="text-2xl font-bold">"Featured Content"</h2>
<p class="mt-2 opacity-90">
"Cards can be completely customized with your own styles"
</p>
</CardContent>
</Card>
}
Grid of Cards
view! {
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{(0..6).map(|i| {
view! {
<Card>
<CardHeader>
<CardTitle>{format!("Card {}", i + 1)}</CardTitle>
</CardHeader>
<CardContent>
<p>"Content for card"</p>
</CardContent>
</Card>
}
}).collect::<Vec<_>>()}
</div>
}
Accessibility
Semantic Structure
Cards use semantic HTML elements:
<article>for the card container<h2>(default) for the card title- Proper heading hierarchy
Keyboard Navigation
For interactive cards:
| Key | Action |
|---|---|
| Tab | Focus card (when interactive) |
| Enter | Activate card (when interactive) |
| Space | Activate card (when interactive) |
ARIA Attributes
// Good: Descriptive card title
<Card>
<CardHeader>
<CardTitle>"User Profile Settings"</CardTitle>
</CardHeader>
</Card>
// Good: Interactive cards indicate clickability
<Card
interactive=Signal::derive(|| true)
role="button"
aria_label="View details"
>
CSS Classes
Card Classes
.shadcn-card {
rounded-lg border bg-card text-card-foreground shadow-sm
}
Variant Classes
| Variant | CSS Classes |
|---|---|
Default |
Base classes only |
Destructive |
border-destructive bg-destructive/5 |
Warning |
border-warning bg-warning/5 |
Success |
border-success bg-success/5 |
Interactive Classes
.shadcn-card--interactive {
cursor-pointer hover:shadow-md transition-shadow
}
Subcomponent Classes
| Component | Base Classes |
|---|---|
CardHeader |
flex flex-col space-y-1.5 p-6 |
CardTitle |
text-2xl font-semibold leading-none tracking-tight |
CardDescription |
text-sm text-muted-foreground |
CardContent |
p-6 pt-0 |
CardFooter |
flex items-center p-6 pt-0 |
TypeScript API
interface CardProps {
className?: string;
id?: string;
style?: React.CSSProperties;
variant?: 'default' | 'destructive' | 'warning' | 'success';
interactive?: boolean;
children?: React.ReactNode;
}
interface CardHeaderProps {
className?: string;
id?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
}
interface CardTitleProps {
className?: string;
id?: string;
style?: React.CSSProperties;
level?: 1 | 2 | 3 | 4 | 5 | 6;
children?: React.ReactNode;
}
interface CardDescriptionProps {
className?: string;
id?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
}
interface CardContentProps {
className?: string;
id?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
}
interface CardFooterProps {
className?: string;
id?: string;
style?: React.CSSProperties;
children?: React.ReactNode;
}
export const Card: React.FC<CardProps>;
export const CardHeader: React.FC<CardHeaderProps>;
export const CardTitle: React.FC<CardTitleProps>;
export const CardDescription: React.FC<CardDescriptionProps>;
export const CardContent: React.FC<CardContentProps>;
export const CardFooter: React.FC<CardFooterProps>;
Best Practices
- Use consistent hierarchy - Header → Content → Footer
- Provide clear titles - Summarize card content
- Use descriptions sparingly - Add context when needed
- Keep content focused - One topic per card
- Consider mobile layouts - Stack cards on small screens