Card
Displays a card with header, content, and footer.
Loading...
<twig:Card class="w-full max-w-sm">
<twig:Card:Header>
<twig:Card:Title>Login to your account</twig:Card:Title>
<twig:Card:Description>
Enter your email below to login to your account
</twig:Card:Description>
<twig:Card:Action>
<twig:Button variant="link">Sign Up</twig:Button>
</twig:Card:Action>
</twig:Card:Header>
<twig:Card:Content>
<form>
<div class="flex flex-col gap-6">
<div class="grid gap-2">
<twig:Label for="email">Email</twig:Label>
<twig:Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div class="grid gap-2">
<div class="flex items-center">
<twig:Label for="password">Password</twig:Label>
<a
href="#"
class="ml-auto inline-block text-sm underline-offset-4 hover:underline"
>
Forgot your password?
</a>
</div>
<twig:Input id="password" type="password" required />
</div>
</div>
</form>
</twig:Card:Content>
<twig:Card:Footer class="flex-col gap-2">
<twig:Button type="submit" class="w-full">
Login
</twig:Button>
<twig:Button variant="outline" class="w-full">
Login with Google
</twig:Button>
</twig:Card:Footer>
</twig:Card>
Installation
bin/console ux:install card --kit shadcn
That's it!
Install the following Composer dependencies:
composer require tales-from-a-dev/twig-tailwind-extra:^1.0.0
Copy the following file(s) into your Symfony app:
templates/components/Card.html.twig
{# @prop size 'default'|'sm' Size variant of the card. Defaults to `'default'` #}
{# @block content The card content, typically includes `Card:Header`, `Card:Content`, and/or `Card:Footer` #}
{%- props size = 'default' -%}
<div
class="{{ ('group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({
'data-slot': 'card',
'data-size': size,
}) }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/Card/Action.html.twig
{# @block content The action area placed in the top-right corner of the card header #}
<div
class="{{ ('col-start-2 row-span-2 row-start-1 self-start justify-self-end ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({'data-slot': 'card-action'}) }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/Card/Content.html.twig
{# @block content The main content area of the card #}
<div
class="{{ ('px-4 group-data-[size=sm]/card:px-3 ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({'data-slot': 'card-content'}) }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/Card/Description.html.twig
{# @block content The descriptive text of the card #}
<div
class="{{ ('text-sm text-muted-foreground ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({'data-slot': 'card-description'}) }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/Card/Footer.html.twig
{# @block content The footer area, typically contains actions or additional information #}
<div
class="{{ ('flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3 ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({'data-slot': 'card-footer'}) }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/Card/Header.html.twig
{# @block content The header area, typically contains `Card:Title`, `Card:Description`, and `Card:Action` #}
<div
class="{{ ('group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({'data-slot': 'card-header'}) }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/Card/Title.html.twig
{# @block content The title text of the card #}
<div
class="{{ ('cn-font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({'data-slot': 'card-title'}) }}
>
{%- block content %}{% endblock -%}
</div>
Happy coding!
Usage
<twig:Card>
<twig:Card:Header>
<twig:Card:Title>Card Title</twig:Card:Title>
<twig:Card:Description>Card Description</twig:Card:Description>
<twig:Card:Action>Card Action</twig:Card:Action>
</twig:Card:Header>
<twig:Card:Content>
<p>Card Content</p>
</twig:Card:Content>
<twig:Card:Footer>
<p>Card Footer</p>
</twig:Card:Footer>
</twig:Card>
Examples
Size
Use the size="sm" prop to set the size of the card to small. The small size variant uses smaller spacing.
Loading...
<twig:Card size="sm" class="mx-auto w-full max-w-sm">
<twig:Card:Header>
<twig:Card:Title>Small Card</twig:Card:Title>
<twig:Card:Description>
This card uses the small size variant.
</twig:Card:Description>
</twig:Card:Header>
<twig:Card:Content>
<p>
The card component supports a size prop that can be set to
"sm" for a more compact appearance.
</p>
</twig:Card:Content>
<twig:Card:Footer>
<twig:Button variant="outline" size="sm" class="w-full">
Action
</twig:Button>
</twig:Card:Footer>
</twig:Card>
Image
Add an image before the card header to create a card with an image.
Loading...
<twig:Card 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="https://avatar.vercel.sh/shadcn1"
alt="Event cover"
class="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40"
/>
<twig:Card:Header>
<twig:Card:Action>
<twig:Badge variant="secondary">Featured</twig:Badge>
</twig:Card:Action>
<twig:Card:Title>Design systems meetup</twig:Card:Title>
<twig:Card:Description>
A practical talk on component APIs, accessibility, and shipping faster.
</twig:Card:Description>
</twig:Card:Header>
<twig:Card:Footer>
<twig:Button class="w-full">View Event</twig:Button>
</twig:Card:Footer>
</twig:Card>
RTL
To enable RTL support, set the dir="rtl" attribute on the root element.
Loading...
<div class="flex w-full flex-col items-center gap-8">
{# Arabic #}
<twig:Card class="w-full max-w-sm" dir="rtl">
<twig:Card:Header>
<twig:Card:Title>تسجيل الدخول إلى حسابك</twig:Card:Title>
<twig:Card:Description>
أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك
</twig:Card:Description>
<twig:Card:Action>
<twig:Button variant="link">إنشاء حساب</twig:Button>
</twig:Card:Action>
</twig:Card:Header>
<twig:Card:Content>
<form>
<div class="flex flex-col gap-6">
<div class="grid gap-2">
<twig:Label for="email-ar">البريد الإلكتروني</twig:Label>
<twig:Input id="email-ar" type="email" placeholder="m@example.com" required />
</div>
<div class="grid gap-2">
<div class="flex items-center">
<twig:Label for="password-ar">كلمة المرور</twig:Label>
<a href="#" class="ms-auto inline-block text-sm underline-offset-4 hover:underline">
نسيت كلمة المرور؟
</a>
</div>
<twig:Input id="password-ar" type="password" required />
</div>
</div>
</form>
</twig:Card:Content>
<twig:Card:Footer class="flex-col gap-2">
<twig:Button type="submit" class="w-full">تسجيل الدخول</twig:Button>
<twig:Button variant="outline" class="w-full">تسجيل الدخول باستخدام Google</twig:Button>
</twig:Card:Footer>
</twig:Card>
{# Hebrew #}
<twig:Card class="w-full max-w-sm" dir="rtl">
<twig:Card:Header>
<twig:Card:Title>התחברות לחשבון שלך</twig:Card:Title>
<twig:Card:Description>
הזן את כתובת האימייל שלך למטה כדי להתחבר לחשבון שלך
</twig:Card:Description>
<twig:Card:Action>
<twig:Button variant="link">הרשמה</twig:Button>
</twig:Card:Action>
</twig:Card:Header>
<twig:Card:Content>
<form>
<div class="flex flex-col gap-6">
<div class="grid gap-2">
<twig:Label for="email-he">כתובת אימייל</twig:Label>
<twig:Input id="email-he" type="email" placeholder="m@example.com" required />
</div>
<div class="grid gap-2">
<div class="flex items-center">
<twig:Label for="password-he">סיסמה</twig:Label>
<a href="#" class="ms-auto inline-block text-sm underline-offset-4 hover:underline">
שכחת את הסיסמה?
</a>
</div>
<twig:Input id="password-he" type="password" required />
</div>
</div>
</form>
</twig:Card:Content>
<twig:Card:Footer class="flex-col gap-2">
<twig:Button type="submit" class="w-full">התחברות</twig:Button>
<twig:Button variant="outline" class="w-full">התחברות עם Google</twig:Button>
</twig:Card:Footer>
</twig:Card>
</div>
API Reference
Component Card
| Prop | Type | Description |
|---|---|---|
size |
'default'|'sm' |
Size variant of the card. Defaults to 'default' |
| Block | Description |
|---|---|
content |
The card content, typically includes Card:Header, Card:Content, and/or Card:Footer |
Component Card:Action
| Block | Description |
|---|---|
content |
The action area placed in the top-right corner of the card header |
Component Card:Content
| Block | Description |
|---|---|
content |
The main content area of the card |
Component Card:Description
| Block | Description |
|---|---|
content |
The descriptive text of the card |
Component Card:Footer
| Block | Description |
|---|---|
content |
The footer area, typically contains actions or additional information |
Component Card:Header
| Block | Description |
|---|---|
content |
The header area, typically contains Card:Title, Card:Description, and Card:Action |
Component Card:Title
| Block | Description |
|---|---|
content |
The title text of the card |