AVP & Sathyam Builders - Corporate Website
A modern React site for a 60-year-old construction firm.
Years in Business
Projects shown
Tech Stack
Tech Stack
The problem
AVP & Sathyam Builders had 60 years of experience constructing hospitals, tech parks, and luxury apartments - but they had no website. When potential clients searched for them online, there was nothing to find. A company with that track record was being passed over simply because it was invisible.
The challenge
AVP & Sathyam Builders had decades of project experience, including Hewlett Packard, Bangalore Baptist Hospital, Software Tech Parks, and luxury apartments, but no digital presence for modern clients. The website needed to present that history, organize commercial and residential work, and turn visits from architects and developers into inquiries.
Frontend Architecture
Single-page React 18 + Vite application with React Router for multi-page navigation. Framer Motion powers scroll-triggered animations across the project pages and hero sections. Radix UI primitives provide accessible modal, dropdown, and tooltip components. Lucide React and Font Awesome 6 handle iconography. Google Fonts serves Epilogue for headings and Manrope for body text. The project data layer is static JSON, so the build can deploy to a CDN without a backend.
Code Walkthrough
Scroll-triggered project card reveal using Framer Motion's whileInView API.
- 01
Step 1 of 3
Scroll-triggered project card reveal with cascading delay
AVP_Sathyam_Builders/src/components/home/ProjectsSection.jsxThe Featured Projects grid has to feel alive as the visitor scrolls in, but firing all cards simultaneously looks cheap and dumps the work onto one frame. Mapping the card index into the transition delay (`delay: index * 0.1`) staggers the entrance into a visual rhythm, and `viewport={{ once: true }}` makes sure the animation only plays the first time it enters view - scrolling back up doesn't re-trigger it.
jsx<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> {projects.map((project, index) => ( <motion.div key={index} initial={{ opacity: 0, y: 30 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.5, delay: index * 0.1 }} className="project-card group" > <div className="aspect-square overflow-hidden"> <img className="w-full h-full object-cover" alt={project.title} loading="lazy" src={project.image} /> </div> <div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/50 to-transparent p-6 flex flex-col justify-end"> <span className="text-accent text-sm font-heading font-semibold uppercase tracking-wider mb-2">{project.category}</span> <h3 className="text-xl font-heading font-semibold mb-2 text-white">{project.title}</h3> <p className="text-white/80 text-sm font-body opacity-0 group-hover:opacity-100 transition-opacity duration-300">{project.description}</p> </div> </motion.div> ))} </div>TakeawayIndex-based transition delays turn a uniform grid into a staggered reveal with no extra state - the map index is already the animation clock.
- 02
Step 2 of 3
Hand-choreographed hero entrance with cascading delays
AVP_Sathyam_Builders/src/components/home/HeroSection.jsxThe hero has four distinct elements - legacy badge, headline, subhead, CTAs - that need to land in sequence so the 60-year brand story reads top-to-bottom, not all at once. Instead of reaching for Framer Motion's `staggerChildren` variants (which would require restructuring the JSX into a parent/child tree), each block is its own `motion.div` with a hand-tuned `delay` - 0s → 0.15s → 0.3s → 0.45s - so the choreography is legible straight from the JSX.
jsx{/* Legacy Badge */} <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6 }} className="mb-6" > <div className="legacy-badge inline-flex"> <Award className="w-4 h-4" /> <span>Building Excellence Since 1965</span> </div> </motion.div> {/* Main Headline */} <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, delay: 0.15 }} > <h1 className="text-white mb-6 font-heading text-4xl md:text-5xl lg:text-6xl xl:text-7xl leading-tight"> 60 Years of Architectural Precision & Craftsmanship </h1> </motion.div> {/* Subheading */} <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, delay: 0.3 }} > <p className="text-white/90 text-lg md:text-xl lg:text-2xl mb-10 font-body leading-relaxed max-w-2xl"> Where Traditional Craftsmanship Meets Modern Solutions </p> </motion.div> {/* CTA Buttons */} <motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.6, delay: 0.45 }} className="flex flex-wrap gap-4" > <button className="btn-primary group"> Explore Our Projects <ArrowRight className="ml-2 h-5 w-5 transition-transform group-hover:translate-x-1" /> </button> </motion.div>TakeawayWhen a hero only animates once, inline delays beat variants - the JSX reads as a storyboard instead of a state machine.
- 03
Step 3 of 3
Route-aware page transitions with AnimatePresence
AVP_Sathyam_Builders/src/components/PageTransition.jsxReact Router swaps routes instantly, which makes a multi-page marketing site feel jarring. Wrapping the outlet in `AnimatePresence mode="wait"` forces the outgoing page to finish its exit animation before the incoming one starts - and keying the `motion.div` on `location.pathname` is what tells Framer Motion that a route change is a remount, not a re-render. The cubic-bezier `[0.0, 0.0, 0.2, 1]` is Material's standard-decelerate curve, which feels natural for incoming content.
jsxconst pageVariants = { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0, transition: { duration: 0.4, ease: [0.0, 0.0, 0.2, 1] } }, exit: { opacity: 0, y: -20, transition: { duration: 0.3 } } }; export const PageTransition = ({ children }) => { const location = useLocation(); return ( <AnimatePresence mode="wait"> <motion.div key={location.pathname} variants={pageVariants} initial="initial" animate="animate" exit="exit" > {children} </motion.div> </AnimatePresence> ); };Takeaway`mode="wait"` + a pathname key is the minimal pattern for route transitions - one wrapper component turns every React Router navigation into a choreographed fade.
Results
The live site at avpsathyambuilders.com presents AVP & Sathyam Builders' full project catalogue - spanning pharmaceutical manufacturing facilities (Wrigley India, US FDA compliant), tech campuses (Hewlett Packard Electronic City), hospitals (Bangalore Baptist Hospital - zero disruption to ongoing operations), and luxury residential projects - with animated storytelling and a structured inquiry flow for new client enquiries.
Interested in this work?
I can walk through the architecture and code during an interview.