Overview Stacked
Overview stacked cards animation when clicked to expand or shrink
Overview
The Oddysey
Explore unknow galexies.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.
September 2024
Angry Rabit
They are coming for you.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.
September 2024
Ghost town
Scary ghost.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.
September 2024
Pirates in the jungle
Find the treasure.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.
September 2024
Lost in the mountains
Be careful.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.
September 2024
Installation
Run the following command
It will create a new file overview-Stacked.tsx
inside the components/cards/overview-Stacked.tsx
directory.
mkdir -p components/cards && touch components/cards/overview-stacked.tsx
Paste the code
Open the newly created file and paste the following code:
"use client";
import { motion } from "framer-motion";
import { ChevronUp } from "lucide-react";
import Image, { StaticImageData } from "next/image";
import { useState } from "react";
import { cn } from "@/lib/utils";
import image1 from "@/public/others/photo-1.jpg";
import image2 from "@/public/others/photo-2.jpg";
import image3 from "@/public/others/photo-3.jpg";
import image4 from "@/public/others/photo-4.jpg";
import image5 from "@/public/others/photo-5.jpg";
type Card = {
id: number;
title: string;
image: StaticImageData;
description: string;
sm: string;
};
const CARDS: Card[] = [
{
id: 1,
title: "The Oddysey",
sm: "Explore unknow galexies.",
image: image1,
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.",
},
{
id: 2,
title: "Angry Rabit",
sm: "They are coming for you.",
image: image2,
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.",
},
{
id: 3,
title: "Ghost town",
sm: "Scary ghost.",
image: image3,
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.",
},
{
id: 4,
title: "Pirates in the jungle",
sm: "Find the treasure.",
image: image4,
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.",
},
{
id: 5,
title: "Lost in the mountains",
sm: "Be careful.",
image: image5,
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lobortis, justo id ullamcorper fermentum, felis lectus facilisis ex, sed consectetur lectus nisi in metus.",
},
];
const OverviewStacked = () => {
const [stacked, setStacked] = useState<boolean>(false);
return (
<div className="h-full w-full relative flex items-center justify-center">
<motion.div
layout
className="w-full flex flex-col items-center gap-10 max-w-md relative h-auto"
>
<div
className="flex justify-between items-center gap-10 w-full"
onClick={() => setStacked(!stacked)}
>
<h1 className="text-3xl font-semibold">Overview</h1>
<button className="flex items-center gap-2 text-gray-500">
<p className="flex items-center gap-2">
{CARDS.length}
<span>
<ChevronUp
size={16}
className={cn(
"transition-all duration-150",
stacked && "rotate-180"
)}
/>
</span>
</p>
</button>
</div>
<motion.ul
className={`flex flex-col gap-4 justify-center items-center max-w-md w-full h-fit`}
layout
transition={{
duration: 0.5,
ease: "easeInOut",
}}
style={{
flexDirection: "column",
gap: "8px",
}}
>
{CARDS.map((card, index) => (
<Card key={card.id} card={card} stacked={stacked} index={index} />
))}
</motion.ul>
</motion.div>
</div>
);
};
function Card(props: { card: Card; stacked: boolean; index: number }) {
return (
<motion.li
layout
className={cn(
"relative flex items-start space-x-4 border p-4 rounded-2xl bg-background",
props.stacked ? "overflow-hidden" : ""
)}
style={{
//position: props.stacked ? "absolute" : "static",
width: props.stacked ? `calc(100% - ${props.index * 20}px)` : "auto",
zIndex: CARDS.length - props.index,
}}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: props.stacked ? props.index * -130 : 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{
type: "spring",
stiffness: 500,
damping: 30,
mass: 1,
}}
>
<div
className="relative flex items-center justify-center w-16 h-16 shrink-0"
style={{ borderRadius: "14px" }}
>
<Image
src={props.card.image}
alt={props.card.title}
className="w-full h-full object-cover"
style={{ borderRadius: "14px" }}
/>
</div>
<div className="overflow-hidden w-full">
<div className="flex items-center justify-start gap-2">
<h2 className="text-xl font-semibold truncate">{props.card.title}</h2>
<span className="w-1 h-1 rounded-full bg-slate-800"></span>
<p className="text-sm text-gray-600 line-clamp-1">{props.card.sm}</p>
</div>
<p className="text-sm text-gray-500 mt-1 mb-2 line-clamp-2">
{props.card.description}
</p>
<p className="text-xs text-gray-400 w-full text-right pr-4">
September 2024
</p>
</div>
</motion.li>
);
}
export default OverviewStacked;
Credits
Inspired from Jakub Krehel
Built by Bossadi Zenith