- Introduced RobotProductCard component for displaying robot details. - Added WhyUs component highlighting key reasons for choosing our robotics solutions. - Implemented CursorSpotlight for enhanced user interaction. - Created GlassPanel for a stylish UI element. - Developed MotionSection for animated section visibility. - Added PremiumButton for versatile button options. - Established data structures for industries and robots, including detailed specifications and use cases. - Included utility functions for retrieving robots by slug and category.
133 lines
4.4 KiB
TypeScript
133 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* Premium horizontal logo marquee.
|
|
*
|
|
* Setup:
|
|
* 1. globals.css must have:
|
|
* @keyframes marquee {
|
|
* 0% { transform: translateX(0); }
|
|
* 100% { transform: translateX(-50%); }
|
|
* }
|
|
* 2. Pass clients prop or create lib/clients.ts:
|
|
* export const clients = [
|
|
* { src: "/images/clients/brand-a.png", alt: "Brand A" },
|
|
* ...
|
|
* ];
|
|
*/
|
|
import Image from "next/image";
|
|
import { useReducedMotion } from "framer-motion";
|
|
|
|
export type Client = { src: string; alt: string };
|
|
|
|
export function ClientLogoMarquee({
|
|
clients,
|
|
eyebrow = "Our Clients",
|
|
title,
|
|
subtitle,
|
|
}: {
|
|
clients: Client[];
|
|
eyebrow?: string;
|
|
title: React.ReactNode;
|
|
subtitle?: React.ReactNode;
|
|
}) {
|
|
const reduce = useReducedMotion();
|
|
const loop = [...clients, ...clients];
|
|
|
|
return (
|
|
<section className="relative overflow-hidden py-16 md:py-20 lg:py-24">
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute -left-32 top-1/3 size-[480px] rounded-full radial-gold blur-3xl opacity-30"
|
|
/>
|
|
<div className="mx-auto max-w-7xl px-6 md:px-10">
|
|
<div className="flex flex-col items-center gap-6 text-center">
|
|
<div className="flex items-center gap-3 text-[11px] font-medium uppercase tracking-[0.22em] text-mist">
|
|
<span className="h-px w-8 bg-gold/40" />
|
|
<span>{eyebrow}</span>
|
|
</div>
|
|
<h2 className="font-display text-4xl leading-[1.04] text-bone text-balance md:text-5xl">
|
|
{title}
|
|
</h2>
|
|
{subtitle ? (
|
|
<p className="max-w-2xl text-pretty text-mist md:text-lg">{subtitle}</p>
|
|
) : null}
|
|
</div>
|
|
|
|
<div className="relative mt-12 md:mt-14">
|
|
{/* edge fades */}
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-gradient-to-r from-obsidian to-transparent md:w-32"
|
|
/>
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-gradient-to-l from-obsidian to-transparent md:w-32"
|
|
/>
|
|
<div
|
|
className="flex overflow-hidden"
|
|
style={{
|
|
maskImage:
|
|
"linear-gradient(90deg, transparent, black 8%, black 92%, transparent)",
|
|
}}
|
|
>
|
|
{reduce ? (
|
|
<ClientGrid clients={clients} />
|
|
) : (
|
|
<div
|
|
className="flex shrink-0 gap-4 pr-4 md:gap-6 md:pr-6"
|
|
style={{
|
|
animation: "marquee 50s linear infinite",
|
|
animationPlayState: "running",
|
|
}}
|
|
onMouseEnter={(e) =>
|
|
(e.currentTarget.style.animationPlayState = "paused")
|
|
}
|
|
onMouseLeave={(e) =>
|
|
(e.currentTarget.style.animationPlayState = "running")
|
|
}
|
|
>
|
|
{loop.map((c, i) => (
|
|
<LogoTile key={`${c.src}-${i}`} client={c} />
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function LogoTile({ client }: { client: Client }) {
|
|
return (
|
|
<div className="group/tile relative grid h-24 w-44 shrink-0 place-items-center overflow-hidden rounded-2xl border border-bone/10 bg-graphite/40 px-5 backdrop-blur-sm transition-all duration-500 hover:border-gold/30 hover:bg-graphite/60 md:h-28 md:w-52">
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-700 group-hover/tile:opacity-100"
|
|
style={{
|
|
background:
|
|
"radial-gradient(60% 80% at 50% 50%, rgba(212,164,55,0.18), transparent 70%)",
|
|
}}
|
|
/>
|
|
<Image
|
|
src={client.src}
|
|
alt={client.alt}
|
|
width={200}
|
|
height={100}
|
|
className="relative max-h-14 w-auto object-contain opacity-70 brightness-[1.05] contrast-[0.9] grayscale transition-all duration-500 group-hover/tile:scale-105 group-hover/tile:opacity-100 group-hover/tile:grayscale-0 md:max-h-16"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ClientGrid({ clients }: { clients: Client[] }) {
|
|
return (
|
|
<div className="grid w-full grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-6">
|
|
{clients.map((c) => (
|
|
<LogoTile key={c.src} client={c} />
|
|
))}
|
|
</div>
|
|
);
|
|
}
|