|
|
|
|
@@ -1,25 +1,203 @@
|
|
|
|
|
// AUTO-GENERATED by per-section-migrate. Edit freely — Bob will treat this
|
|
|
|
|
// file as the canonical source for the "hero" section.
|
|
|
|
|
/* eslint-disable */
|
|
|
|
|
// @ts-nocheck — generated by catalog-eject; runtime-correct but TS strict-mode false-positives on inlined catalog body
|
|
|
|
|
import { useRef } from "react";
|
|
|
|
|
import { useScroll, useTransform, motion } from "motion/react";
|
|
|
|
|
import { Check } from "lucide-react";
|
|
|
|
|
import { cls } from "@/lib/utils";
|
|
|
|
|
import HeroBackgroundSlot from "@/components/ui/HeroBackgroundSlot";
|
|
|
|
|
import Button from "@/components/ui/Button";
|
|
|
|
|
import TextAnimation from "@/components/ui/TextAnimation";
|
|
|
|
|
import AvatarGroup from "@/components/ui/AvatarGroup";
|
|
|
|
|
import ImageOrVideo from "@/components/ui/ImageOrVideo";
|
|
|
|
|
import ScrollReveal from "@/components/ui/ScrollReveal";
|
|
|
|
|
import FloatingGradientBackground from "@/components/ui/FloatingGradientBackground";
|
|
|
|
|
|
|
|
|
|
import React from 'react';
|
|
|
|
|
import HeroBillboardCreator from "@/components/sections/hero/HeroBillboardCreator";
|
|
|
|
|
const avatarsSrc = [
|
|
|
|
|
"http://img.b2bpic.net/free-photo/gorgeous-indian-woman-wear-formal-posing-cafe_627829-4625.jpg",
|
|
|
|
|
"http://img.b2bpic.net/free-photo/businesswoman-drinking-coffee-while-short-break_329181-17590.jpg",
|
|
|
|
|
"http://img.b2bpic.net/free-photo/young-pretty-blogger-holding-shoe-heel-sneakers-thoughtfully-looking-aside-while-recording-new-fashion-video-vlog-camera_574295-5294.jpg"
|
|
|
|
|
];
|
|
|
|
|
const primaryButton = {
|
|
|
|
|
href: "#",
|
|
|
|
|
text: "Explore Collections"
|
|
|
|
|
};
|
|
|
|
|
const secondaryButton = {
|
|
|
|
|
text: "View Lookbook",
|
|
|
|
|
href: "#"
|
|
|
|
|
};
|
|
|
|
|
const floatingCardsSrc = [
|
|
|
|
|
"http://img.b2bpic.net/free-photo/glowing-red-warning-triangle-with-exclamation-mark-dark-wet-surface_84443-91213.jpg",
|
|
|
|
|
"http://img.b2bpic.net/free-photo/letter-o-made-green-grass-with-flowers-isolated-white_169016-57035.jpg",
|
|
|
|
|
"http://img.b2bpic.net/free-photo/letter-t-made-green-grass-with-flowers-isolated-white_169016-58706.jpg",
|
|
|
|
|
"http://img.b2bpic.net/free-photo/woman-holding-letter-w_1187-1117.jpg"
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
type FloatingCardPosition = "top-left" | "top-right" | "middle-left" | "middle-right";
|
|
|
|
|
|
|
|
|
|
type HeroBillboardFloatingCardsProps = {
|
|
|
|
|
avatarsSrc: string[];
|
|
|
|
|
avatarsLabel: string;
|
|
|
|
|
title: string;
|
|
|
|
|
description: string;
|
|
|
|
|
primaryButton: { text: string; href: string };
|
|
|
|
|
secondaryButton: { text: string; href: string };
|
|
|
|
|
note?: string;
|
|
|
|
|
floatingCardsSrc: [string, string, string, string];
|
|
|
|
|
logosSrc?: string[];
|
|
|
|
|
} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never });
|
|
|
|
|
|
|
|
|
|
const POSITIONS: FloatingCardPosition[] = ["top-left", "top-right", "middle-left", "middle-right"];
|
|
|
|
|
|
|
|
|
|
const FLOATING_CARD_CONFIG: Record<FloatingCardPosition, {
|
|
|
|
|
position: string;
|
|
|
|
|
rotation: string;
|
|
|
|
|
size: string;
|
|
|
|
|
animation: { duration: number; delay: number; yOffset: number; entryDelay: number };
|
|
|
|
|
}> = {
|
|
|
|
|
"top-left": {
|
|
|
|
|
position: "top-8 left-0",
|
|
|
|
|
rotation: "-rotate-8",
|
|
|
|
|
size: "size-20 xl:size-22 2xl:size-24",
|
|
|
|
|
animation: { duration: 4, delay: 0, yOffset: -8, entryDelay: 0.3 },
|
|
|
|
|
},
|
|
|
|
|
"top-right": {
|
|
|
|
|
position: "top-4 right-4",
|
|
|
|
|
rotation: "rotate-10",
|
|
|
|
|
size: "size-18 xl:size-20 2xl:size-22",
|
|
|
|
|
animation: { duration: 5, delay: 1, yOffset: -10, entryDelay: 0.5 },
|
|
|
|
|
},
|
|
|
|
|
"middle-left": {
|
|
|
|
|
position: "top-1/2 left-2",
|
|
|
|
|
rotation: "rotate-6",
|
|
|
|
|
size: "size-18 xl:size-20 2xl:size-22",
|
|
|
|
|
animation: { duration: 4.5, delay: 0.5, yOffset: -9, entryDelay: 0.7 },
|
|
|
|
|
},
|
|
|
|
|
"middle-right": {
|
|
|
|
|
position: "top-1/2 right-0",
|
|
|
|
|
rotation: "-rotate-6",
|
|
|
|
|
size: "size-20 xl:size-22 2xl:size-24",
|
|
|
|
|
animation: { duration: 3.8, delay: 1.5, yOffset: -8, entryDelay: 0.9 },
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const HeroBillboardFloatingCards = () => {
|
|
|
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
const { scrollYProgress } = useScroll({ target: containerRef });
|
|
|
|
|
|
|
|
|
|
const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
|
|
|
|
|
const scale = useTransform(scrollYProgress, [0, 1], [1.05, 1]);
|
|
|
|
|
|
|
|
|
|
export default function HeroSection(): React.JSX.Element {
|
|
|
|
|
return (
|
|
|
|
|
<div id="hero" data-section="hero">
|
|
|
|
|
<HeroBillboardCreator
|
|
|
|
|
tag="AI-Driven Style"
|
|
|
|
|
title="The Future of Fashion"
|
|
|
|
|
titleHighlight="Curated by AI."
|
|
|
|
|
description="Elevate your personal style with our AI-powered curation. Immersive experiences for men and women's fashion collections defined by innovation."
|
|
|
|
|
primaryButton={{ text: "Explore Collections", href: "#" }}
|
|
|
|
|
note="Personalized for every silhouette"
|
|
|
|
|
videos={[
|
|
|
|
|
{ videoSrc: "https://storage.googleapis.com/webild/default/templates/skincare-luxury/influencer-amara.mp4", name: "Men's Tech-Wear", followers: "5M+ Trends", imageSrc: "http://img.b2bpic.net/free-photo/glowing-red-warning-triangle-with-exclamation-mark-dark-wet-surface_84443-91213.jpg" },
|
|
|
|
|
{ videoSrc: "https://storage.googleapis.com/webild/default/templates/skincare-luxury/influencer-chloe.mp4", name: "Women's Luxe-Flow", followers: "8M+ Trends", imageSrc: "http://img.b2bpic.net/free-photo/letter-o-made-green-grass-with-flowers-isolated-white_169016-57035.jpg" }
|
|
|
|
|
]}
|
|
|
|
|
badgeText="Trend-setting"
|
|
|
|
|
<section aria-label="Hero section" className="relative">
|
|
|
|
|
<HeroBackgroundSlot />
|
|
|
|
|
<div className="absolute inset-0 z-0 pointer-events-none">
|
|
|
|
|
<FloatingGradientBackground position="absolute" />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="absolute inset-0 z-0 pointer-events-none">
|
|
|
|
|
<FloatingGradientBackground position="absolute" />
|
|
|
|
|
</div>
|
|
|
|
|
<div ref={containerRef} className="pt-25 pb-20 md:pt-30 perspective-distant relative z-10">
|
|
|
|
|
<div className="relative w-content-width mx-auto">
|
|
|
|
|
{POSITIONS.map((position, index) => {
|
|
|
|
|
const config = FLOATING_CARD_CONFIG[position];
|
|
|
|
|
const src = floatingCardsSrc[index];
|
|
|
|
|
if (!src) return null;
|
|
|
|
|
return (
|
|
|
|
|
<motion.div
|
|
|
|
|
key={index}
|
|
|
|
|
className={cls("absolute z-10 hidden md:block", config.position)}
|
|
|
|
|
animate={{ y: [0, config.animation.yOffset, 0] }}
|
|
|
|
|
transition={{ duration: config.animation.duration, repeat: Infinity, ease: "easeInOut", delay: config.animation.delay }}
|
|
|
|
|
>
|
|
|
|
|
<motion.div
|
|
|
|
|
className={cls("p-2 card rounded-2xl overflow-hidden", config.size, config.rotation)}
|
|
|
|
|
initial={{ opacity: 0, y: 30 }}
|
|
|
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
|
|
|
viewport={{ once: true }}
|
|
|
|
|
transition={{ duration: 0.6, delay: config.animation.entryDelay }}
|
|
|
|
|
>
|
|
|
|
|
<img src={src} alt="" className="w-full h-full object-contain rounded-xl" />
|
|
|
|
|
</motion.div>
|
|
|
|
|
</motion.div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
|
|
|
|
|
<div className="flex flex-col items-center gap-3 md:max-w-8/10 mx-auto text-center">
|
|
|
|
|
<div className="p-0.5 pr-3 mb-1 card rounded-full">
|
|
|
|
|
<AvatarGroup avatarsSrc={avatarsSrc} label={"Over 1M+ Styles Curated"} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<TextAnimation
|
|
|
|
|
text={"The Future of Fashion"}
|
|
|
|
|
variant="slide-up"
|
|
|
|
|
gradientText={true}
|
|
|
|
|
tag="h1"
|
|
|
|
|
className="text-7xl 2xl:text-8xl leading-[1.15] font-semibold text-center text-balance"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<TextAnimation
|
|
|
|
|
text={"Elevate your personal style with our AI-powered curation. Immersive experiences for men and women's fashion collections defined by innovation."}
|
|
|
|
|
variant="slide-up"
|
|
|
|
|
gradientText={false}
|
|
|
|
|
tag="p"
|
|
|
|
|
className="text-lg md:text-xl leading-snug text-balance"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div className="flex flex-wrap justify-center gap-3 mt-2 md:mt-3">
|
|
|
|
|
<Button text={primaryButton.text} href={primaryButton.href} variant="primary" />
|
|
|
|
|
<Button text={secondaryButton.text} href={secondaryButton.href} variant="secondary" animationDelay={0.1} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{"Personalized for every silhouette" && (
|
|
|
|
|
<motion.div
|
|
|
|
|
className="flex justify-center mt-2 md:mt-3 text-sm text-foreground/70"
|
|
|
|
|
initial={{ opacity: 0, y: 10 }}
|
|
|
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
|
|
|
viewport={{ once: true }}
|
|
|
|
|
transition={{ duration: 0.5, delay: 0.2 }}
|
|
|
|
|
>
|
|
|
|
|
<span className="flex items-center gap-2">
|
|
|
|
|
<div className="flex items-center justify-center size-4 primary-button rounded-full">
|
|
|
|
|
<Check className="size-1/2 text-primary-cta-text" />
|
|
|
|
|
</div>
|
|
|
|
|
{"Personalized for every silhouette"}
|
|
|
|
|
</span>
|
|
|
|
|
</motion.div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="w-content-width mx-auto mt-8 p-2 card rounded overflow-hidden rotate-x-20 md:hidden">
|
|
|
|
|
<ImageOrVideo className="aspect-4/5" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<motion.div
|
|
|
|
|
style={{ rotateX: rotate, scale }}
|
|
|
|
|
className="w-content-width mx-auto mt-5 2xl:mt-2 p-2 xl:p-3 2xl:p-4 card rounded overflow-hidden hidden md:block"
|
|
|
|
|
>
|
|
|
|
|
<ImageOrVideo className="aspect-video" />
|
|
|
|
|
</motion.div>
|
|
|
|
|
|
|
|
|
|
{undefined && undefined.length > 0 && (
|
|
|
|
|
<ScrollReveal variant="slide-up" className="w-content-width mx-auto mt-2 xl:mt-4 2xl:mt-6 overflow-hidden mask-fade-x">
|
|
|
|
|
<div className="flex w-max animate-marquee-horizontal" style={{ animationDuration: "45s" }}>
|
|
|
|
|
{[...undefined, ...undefined, ...undefined, ...undefined, ...undefined, ...undefined, ...undefined, ...undefined].map((logo, index) => (
|
|
|
|
|
<div key={index} className="shrink-0 mx-1 xl:mx-2 2xl:mx-3 p-3 rounded card">
|
|
|
|
|
<img src={logo} alt="" className="h-8 w-auto object-contain rounded" />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</ScrollReveal>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default function HeroSection() {
|
|
|
|
|
return (
|
|
|
|
|
<div data-webild-section="hero" id="hero">
|
|
|
|
|
<HeroBillboardFloatingCards />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|