From 1a85f0eb2df97b4a60dd5a93c8fd559694c54677 Mon Sep 17 00:00:00 2001 From: knuthtimo-lab Date: Thu, 15 Jan 2026 18:27:22 +0100 Subject: [PATCH] feat: Initialize project with Vite and React 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. --- .gitignore | 24 ++++++ App.tsx | 74 +++++++++++++++++ README.md | 25 ++++-- components/BackToTop.tsx | 45 +++++++++++ components/Blog.tsx | 131 ++++++++++++++++++++++++++++++ components/Contact.tsx | 86 ++++++++++++++++++++ components/Features.tsx | 82 +++++++++++++++++++ components/Footer.tsx | 73 +++++++++++++++++ components/Hero.tsx | 88 +++++++++++++++++++++ components/Mission.tsx | 57 +++++++++++++ components/Navbar.tsx | 81 +++++++++++++++++++ components/Process.tsx | 153 +++++++++++++++++++++++++++++++++++ components/Services.tsx | 167 +++++++++++++++++++++++++++++++++++++++ index.html | 86 ++++++++++++++++++++ index.tsx | 15 ++++ metadata.json | 5 ++ package.json | 24 ++++++ tsconfig.json | 29 +++++++ vite.config.ts | 23 ++++++ 19 files changed, 1260 insertions(+), 8 deletions(-) create mode 100644 .gitignore create mode 100644 App.tsx create mode 100644 components/BackToTop.tsx create mode 100644 components/Blog.tsx create mode 100644 components/Contact.tsx create mode 100644 components/Features.tsx create mode 100644 components/Footer.tsx create mode 100644 components/Hero.tsx create mode 100644 components/Mission.tsx create mode 100644 components/Navbar.tsx create mode 100644 components/Process.tsx create mode 100644 components/Services.tsx create mode 100644 index.html create mode 100644 index.tsx create mode 100644 metadata.json create mode 100644 package.json create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/App.tsx b/App.tsx new file mode 100644 index 0000000..7719945 --- /dev/null +++ b/App.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useRef } from 'react'; +import Lenis from '@studio-freight/lenis'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; +import { ScrollToPlugin } from 'gsap/ScrollToPlugin'; +import Navbar from './components/Navbar'; +import Hero from './components/Hero'; +import Mission from './components/Mission'; +import Services from './components/Services'; +import Process from './components/Process'; +import Features from './components/Features'; +import Blog from './components/Blog'; +import Contact from './components/Contact'; +import Footer from './components/Footer'; +import BackToTop from './components/BackToTop'; + +// Register GSAP plugins globally +gsap.registerPlugin(ScrollTrigger, ScrollToPlugin); + +// Grain Overlay Component +const GrainOverlay = () => ( +
+); + +export default function App() { + useEffect(() => { + // Initialize Lenis for smooth scrolling + const lenis = new Lenis({ + duration: 1.2, + easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), + direction: 'vertical', + gestureDirection: 'vertical', + smooth: true, + smoothTouch: false, + touchMultiplier: 2, + } as any); + + // Synchronize Lenis with GSAP ScrollTrigger + lenis.on('scroll', ScrollTrigger.update); + + const ticker = (time: number) => { + lenis.raf(time * 1000); + }; + + // Use GSAP ticker for smoother animation loop integration + gsap.ticker.add(ticker); + + // Disable lag smoothing to prevent jumps + gsap.ticker.lagSmoothing(0); + + return () => { + gsap.ticker.remove(ticker); + lenis.destroy(); + }; + }, []); + + return ( +
+ + +
+ + + + + + + +
+
+ +
+ ); +} \ No newline at end of file diff --git a/README.md b/README.md index 2241000..b2fc626 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,20 @@
- GHBanner - -

Built with AI Studio

- -

The fastest path from prompt to production with Gemini.

- - Start building -
+ +# Run and deploy your AI Studio app + +This contains everything you need to run your app locally. + +View your app in AI Studio: https://ai.studio/apps/drive/19QGHBMfoMD7dqVbMKC_4jTa_Dco6mOuv + +## Run Locally + +**Prerequisites:** Node.js + + +1. Install dependencies: + `npm install` +2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key +3. Run the app: + `npm run dev` diff --git a/components/BackToTop.tsx b/components/BackToTop.tsx new file mode 100644 index 0000000..feeb5fd --- /dev/null +++ b/components/BackToTop.tsx @@ -0,0 +1,45 @@ +import React, { useEffect, useState } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import gsap from 'gsap'; + +const BackToTop: React.FC = () => { + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const toggleVisibility = () => { + if (window.scrollY > 500) { + setIsVisible(true); + } else { + setIsVisible(false); + } + }; + + window.addEventListener('scroll', toggleVisibility); + return () => window.removeEventListener('scroll', toggleVisibility); + }, []); + + const scrollToTop = () => { + gsap.to(window, { duration: 1.2, scrollTo: 0, ease: "power3.inOut" }); + }; + + return ( + + {isVisible && ( + + arrow_upward + + )} + + ); +}; + +export default BackToTop; \ No newline at end of file diff --git a/components/Blog.tsx b/components/Blog.tsx new file mode 100644 index 0000000..3d84071 --- /dev/null +++ b/components/Blog.tsx @@ -0,0 +1,131 @@ +import React, { useRef, useLayoutEffect } from 'react'; +import { motion } from 'framer-motion'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); + +const posts = [ + { + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuARalmRkuoZMBAbavGQgx4a-JhLgXBJ6JSD0U4vycdwaGGV3d-ffUFrdbx2lIbKrYCmS100i7VJ0w5cDHITXYV6w1-pSUPHKL7Jik__TWOIYOnq_4ND5ri7l8SQoaJdjJK9jhYvtxdxrZm6j8t8BNAjvPTaUdUDo4C7QVqcx1KbGvup6cpF8vY1LJ82S_5OMAZ6JgH0rK5bvWpqD3WqPhtqJCUB6d_1gUvluKjotwnNQ03t1dSYV8HOtRrLE83j6i_wgL4GZ0XTsMZb', + date: 'Oct 12, 2024', + category: 'Cybersecurity', + title: 'The Hidden Risks of Remote Work', + excerpt: 'As remote work becomes permanent, new vulnerabilities emerge. Learn how to secure your distributed workforce effectively.' + }, + { + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCz5lTYjY4RNXubQlrA-BtLIGR3nUY8ULkD9omwT5FShfdMrbMgS5dDCyfN3xiB5WC7T3vjNvyvVbvnD0G1zBpbNTjfOYyhmAEfno7Hf5W1sm-KYRXYrLGQq-c6TkLgEf0i9JGNvuFZ6edcenr2o39dCzIPXcp_z9XWOIzp7kBX2EydNPLJoRofVYuSTmEA1y0_xh4sdiRy1PykRASGLhKfN19_XLNuwyTBVKYISY7cHc-An69eZpAfhrvngu3E47rU6KuQS0k3QXBZ', + date: 'Sep 28, 2024', + category: 'Cloud Infrastructure', + title: 'Migrating to the Cloud: A Step-by-Step Guide', + excerpt: 'Thinking about moving your data? Here is a comprehensive checklist to ensure a smooth and secure transition.' + }, + { + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCl5iOhTsCqcHnho89DkoLh0DYeuvef0pdp8k26NKzcAq7YPvWbAYARg9mCIvqGTxQGradp8zvscuuibskpz4W_nEzQQO1z7lgwKJ1Xxiw_yQOyXMLfoRNLTHXzqFUH8Q5daCAfYTb7Zl3sFjB7k8i44D6TGolzqrN05Db27Abf2TWDDzHpVSrNml4zddvxholHFxMzqDeSzQ5p77SLDSFNaYBZGR2lEdN2V9O0GzMqxbOjFmBGMW48nlrEDLDzYGv_gWI3RSqNqBl-', + date: 'Sep 15, 2024', + category: 'Innovation', + title: 'AI in Business: Beyond the Hype', + excerpt: 'Artificial Intelligence is transforming industries. Discover practical applications that can drive efficiency in your business today.' + } +]; + +const Blog: React.FC = () => { + const containerRef = useRef(null); + const imagesRef = useRef<(HTMLDivElement | null)[]>([]); + imagesRef.current = []; + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + imagesRef.current.forEach((imgWrapper) => { + if (!imgWrapper) return; + + gsap.to(imgWrapper, { + yPercent: 30, + ease: "none", + scrollTrigger: { + trigger: imgWrapper.closest('article'), + start: "top bottom", + end: "bottom top", + scrub: true + } + }); + }); + }, containerRef); + + return () => ctx.revert(); + }, []); + + return ( + +
+
+
+ Latest Insights +

+ Knowledge base. +

+
+ + View all posts arrow_forward + +
+ +
+ {posts.map((post, i) => ( + +
+
{ if(el) imagesRef.current.push(el); }} + className="w-full h-[140%] -mt-[20%]" + > + {post.title} +
+
+
+ Read +
+
+
+ {post.date} + + {post.category} +
+

+ {post.title} +

+

+ {post.excerpt} +

+
+ ))} +
+
+
+ ); +}; + +export default Blog; \ No newline at end of file diff --git a/components/Contact.tsx b/components/Contact.tsx new file mode 100644 index 0000000..f870e9f --- /dev/null +++ b/components/Contact.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Contact: React.FC = () => { + return ( + +
+ +

+ Get in Touch +

+

+ Ready to elevate your IT infrastructure? Fill out the form below and we'll get back to you shortly. +

+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + Send Message + +
+
+
+
+ ); +}; + +export default Contact; \ No newline at end of file diff --git a/components/Features.tsx b/components/Features.tsx new file mode 100644 index 0000000..f5d6d26 --- /dev/null +++ b/components/Features.tsx @@ -0,0 +1,82 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Features: React.FC = () => { + const features = [ + { + icon: 'verified_user', + title: 'Certified Experts', + desc: 'Our team holds top-tier certifications from Microsoft, Cisco, and AWS, ensuring you get world-class expertise.', + color: 'blue' + }, + { + icon: 'rocket_launch', + title: 'Rapid Response', + desc: 'Time is money. We guarantee a 15-minute initial response time for critical issues to keep you moving.', + color: 'purple' + }, + { + icon: 'handshake', + title: 'Dedicated Partnership', + desc: "We don't just fix computers; we align IT strategy with your business goals for long-term success.", + color: 'emerald' + } + ]; + + const getColorClasses = (color: string) => { + switch(color) { + case 'blue': return 'bg-blue-100 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400'; + case 'purple': return 'bg-purple-100 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400'; + case 'emerald': return 'bg-emerald-100 dark:bg-emerald-900/30 text-emerald-600 dark:text-emerald-400'; + default: return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400'; + } + }; + + return ( + +
+
+ Why Choose Us +

+ Built on trust. Driven by excellence. +

+
+ +
+ {features.map((feature, i) => ( + + + {feature.icon} + +

{feature.title}

+

+ {feature.desc} +

+
+ ))} +
+
+
+ ); +}; + +export default Features; \ No newline at end of file diff --git a/components/Footer.tsx b/components/Footer.tsx new file mode 100644 index 0000000..c2de0ee --- /dev/null +++ b/components/Footer.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Footer: React.FC = () => { + return ( +
+
+
+
+
+ dns + Bay Area Affiliates +
+

+ Track every move, analyze your performance, and get real-time coaching. Your dedicated IT partner in the Bay Area. +

+
+ {['X', 'in', 'fb'].map((social) => ( + + {social} + + ))} +
+
+ +
+

Navigation

+
    + {['About', 'Features', 'Testimonials', 'Pricing'].map((item) => ( +
  • + + {item} + +
  • + ))} +
+
+ +
+

Contact

+
    +
  • support@bayareaaffiliates.com
  • +
  • (361) 765-8400
  • +
  • 123 Market St, San Francisco, CA
  • +
  • FAQ
  • +
+
+
+ +
+

+ © 2024 Bay Area Affiliates, Inc. All rights reserved. +

+
+ Privacy Policy + Terms of Service +
+
+
+
+ ); +}; + +export default Footer; \ No newline at end of file diff --git a/components/Hero.tsx b/components/Hero.tsx new file mode 100644 index 0000000..5e43de6 --- /dev/null +++ b/components/Hero.tsx @@ -0,0 +1,88 @@ +import React, { useRef, useLayoutEffect } from 'react'; +import { motion } from 'framer-motion'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); + +const Hero: React.FC = () => { + const containerRef = useRef(null); + const imageRef = useRef(null); + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + // Parallax Background + gsap.to(imageRef.current, { + yPercent: 30, + ease: "none", + scrollTrigger: { + trigger: containerRef.current, + start: "top top", + end: "bottom top", + scrub: true + } + }); + + // Text Stagger Animation + gsap.fromTo(".hero-stagger", + { y: 50, opacity: 0 }, + { y: 0, opacity: 1, duration: 1, stagger: 0.2, ease: "power3.out", delay: 0.2 } + ); + }, containerRef); + + return () => ctx.revert(); + }, []); + + return ( +
+
+ Abstract dark technology background +
+
+
+ +
+
+ + Established 1998 + +
+ +

+ Reliable IT Services
+ for Over 25 Years. +

+ +

+ Bay Area Affiliates is your silent partner in technology. We provide the infrastructure that whispers clarity, ensures uptime, and guides your business growth. +

+ +
+ + Explore Services + + + Get a Consultation + +
+
+
+ ); +}; + +export default Hero; \ No newline at end of file diff --git a/components/Mission.tsx b/components/Mission.tsx new file mode 100644 index 0000000..acd8ad1 --- /dev/null +++ b/components/Mission.tsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const Mission: React.FC = () => { + return ( + +
+
+ +
+ + Our Mission +
+

+ Harness invisible power to operate faster, focus deeper, and scale effortlessly. +

+
+ + +

+ Technology shouldn't be a hurdle; it should be the wind at your back. From seamless cloud migrations to robust cybersecurity, we handle the complexities so you can focus on what matters most: your business. +

+
+ + 99.9% + Uptime Guarantee + + + 24/7 + Support Availability + +
+
+
+
+
+ ); +}; + +export default Mission; \ No newline at end of file diff --git a/components/Navbar.tsx b/components/Navbar.tsx new file mode 100644 index 0000000..45735db --- /dev/null +++ b/components/Navbar.tsx @@ -0,0 +1,81 @@ +import React, { useState } from 'react'; +import { motion, useScroll, useMotionValueEvent, useSpring } from 'framer-motion'; + +const Navbar: React.FC = () => { + const [hidden, setHidden] = useState(false); + const { scrollY, scrollYProgress } = useScroll(); + + const scaleX = useSpring(scrollYProgress, { + stiffness: 100, + damping: 30, + restDelta: 0.001 + }); + + useMotionValueEvent(scrollY, "change", (latest) => { + const previous = scrollY.getPrevious() || 0; + if (latest > previous && latest > 150) { + setHidden(true); + } else { + setHidden(false); + } + }); + + return ( + +
+
+ + dns + + Bay Area Affiliates +
+ +
+ {['Services', 'Features', 'Blog', 'Contact'].map((item) => ( + + {item} + + + ))} +
+ + + Client Portal + +
+ + {/* Scroll Progress Indicator */} + +
+ ); +}; + +export default Navbar; \ No newline at end of file diff --git a/components/Process.tsx b/components/Process.tsx new file mode 100644 index 0000000..51b76b1 --- /dev/null +++ b/components/Process.tsx @@ -0,0 +1,153 @@ +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(null); + const imageColRef = useRef(null); + const imgRef = useRef(null); + const textColRef = useRef(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 ( +
+
+
+ + {/* Image Column - Will be pinned */} +
+ Close up of server lights in dark room +
+
+
+ construction +
+
+

On-Site Support

+

Technicians dispatched within 2 hours for critical failures in the Bay Area.

+
+
+
+
+ + {/* Text Content */} +
+ Process +

+ One consultation to begin,
+ three steps to clarity. +

+ +
+ {[ + { 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) => ( +
+
+ + {step.num} + +
+
+

{step.title}

+

+ {step.desc} +

+
+
+ ))} +
+
+ +
+
+
+ ); +}; + +export default Process; \ No newline at end of file diff --git a/components/Services.tsx b/components/Services.tsx new file mode 100644 index 0000000..340e1da --- /dev/null +++ b/components/Services.tsx @@ -0,0 +1,167 @@ +import React, { useState, useRef, useLayoutEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import gsap from 'gsap'; +import { ScrollTrigger } from 'gsap/ScrollTrigger'; + +gsap.registerPlugin(ScrollTrigger); + +const servicesData = [ + { + id: 1, + category: 'IT Infrastructure', + title: 'Windows 11 Transition', + description: 'Seamless upgrades for your entire fleet. We handle compatibility checks, data backup, and deployment so your workflow never stutters.', + icon: 'desktop_windows', + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBMpd_cFINnFibfNErBs8OVAAyDQYTRXix88YH91QImuGi11XGwlY_QUB2R9htcC1h_fTXUeftdEieGT-oi5p5TBjpAyW-86mSsXu-rqhRTBsJlAGuE37bxJES4DUayktXIToEcF-M4PyXdyyTPIYtpYrxK18b2-sPwMzuzCL0LpgJwd5EoYxAkrJQ7W4eBrIG2e9Cw9sY0dJpXJy-TRgwBG0nk-S7W4Y0s3U9w--AzE4fcUimeGMqWwdCncU5tnETmkrkDNFiCyKSA' + }, + { + id: 2, + category: 'Security', + title: 'Web Services & Security', + description: 'From hosting to rigorous penetration testing. Secure your digital storefront with enterprise-grade protection and 99.9% uptime.', + icon: 'security', + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCxibXNCB5mU7MdWE5znMWnQUc9-d2ZoYF7LXK1CMssnvaFz2ZsGzyxXMbqDmely-UfxapqILD5-Exeo1wlQZKg8T2MK4vjlyAMaehoJoqTy2hHh8rxj46i8CKb4-ILL2JswBc98nJt_Fo1DfcDH0dHH5Zz6H4R2Jm1deViSW8Sp2zNp1sTc4eRHy1URiSRQFcr1C8rca6dKiuNDuyDiUmmesqHobXGItaBeFjJC-0OatWpKbr0zF-Y5qvk9Yl5FY2KUcDY9AcTfelu' + }, + { + id: 3, + category: 'Consulting', + title: 'Performance Upgrades', + description: 'Is your hardware holding you back? We analyze bottlenecks and implement strategic upgrades to memory, storage, and networks.', + icon: 'speed', + image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuBs2fGGwp4EkMarA9Uvy7IOqyW0Pzxzt-94Bsr8Tkbem4uHPq-vMEmGgKuEmds2zKwPrw2nVcvL3MjjKYWieLSLh5pVUbbK6T9aDxt2xhvo4trARZobhzoQCJfI-r6aGW_aqfwC5XxOr9VA3YdnNnYEgkfW_TWrUWYa6mD8X0KdVG3sLimA8p7qWxIqUzFFV82twn60rP4OwLdIsc6t1OGnJzjemxL1Aw05aDo6Ckfr0a1oZ2kD4xKeTkG--zUhezvXB9I03l6f3b46' + } +]; + +const categories = ['All', 'IT Infrastructure', 'Web Development', 'Consulting', 'Security']; + +const Services: React.FC = () => { + const [activeCategory, setActiveCategory] = useState('All'); + const containerRef = useRef(null); + const imagesRef = useRef<(HTMLDivElement | null)[]>([]); + + // Reset refs on render to handle filtering updates + imagesRef.current = []; + + const filteredServices = activeCategory === 'All' + ? servicesData + : servicesData.filter(s => s.category === activeCategory || (activeCategory === 'Web Development' && s.category === 'Security')); + + useLayoutEffect(() => { + const ctx = gsap.context(() => { + imagesRef.current.forEach((imgWrapper) => { + if (!imgWrapper) return; + + gsap.to(imgWrapper, { + yPercent: 30, + ease: "none", + scrollTrigger: { + trigger: imgWrapper.closest('.group'), + start: "top bottom", + end: "bottom top", + scrub: true + } + }); + }); + }, containerRef); + + return () => ctx.revert(); + }, [filteredServices]); + + return ( + +
+
+ Core Offerings +

+ Different paths to explore all guided by one expert team. +

+
+ +
+ {categories.map((cat) => ( + + ))} +
+ +
+ + {filteredServices.map((service) => ( + + {/* Image Container */} +
+ {/* Parallax Wrapper */} +
{ if (el) imagesRef.current.push(el); }} + className="w-full h-[140%] -mt-[20%]" + > + {service.title} +
+
+
+ +
+ + {service.icon} + +

{service.title}

+

+ {service.description} +

+ + Learn More arrow_forward + +
+
+ ))} +
+
+
+
+ ); +}; + +export default Services; \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..a4ef490 --- /dev/null +++ b/index.html @@ -0,0 +1,86 @@ + + + + + + Bay Area Affiliates, Inc. - Modern IT Solutions + + + + + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/index.tsx b/index.tsx new file mode 100644 index 0000000..6ca5361 --- /dev/null +++ b/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +const rootElement = document.getElementById('root'); +if (!rootElement) { + throw new Error("Could not find root element to mount to"); +} + +const root = ReactDOM.createRoot(rootElement); +root.render( + + + +); \ No newline at end of file diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..02e9eee --- /dev/null +++ b/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Bay Area Affiliates", + "description": "Modern IT Solutions corporate website featuring smooth GSAP scrolling, Framer Motion animations, and a premium dark-mode aesthetic.", + "requestFramePermissions": [] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..8e5f27c --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "bay-area-affiliates", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.3", + "react-dom": "^19.2.3", + "framer-motion": "^12.26.2", + "gsap": "^3.14.2", + "@studio-freight/lenis": "^1.0.42" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^5.0.0", + "typescript": "~5.8.2", + "vite": "^6.2.0" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c6eed5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ES2022", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + "types": [ + "node" + ], + "moduleResolution": "bundler", + "isolatedModules": true, + "moduleDetection": "force", + "allowJs": true, + "jsx": "react-jsx", + "paths": { + "@/*": [ + "./*" + ] + }, + "allowImportingTsExtensions": true, + "noEmit": true + } +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..ee5fb8d --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,23 @@ +import path from 'path'; +import { defineConfig, loadEnv } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, '.', ''); + return { + server: { + port: 3000, + host: '0.0.0.0', + }, + plugins: [react()], + define: { + 'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY), + 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY) + }, + resolve: { + alias: { + '@': path.resolve(__dirname, '.'), + } + } + }; +});