Sets up the project structure with Vite, React, and essential libraries like GSAP and Framer Motion. Configures Tailwind CSS for styling, including dark mode and custom color schemes. Includes basic component structure for the application, laying the groundwork for UI development.
153 lines
6.4 KiB
TypeScript
153 lines
6.4 KiB
TypeScript
import React, { useLayoutEffect, useRef } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import gsap from 'gsap';
|
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
|
|
|
gsap.registerPlugin(ScrollTrigger);
|
|
|
|
const Process: React.FC = () => {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const imageColRef = useRef<HTMLDivElement>(null);
|
|
const imgRef = useRef<HTMLImageElement>(null);
|
|
const textColRef = useRef<HTMLDivElement>(null);
|
|
|
|
useLayoutEffect(() => {
|
|
const ctx = gsap.context(() => {
|
|
// Fade in the whole section
|
|
gsap.fromTo(containerRef.current,
|
|
{ opacity: 0, y: 50 },
|
|
{
|
|
opacity: 1,
|
|
y: 0,
|
|
duration: 1,
|
|
ease: "power3.out",
|
|
scrollTrigger: {
|
|
trigger: containerRef.current,
|
|
start: "top 80%",
|
|
once: true
|
|
}
|
|
}
|
|
);
|
|
|
|
// Desktop specific animations
|
|
const mm = gsap.matchMedia();
|
|
|
|
mm.add("(min-width: 1024px)", () => {
|
|
if (containerRef.current && imageColRef.current && imgRef.current) {
|
|
// Pinning logic
|
|
ScrollTrigger.create({
|
|
trigger: containerRef.current,
|
|
start: "top center",
|
|
end: "bottom center",
|
|
pin: imageColRef.current,
|
|
pinSpacing: false,
|
|
scrub: true,
|
|
});
|
|
|
|
// Scroll-to-Zoom logic
|
|
gsap.fromTo(imgRef.current,
|
|
{ scale: 1 },
|
|
{
|
|
scale: 2.2,
|
|
ease: "power1.inOut",
|
|
scrollTrigger: {
|
|
trigger: containerRef.current,
|
|
start: "top bottom",
|
|
end: "bottom top",
|
|
scrub: 1,
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
// Animate steps as they come into view
|
|
const steps = gsap.utils.toArray('.process-step');
|
|
steps.forEach((step: any) => {
|
|
gsap.fromTo(step,
|
|
{ opacity: 0.3, x: 20 },
|
|
{
|
|
opacity: 1,
|
|
x: 0,
|
|
duration: 0.5,
|
|
scrollTrigger: {
|
|
trigger: step,
|
|
start: "top 80%",
|
|
end: "top 50%",
|
|
scrub: 0.5,
|
|
toggleActions: "play reverse play reverse"
|
|
}
|
|
}
|
|
);
|
|
});
|
|
});
|
|
}, containerRef);
|
|
return () => ctx.revert();
|
|
}, []);
|
|
|
|
return (
|
|
<section ref={containerRef} className="py-24 bg-background-light dark:bg-background-dark overflow-hidden">
|
|
<div className="max-w-7xl mx-auto px-6">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-start relative">
|
|
|
|
{/* Image Column - Will be pinned */}
|
|
<div ref={imageColRef} className="relative rounded-2xl overflow-hidden border border-gray-200 dark:border-white/10 shadow-2xl h-[400px] lg:h-[400px] w-full max-w-lg lg:max-w-none mx-auto group z-10">
|
|
<img
|
|
ref={imgRef}
|
|
alt="Close up of server lights in dark room"
|
|
className="w-full h-full object-cover opacity-90 will-change-transform origin-center scale-100"
|
|
src="https://lh3.googleusercontent.com/aida-public/AB6AXuD6sSjDiHv-HJ5pcRY_PSPYLWxc5ePEHS5R5qi86rya93pqyTkDAG2t__6s26GhoK1LKnSPbBcqi2cfiVn8A02_G47nrQU8REa-C-4oC8wITbxnE79seVsSNsg-6pA58AgqgT4WBextocvu9UYIxQo0Hkymge7c870jS6yGtkUxgJR6RH6_HKnKs03tA7yYClVL2l6ObnPSvfXprP2XRs_GJHWm0bDD_0LhX0eCnXSkUdrYV4_LBvAFosluY1t6lmI2NUcO0diZffZZ"
|
|
/>
|
|
<div className="absolute bottom-6 left-6 right-6 p-6 bg-white/5 backdrop-blur-md rounded-lg border border-white/10 pointer-events-none">
|
|
<div className="flex items-start gap-4">
|
|
<div className="w-10 h-10 rounded-full bg-white/10 flex items-center justify-center flex-shrink-0">
|
|
<span className="material-symbols-outlined text-white text-sm">construction</span>
|
|
</div>
|
|
<div>
|
|
<h4 className="text-white font-medium mb-1">On-Site Support</h4>
|
|
<p className="text-gray-400 text-xs leading-relaxed">Technicians dispatched within 2 hours for critical failures in the Bay Area.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Text Content */}
|
|
<div ref={textColRef} className="lg:py-12">
|
|
<span className="text-xs font-semibold uppercase tracking-widest text-gray-500 dark:text-gray-500 mb-2 block">Process</span>
|
|
<h2 className="font-display text-4xl font-medium mb-6 text-gray-900 dark:text-white">
|
|
One consultation to begin,<br/>
|
|
<span className="text-gray-400 dark:text-gray-600">three steps to clarity.</span>
|
|
</h2>
|
|
|
|
<div className="space-y-16 mt-16">
|
|
{[
|
|
{ num: "1", title: "Audit & Assess", desc: "We dive deep into your current infrastructure to identify vulnerabilities and opportunities for optimization." },
|
|
{ num: "2", title: "Implement & Secure", desc: "Our team deploys the necessary hardware and software solutions with minimal disruption to your daily operations." },
|
|
{ num: "3", title: "Monitor & Maintain", desc: "Ongoing 24/7 monitoring ensures problems are solved before you even notice them." }
|
|
].map((step, i) => (
|
|
<div key={i} className="process-step flex gap-6 group cursor-default">
|
|
<div className="flex-shrink-0 mt-1">
|
|
<motion.span
|
|
whileHover={{ scale: 1.2, borderColor: "#3b82f6", color: "#3b82f6" }}
|
|
className="flex items-center justify-center w-8 h-8 rounded border border-gray-300 dark:border-white/20 text-sm font-medium text-gray-500 dark:text-gray-400 transition-colors"
|
|
>
|
|
{step.num}
|
|
</motion.span>
|
|
</div>
|
|
<div>
|
|
<h3 className="text-xl font-medium text-gray-900 dark:text-white group-hover:translate-x-1 transition-transform group-hover:text-blue-500">{step.title}</h3>
|
|
<p className="text-base text-gray-600 dark:text-gray-400 mt-2 leading-relaxed">
|
|
{step.desc}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default Process; |