Card layout primitives
Login to your account
Enter your email below to login to your account
import { Button } from '@/components/ui/button'
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
...import { Button } from '@/components/ui/button'
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
export default function CardDemoExample() {
return (
<Card class="w-full max-w-sm">
<CardHeader><CardTitle>Login to your account</CardTitle><CardDescription>Enter your email below to login to your account</CardDescription><CardAction><Button variant="link">Sign Up</Button></CardAction></CardHeader>
<CardContent><form><div class="flex flex-col gap-6"><div class="grid gap-2"><Label for="email">Email</Label><Input id="email" type="email" placeholder="m@example.com" required /></div><div class="grid gap-2"><div class="flex items-center"><Label for="password">Password</Label><a href="#" class="ml-auto text-sm underline-offset-4 hover:underline">Forgot your password?</a></div><Input id="password" type="password" required /></div></div></form></CardContent>
<CardFooter class="flex-col gap-2"><Button type="submit" class="w-full">Login</Button><Button variant="outline" class="w-full">Login with Google</Button></CardFooter>
</Card>
)
}
Installation
npx @fictjs/shadcn@latest add cardCopy the Fict shadcn component into your project.
Source
components/ui/card.tsx
src/components/ui/card.tsx
import { cn } from '@/lib/cn'
type DivProps = JSX.IntrinsicElements['div']
type HeadingProps = JSX.IntrinsicElements['h3']
type ParagraphProps = JSX.IntrinsicElements['p']
type CardProps = DivProps & { size?: 'default' | 'sm' }
export function Card(props: CardProps) {
const { class: className, size = 'default', ...rest } = props
return <div data-slot='card' data-size={size} class={cn('group/card flex flex-col rounded-xl border bg-card text-card-foreground shadow', size === 'sm' && 'gap-3 py-4', className)} {...rest} />
}
export function CardHeader(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='card-header' class={cn('grid auto-rows-min items-start gap-1.5 px-6 pt-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] group-data-[size=sm]/card:px-4 group-data-[size=sm]/card:pt-0', className)} {...rest} />
}
export function CardTitle(props: HeadingProps) {
const { class: className, ...rest } = props
return <h3 data-slot='card-title' class={cn('font-semibold leading-none tracking-tight', className)} {...rest} />
}
export function CardDescription(props: ParagraphProps) {
const { class: className, ...rest } = props
return <p data-slot='card-description' class={cn('text-sm text-muted-foreground', className)} {...rest} />
}
export function CardAction(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='card-action' class={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)} {...rest} />
}
export function CardContent(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='card-content' class={cn('px-6 group-data-[size=sm]/card:px-4', className)} {...rest} />
}
export function CardFooter(props: DivProps) {
const { class: className, ...rest } = props
return <div data-slot='card-footer' class={cn('flex items-center px-6 pb-6 group-data-[size=sm]/card:border-t group-data-[size=sm]/card:px-4 group-data-[size=sm]/card:pt-4 group-data-[size=sm]/card:pb-0', className)} {...rest} />
}
Usage
Examples
Small
Small Card
This card uses the small size variant.
The card component supports a size prop that can be set to "sm" for a more compact appearance.
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
...import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
export default function CardSmallExample() {
return (
<Card size="sm" class="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>
)
}
Image
Featured
Design systems meetup
A practical talk on component APIs, accessibility, and shipping faster.
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
...import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
export default function CardImageExample() {
return (
<Card class="relative w-full max-w-sm overflow-hidden pt-0">
<div class="absolute inset-0 z-30 aspect-video bg-black/35" aria-hidden="true" />
<img src="https://avatar.vercel.sh/shadcn1" alt="Event cover" class="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40" />
<CardHeader><CardAction><Badge variant="secondary">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>
)
}
Rtl
تسجيل الدخول إلى حسابك
أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك
import { Button } from '@/components/ui/button'
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
...import { Button } from '@/components/ui/button'
import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
const translations = {
ar: { title: 'تسجيل الدخول إلى حسابك', description: 'أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك', signUp: 'إنشاء حساب', email: 'البريد الإلكتروني', password: 'كلمة المرور', forgot: 'نسيت كلمة المرور؟', login: 'تسجيل الدخول', google: 'تسجيل الدخول باستخدام Google' },
he: { title: 'התחבר לחשבון שלך', description: 'הזן את האימייל שלך למטה כדי להתחבר לחשבון שלך', signUp: 'הירשם', email: 'אימייל', password: 'סיסמה', forgot: 'שכחת את הסיסמה?', login: 'התחבר', google: 'התחבר עם Google' },
en: { title: 'Login to your account', description: 'Enter your email below to login to your account', signUp: 'Sign Up', email: 'Email', password: 'Password', forgot: 'Forgot your password?', login: 'Login', google: 'Login with Google' },
} as const
export default function CardRtlExample() {
let language = $state<keyof typeof translations>('ar')
const direction = () => language === 'en' ? 'ltr' : 'rtl'
const text = () => translations[language]
return (
<div class="grid gap-4"><select value={language} onChange={event => { language = event.currentTarget.value as keyof typeof translations }}><option value="ar">Arabic (العربية)</option><option value="he">Hebrew (עברית)</option><option value="en">English</option></select>
<Card class="w-full max-w-sm" dir={direction()}><CardHeader><CardTitle>{text().title}</CardTitle><CardDescription>{text().description}</CardDescription><CardAction><Button variant="link">{text().signUp}</Button></CardAction></CardHeader><CardContent><form><div class="flex flex-col gap-6"><div class="grid gap-2"><Label for="email-rtl">{text().email}</Label><Input id="email-rtl" type="email" placeholder="m@example.com" required /></div><div class="grid gap-2"><div class="flex items-center"><Label for="password-rtl">{text().password}</Label><a href="#" class="ms-auto text-sm underline-offset-4 hover:underline">{text().forgot}</a></div><Input id="password-rtl" type="password" required /></div></div></form></CardContent><CardFooter class="flex-col gap-2"><Button type="submit" class="w-full">{text().login}</Button><Button variant="outline" class="w-full">{text().google}</Button></CardFooter></Card>
</div>
)
}