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~
Want to see a live demo? Demo
Full Code and Repo Github
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 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 FlipCardComponent.jsx
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 and5rem
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.
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 to7xl
(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.
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 of3rem
(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.
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, from2.25rem
to3.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.
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. Thegap-12
andsm: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.
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 a3D
perspective and a fixed size of24rem
by24rem
. -
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 by180 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.
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.
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 by180 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:
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. If you found this helpful you can visit our site Zephyr Pixels for more tips on web development and Next.js. Cheers 🚀
Top comments (0)