Components
Loading preview...
"use client";
import { ChevronLeft, ChevronRight } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useCallback, useEffect, useState } from "react";
import { Badge } from "@/demos/ui/badge";
import { Button } from "@/components/ui/button";
import {
Carousel,
type CarouselApi,
CarouselContent,
CarouselItem,
} from "@/demos/ui/carousel";
const posts = [
{
category: "Robotics",
image: "https://images.cnippet.dev/image/upload/v1770400411/h1.jpg",
publishedDate: "March 20, 2025",
tailwindClass: " basis-[60%]",
title: "The Rise of Collaborative Robots: A New Era in Automation",
type: "full",
},
{
author: {
avatar: "https://images.cnippet.dev/image/upload/v1770400411/a3.jpg",
name: "Sarah Chen",
},
category: "Arduino",
image: "https://images.cnippet.dev/image/upload/v1770400411/h2.jpg",
publishedDate: "March 19, 2025",
readTime: "6 min read",
title: "Getting Started with Arduino: 5 Beginner Projects",
type: "image",
},
{
category: "Microcontrollers",
image: "https://images.cnippet.dev/image/upload/v1770400411/h4.jpg",
publishedDate: "March 18, 2025",
title: "Top 5 Microcontroller Boards for 2025",
type: "dark",
},
{
author: {
avatar: "https://images.cnippet.dev/image/upload/v1770400411/a2.jpg",
name: "Mark Davis",
},
category: "Microcontrollers",
image: "https://images.cnippet.dev/image/upload/v1770400411/h6.jpg",
publishedDate: "March 17, 2025",
readTime: "7 min read",
title: "ESP32 vs Raspberry Pi Pico: Choosing the Right MCU",
type: "image",
},
{
category: "AI & Machine Learning",
image: "https://images.cnippet.dev/image/upload/v1770400411/h7.jpg",
publishedDate: "March 16, 2025",
tailwindClass: " basis-[60%]",
title: "TinyML: Running Neural Networks on Microcontrollers",
type: "full",
},
];
function CurvedCorner({ position }: { position: "left" | "bottom" }) {
if (position === "left") {
return (
<div className="absolute top-0 right-auto bottom-auto -left-2 flex h-2 w-2 items-center justify-center">
<svg
fill="none"
height="8"
viewBox="0 0 8 8"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M8 0H0C4.41828 0 8 3.58172 8 8V0Z"
fill="white"
fillRule="evenodd"
/>
</svg>
</div>
);
}
return (
<div className="absolute top-auto right-0 -bottom-2 left-auto flex h-2 w-2 items-center justify-center">
<svg
fill="none"
height="8"
viewBox="0 0 8 8"
width="8"
xmlns="http://www.w3.org/2000/svg"
>
<path
clipRule="evenodd"
d="M8 0H0C4.41828 0 8 3.58172 8 8V0Z"
fill="white"
fillRule="evenodd"
/>
</svg>
</div>
);
}
export default function BlogVariant3() {
const [api, setApi] = useState<CarouselApi>();
const [current, setCurrent] = useState(0);
const [count, setCount] = useState(0);
useEffect(() => {
if (!api) return;
setCount(api.scrollSnapList().length);
setCurrent(api.selectedScrollSnap());
api.on("select", () => {
setCurrent(api.selectedScrollSnap());
});
}, [api]);
const scrollPrev = useCallback(() => {
api?.scrollPrev();
}, [api]);
const scrollNext = useCallback(() => {
api?.scrollNext();
}, [api]);
const progress = count > 0 ? ((current + 1) / count) * 100 : 0;
return (
<section className="w-full bg-white py-12 sm:py-16 lg:py-20">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex flex-col gap-6 sm:flex-row sm:items-end sm:justify-between">
<div>
<Badge
className="mb-4 rounded-full border-gray-300 px-4 py-0 font-medium font-onest text-gray-700 text-sm"
variant="outline"
>
Future Tech
</Badge>
<h2 className="font-medium max-w-lg font-onest text-4xl text-gray-900 tracking-tight sm:text-5xl">
Explore the future of
<br />
robotics
</h2>
</div>
<Button
className="w-fit rounded-full border-blue-600 bg-blue-600 px-8 py-2.5 font-medium font-onest text-sm text-white hover:bg-blue-700"
render={<Link href="#" />}
>
View all
</Button>
</div>
</div>
<div className="mt-10">
<Carousel
className="w-full"
opts={{
align: "start",
loop: false,
}}
setApi={setApi}
>
<CarouselContent className="-ml-5 translate-x-4 space-x-6 sm:translate-x-40">
{posts.map((post) => (
<CarouselItem
className={`pl-5 ${post.tailwindClass ? post.tailwindClass : "basis-[30%]"}`}
key={post.title}
>
{post.type === "full" && <FullCard post={post} />}
{post.type === "image" && <ImageCard post={post} />}
{post.type === "dark" && <DarkCard post={post} />}
</CarouselItem>
))}
</CarouselContent>
</Carousel>
<div className="mx-auto mt-8 flex max-w-7xl items-center gap-6 px-4 md:px-8">
<div className="flex-1">
<div className="h-0.5 w-full bg-gray-100">
<div
className="h-full bg-blue-600 transition-all duration-300"
style={{ width: `${progress}%` }}
/>
</div>
</div>
<div className="flex items-center gap-2">
<button
className="flex size-10 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-600 transition-colors hover:border-gray-400 hover:text-gray-900 disabled:opacity-50"
disabled={current === 0}
onClick={scrollPrev}
>
<ChevronLeft className="size-5" />
</button>
<button
className="flex size-10 items-center justify-center rounded-full border border-gray-300 bg-white text-gray-600 transition-colors hover:border-gray-400 hover:text-gray-900 disabled:opacity-50"
disabled={current === count - 1}
onClick={scrollNext}
>
<ChevronRight className="size-5" />
</button>
</div>
</div>
</div>
</section>
);
}
function FullCard({
post,
}: {
post: {
category: string;
title: string;
publishedDate: string;
image: string;
};
}) {
return (
<article className="group relative grid h-105 cursor-pointer grid-cols-1 gap-4 overflow-hidden sm:grid-cols-12">
<div className="relative z-10 flex w-full flex-col sm:col-span-5">
<Link
className="font-medium font-onest text-blue-600 text-sm hover:text-blue-700"
href="#"
>
{post.category}
</Link>
<h3 className="mt-3 font-medium font-onest text-2xl! text-gray-900 tracking-tight sm:text-4xl!">
{post.title}
</h3>
<p className="mt-3 text-gray-500 text-sm">
Published on {post.publishedDate}
</p>
<Button
className="mt-auto w-fit rounded-full border-blue-600 bg-blue-600 px-6 py-2 font-medium font-onest text-sm text-white hover:bg-blue-700"
render={<Link href="#" />}
>
Read more
</Button>
</div>
<div className="sm:col-span-7">
<Image
alt=""
className="h-full overflow-hidden rounded-3xl object-cover"
height={1080}
src={post.image}
width={1920}
/>
</div>
</article>
);
}
function ImageCard({
post,
}: {
post: {
category: string;
title: string;
publishedDate: string;
readTime?: string;
image: string;
author?: {
name: string;
avatar: string;
};
};
}) {
return (
<article className="group flex h-full cursor-pointer flex-col">
<div className="relative aspect-4/3 w-full overflow-hidden rounded-b-3xl rounded-tl-3xl">
<Image
alt={post.title}
className="object-cover transition-transform duration-300 group-hover:scale-105"
fill
src={post.image}
/>
{post.readTime && (
<span className="absolute top-0 right-0 rounded-bl-lg bg-white px-3 py-1 font-medium text-gray-600 text-xs">
{post.readTime}
<CurvedCorner position="left" />
<CurvedCorner position="bottom" />
</span>
)}
</div>
<div>
<h3 className="mt-3 font-medium font-onest text-gray-900 text-md! tracking-tight sm:text-xl!">
{post.title}
</h3>
<p className="mt-3 text-gray-500 text-sm">
Published on {post.publishedDate}
</p>
</div>
{post.author && (
<div className="mt-auto flex items-center justify-between">
<div>
<p className="text-gray-500 text-xs">Article by</p>
<p className="font-medium font-onest text-gray-900">
{post.author.name}
</p>
</div>
<div className="relative size-12 overflow-hidden rounded-md">
<Image
alt={post.author.name}
className="object-cover"
fill
src={post.author.avatar}
/>
</div>
</div>
)}
</article>
);
}
function DarkCard({
post,
}: {
post: {
category: string;
title: string;
publishedDate: string;
image: string;
};
}) {
return (
<article className="group relative flex h-105 cursor-pointer flex-col justify-between overflow-hidden rounded-3xl bg-neutral-800 p-6">
<div className="absolute inset-0 opacity-40">
<Image alt="" className="object-cover" fill src={post.image} />
</div>
<div className="absolute inset-0 bg-linear-to-t from-neutral-900/80 via-neutral-900/40 to-transparent" />
<div className="relative z-10">
<Link
className="font-medium font-onest text-amber-500 text-sm hover:text-amber-400"
href="#"
>
{post.category}
</Link>
<h3 className="mt-3 font-medium font-onest text-2xl! text-white leading-snug sm:text-3xl!">
{post.title}
</h3>
<p className="mt-3 text-gray-300 text-sm">
Published on {post.publishedDate}
</p>
</div>
<div className="relative z-10">
<Button
className="rounded-full bg-blue-600 px-6 py-2 font-medium font-onest text-sm text-white hover:bg-blue-700"
render={<Link href="#" />}
>
Read more
</Button>
</div>
</article>
);
}