feat: add robotics components and data structures
- 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.
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user