# 3D Flip card tutorial using Tailwind CSS

> Step-by-step tutorial for building interactive 3D flip cards with Tailwind CSS and Next.js. Complete code examples and live demos included.

- Author: Matthew Rhoads, Developer
- Published: September 2, 2024
- Canonical: https://zephyrpixels.dev/blog/3D-flip-cards-using-tailwind-css

---
## 3D Flip Cards using Tailwind CSS

### What are Flip Cards?

Flip cards are interactive elements that display content on two sides - a front face and back face. When triggered, the card flips to reveal the alternate side, creating an engaging user experience.

### Benefits of Flip Cards

Flip cards offer several advantages for engaging users on your website:

- They provide an interactive element that encourages users to explore and discover more information
- The flipping motion adds a fun, dynamic visual effect that captures attention
- Flip cards are an efficient way to present additional content within a limited space
- They work well for highlighting key features, services, or products

By implementing flip cards on your site, you can create a more immersive and memorable user experience. Visitors are more likely to engage with your content and spend more time exploring what you have to offer.

### **What we are building~**
*[Image: 3D flip cards demonstration showing interactive card animations]*

**Want to see a live demo?** [Demo](https://demo-alpha-peach.vercel.app/)

**Full Code and Repo** [Github](https://github.com/mematthew123/3dFlipCards-demo)

In this guide we'll cover: how to use Tailwind's utility classes to style the flip card, including 3D transforms, perspective, and transitions.

We'll start by setting up a new Next.js component for our flip card. Then we'll apply Tailwind classes to style the card faces, handle the flipping animation, and make the cards responsive.

## **Let's dive in and see how it's done!**

If we haven't already let's go ahead and spin up a new Next.js site site using the command npx create-next-app@latest and selecting Tailwind CSS from the setup instructions.

Next we will install the HeroIcons package

```undefined
 npm i @heroicons/react
```

We will then need to create a new folder called 'components'.

Inside of our new components folder we will create a new file

```undefined
FlipCardComponent.tsx
```

### 1. **Set Up the Section Wrapper**

Begin by setting up a basic section element that will wrap the entire component. This section will add padding around the content to ensure it’s well-spaced on the page.

```
const FlipCardComponent = () => {
  return (
    <section className="py-16 mx-auto sm:py-20">
      {/* Content will go here */}
    </section>
  );
};

export default FlipCardComponent;
```

- **py-16 sm:py-20:** Adds vertical padding (4rem by default and 5rem on small screens and above), ensuring the section is not cramped against other content.
- **mx-auto:** Centers the section horizontally on the page.

### 2. **Add a Container for Centering Content**

Next, add a container inside the section. This container will center the content and adjust its width based on the screen size.

```undefined
const FlipCardComponent = () => {
  return (
    <section className="py-16 mx-auto sm:py-20">
      <div className="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        {/* Content will go here */}
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

- **mx-auto:** Centers the container within the section.
- **flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl:** Creates a responsive container with padding, and adjusts its maximum width on large screens to 7xl (80rem).

### 3. **Introduce a Flexbox Wrapper for Vertical Layout**

Add a Flexbox wrapper to stack the heading and the grid of service cards vertically.

_This wrapper will also control the spacing between these elements._

```undefined
const FlipCardComponent = () => {
  return (
    <section className="py-16 mx-auto sm:py-20">
      <div className="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        <div className="flex justify-center object-center flex-col gap-12 sm:gap-16">
          {/* Heading and grid will go here */}
        </div>
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

  

- **flex justify-center object-center flex-col gap-12 sm:gap-16:** Uses Flexbox to stack child elements vertically with a gap of 3rem (4rem on small screens and above).

### 4. **Add the Heading**

Let’s add the heading inside the Flexbox wrapper. This heading is styled to adjust its size and color based on screen size.

```undefined
const FlipCardComponent = () => {
  return (
    <section className="py-16 mx-auto sm:py-20">
      <div className="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        <div className="flex justify-center object-center flex-col gap-12 sm:gap-16">
          <h2 className="text-4xl font-semibold tracking-tight text-gray-950 sm:text-5xl lg:text-6xl">
            Services
          </h2>
          {/* Grid will go here */}
        </div>
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

  

- **text-4xl sm:text-5xl lg:text-6xl:** Adjusts the font size responsively, from 2.25rem to 3.75rem.
- **font-semibold:** Adds a medium-bold weight to make the heading stand out.
- **tracking-tight:** Slightly reduces letter spacing for a more cohesive look.
- **text-gray-950:** Applies a deep gray color to the text, ensuring high contrast and readability.

### 5. **Create the Grid Layout for Service Cards**

Now, we’ll create a grid layout to hold the service cards.

This grid will have responsive behavior, adjusting from a single column to three columns on larger screens.

```undefined
const FlipCardComponent = () => {
  return (
    <section className="py-16 mx-auto sm:py-20">
      <div className="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        <div className="flex justify-center object-center flex-col gap-12 sm:gap-16">
          <h2 className="text-4xl font-semibold tracking-tight text-gray-950 sm:text-5xl lg:text-6xl">
            Services
          </h2>
          <div className="mx-auto grid gap-12 space-y-10 md:space-y-0 sm:gap-16 lg:grid-cols-3">
            {/* Service cards will go here */}
          </div>
        </div>
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

- **grid gap-12 space-y-10 md:space-y-0 sm:gap-16 lg:grid-cols-3:** Sets up a responsive grid. The gap-12 and sm:gap-16 add spacing between grid items. space-y-10 removes space between rows on larger screens (md:space-y-0). On large screens (lg:), the grid switches to three columns.

### 6. **Build the Flip Card Container**

We’ll start building the individual service cards, with a container that handles the 3D flip effect.

```undefined
const FlipCardComponent = () => {
  const services = [
    {
      step: "01",
      name: "Diagnostics",
      imageUrl: "https://images.unsplash.com/photo-1632733711679-529326f6db12?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
      description: "State-of-the-art diagnostics to accurately identify vehicle issues.",
    },
    // Other services...
  ];

  return (
    <section className="py-16 mx-auto sm:py-20">
      <div className="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        <div className="flex justify-center object-center flex-col gap-12 sm:gap-16">
          <h2 className="text-4xl font-semibold tracking-tight text-gray-950 sm:text-5xl lg:text-6xl">
            Services
          </h2>
          <div className="mx-auto grid gap-12 space-y-10 md:space-y-0 sm:gap-16 lg:grid-cols-3">
            {services.map((service) => (
              <div key={service.name} className="group h-96 w-96 [perspective:1000px]">
                <div className="relative h-full w-full rounded-xl shadow-xl transition-all duration-500 [transform-style:preserve-3d] group-hover:[transform:rotateY(180deg)]">
                  {/* Front and back faces will go here */}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

  

- **group h-96 w-96 [perspective:1000px]:** Sets up the card container with a 3D perspective and a fixed size of 24rem by 24rem.

- **relative h-full w-full rounded-xl shadow-xl transition-all duration-500 [transform-style:preserve-3d] group-hover:[transform:rotateY(180deg)]:**
    - **relative:** Positions the inner container relative to its parent for absolute positioning of child elements.
    - **h-full w-full:** Ensures the inner container fills the outer container completely.
    - **rounded-xl:** Rounds the corners of the card to create a softer, modern look.
    - **shadow-xl:** Applies a large shadow to give the card depth and make it stand out.
    - **transition-all duration-500:** Smooths the transition effects over 0.5 seconds, making the flip more fluid.
    - **[transform-style:preserve-3d]:** Maintains the 3D space for the card elements.
    - **group-hover:[transform:rotateY(180deg)]:** Rotates the card by 180 degrees on hover, creating the flip effect.

### 7. **Add the Front Face of the Card**

Let’s add the front face of the card, which will include an image and the service name.

```undefined
const FlipCardComponent = () => {
  const services = [
    {
      step: "01",
      name: "Diagnostics",
      imageUrl: "https://images.unsplash.com/photo-1632733711679-529326f6db12?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3 &ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
      description: "State-of-the-art diagnostics to accurately identify vehicle issues.",
    },
    // Other services...
  ];

  return (
    <section className="py-16 mx-auto sm:py-20">
      <div className="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        <div className="flex justify-center object-center flex-col gap-12 sm:gap-16">
          <h2 className="text-4xl font-semibold tracking-tight text-gray-950 sm:text-5xl lg:text-6xl">
            Services
          </h2>
          <div className="mx-auto grid gap-12 space-y-10 md:space-y-0 sm:gap-16 lg:grid-cols-3">
            {services.map((service) => (
              <div key={service.name} className="group h-96 w-96 [perspective:1000px]">
                <div className="relative h-full w-full rounded-xl shadow-xl transition-all duration-500 [transform-style:preserve-3d] group-hover:[transform:rotateY(180deg)]">
                  {/* Front Face */}
                  <div className="absolute inset-0 h-full w-full rounded-xl [backface-visibility:hidden]">
                    {service.imageUrl && (
                      <Image
                        className="object-cover cursor-pointer object-left h-full w-full rounded-xl"
                        src={service.imageUrl}
                        alt={service.name}
                        width={320}
                        height={320}
                      />
                    )}
                    <p className="md:my-6 text-2xl">{service.name}</p>
                  </div>
                  {/* Back Face will go here */}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

- **absolute inset-0 h-full w-full rounded-xl:** Positions the front face absolutely within the card container, ensuring it covers the entire area and aligns perfectly with the back face.
- **[backface-visibility:hidden]:** Hides the front face when it’s rotated out of view during the flip.
- **object-cover cursor-pointer object-left h-full w-full rounded-xl:** Ensures the image covers the entire area, has rounded corners, and is positioned to the left without distortion.
- **md:my-6 text-2xl:** Adds vertical margin and sets the font size for the service name.

### 8. **Add the Back Face of the Card**

Now, we’ll add the back face, which will appear when the card is flipped. This face will include a service description and a call-to-action button.

```undefined
const FlipCardComponent = () => {
  const services = [
    {
      step: "01",
      name: "Diagnostics",
      imageUrl: "https://images.unsplash.com/photo-1632733711679-529326f6db12?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
      description: "State-of-the-art diagnostics to accurately identify vehicle issues.",
    },
    // Other services...
  ];

  return (
    <section className="py-16 mx-auto sm:py-20">
      <div className="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        <div className="flex justify-center object-center flex-col gap-12 sm:gap-16">
          <h2 className="text-4xl font-semibold tracking-tight text-gray-950 sm:text-5xl lg:text-6xl">
            Services
          </h2>
          <div className="mx-auto grid gap-12 space-y-10 md:space-y-0 sm:gap-16 lg:grid-cols-3">
            {services.map((service) => (
              <div key={service.name} className="group h-96 w-96 [perspective:1000px]">
                <div className="relative h-full w-full rounded-xl shadow-xl transition-all duration-500 [transform-style:preserve-3d] group-hover:[transform:rotateY(180deg)]">
                  {/* Front Face */}
                  <div className="absolute inset-0 h-full w-full rounded-xl [backface-visibility:hidden]">
                    {service.imageUrl && (
                      <Image
                        className="object-cover cursor-pointer object-left h-full w-full rounded-xl"
                        src={service.imageUrl}
                        alt={service.name}
                        width={320}
                        height={320}
                      />
                    )}
                    <p className="md:my-6 text-2xl">{service.name}</p>
                  </div>
                  {/* Back Face */}
                  <div className="absolute inset-0 h-full w-full rounded-xl bg-black/80 px-12 text-center text-slate-200 [transform:rotateY(180deg)] [backface-visibility:hidden]">
                    <div className="flex min-h-full flex-col items-center justify-center">
                      <h2 className="text-2xl font-bold mb-4">{service.name}</h2>
                      <p className="text-lg text-pretty text-center mb-4">
                        {service.description}
                      </p>
                      <a href="tel:555-555-5555" className="inline-flex">
                        <button className="my-2 bg-yellow-800 hover:bg-yellow-700 text-white font-bold py-2 px-4 w-auto rounded-full inline-flex items-center">
                          <span>Schedule Service</span>
                          <WrenchScrewdriverIcon className="h-6 w-6 ml-2" />
                        </button>
                      </a>
                    </div>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

  

- **absolute inset-0 h-full w-full rounded-xl:** Same positioning and sizing as the front face to ensure they align perfectly when the card flips.
- **bg-black/80 px-12 text-center text-slate-200:** Applies a semi-transparent black background (80% opacity), adds padding, centers the text, and styles it with a slate color.
- **[transform:rotateY(180deg)]:** Pre-rotates the back face by 180 degrees, so it’s hidden until the card is flipped.
- **flex min-h-full flex-col items-center justify-center:** Creates a flexbox container that vertically centers the content, ensuring the text and button are always centered within the card.
- **Button Classes:**
    - **my-2 bg-yellow-800 hover:bg-yellow-700 text-white font-bold py-2 px-4 w-auto rounded-full inline-flex items-center:** Styles the button with appropriate padding, font size, and hover/focus states to ensure it’s accessible and visually appealing.

### 9. **Final Code**

Here’s the full component after all the steps:

```undefined
import React from "react";
import { WrenchScrewdriverIcon } from "@heroicons/react/20/solid";
import Image from "next/image";

const services = [
  {
    step: "01",
    name: "Diagnostics",
    imageUrl:
      "https://images.unsplash.com/photo-1632733711679-529326f6db12?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
    description:
      "State-of-the-art diagnostics to accurately identify vehicle issues.",
  },
  {
    step: "02",
    name: "Repairs",
    imageUrl:
      "https://images.unsplash.com/photo-1687462970787-61d953508926?q=80&w=3087&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
    description:
      "Engine overhauls to brake replacements, we ensure high-quality work for your vehicle’s longevity.",
  },
  {
    step: "03",
    name: "Maintenance",
    imageUrl:
      "https://images.unsplash.com/photo-1635437536607-b8572f443763?q=80&w=2940&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
    description:
      "Oil changes, tire rotations, and more to enhance performance and prevent future issues.",
  },
];

const FlipCardComponent = () => {
  return (
    <section className="py-16 mx-auto sm:py-20">
      <div class

Name="mx-auto flex justify-center object-center px-4 py-16 sm:py-24 lg:max-w-7xl">
        <div className="flex justify-center object-center flex-col gap-12 sm:gap-16">
          <h2 className="text-4xl font-semibold tracking-tight text-gray-950 sm:text-5xl lg:text-6xl">
            Services
          </h2>
          <div className="mx-auto grid gap-12 space-y-10 md:space-y-0 sm:gap-16 lg:grid-cols-3">
            {services.map((service) => (
              <div key={service.name} className="group h-96 w-96 [perspective:1000px]">
                <div className="relative h-full w-full rounded-xl shadow-xl transition-all duration-500 [transform-style:preserve-3d] group-hover:[transform:rotateY(180deg)]">
                  {/* Front Face */}
                  <div className="absolute inset-0 h-full w-full rounded-xl [backface-visibility:hidden]">
                    {service.imageUrl && (
                      <Image
                        className="object-cover cursor-pointer object-left h-full w-full rounded-xl"
                        src={service.imageUrl}
                        alt={service.name}
                        width={320}
                        height={320}
                      />
                    )}
                    <p className="md:my-6 text-2xl">{service.name}</p>
                  </div>
                  {/* Back Face */}
                  <div className="absolute inset-0 h-full w-full rounded-xl bg-black/80 px-12 text-center text-slate-200 [transform:rotateY(180deg)] [backface-visibility:hidden]">
                    <div className="flex min-h-full flex-col items-center justify-center">
                      <h2 className="text-2xl font-bold mb-4">{service.name}</h2>
                      <p className="text-lg text-pretty text-center mb-4">
                        {service.description}
                      </p>
                      <a href="tel:555-555-5555" className="inline-flex">
                        <button className="my-2 bg-yellow-800 hover:bg-yellow-700 text-white font-bold py-2 px-4 w-auto rounded-full inline-flex items-center">
                          <span>Schedule Service</span>
                          <WrenchScrewdriverIcon className="h-6 w-6 ml-2" />
                        </button>
                      </a>
                    </div>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
};

export default FlipCardComponent;
```

### Recap

By adding interactive elements to your website you can increase user engagement while also providing valuable information to users in an easy to consume manner.

Cheers 🚀
