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 &#039;default&#039;
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