The source code for our eny.social landing page, which is mirrored in a different repository as part of the CI setup. eny.social
social-network eny local-first
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(Hero): sync image animation to keyword changes

Sam Sauer b642d15b 880cdb26

+23 -20
+23 -20
app/components/Hero.tsx
··· 3 3 import { useState, useEffect } from "react"; 4 4 import NavLink from "./ui/NavLink"; 5 5 6 - const words = ["people", "media", "communities"]; 6 + const slides = [ 7 + { word: "people", image: "/images/pexels-kindelmedia-7148409 1.png" }, 8 + { word: "media", image: "/images/pexels-shvets-production-7533377 1.png" }, 9 + { word: "communities", image: "/images/pexels-guilhermealmeida-1858175.png" }, 10 + ]; 7 11 8 12 export default function Hero() { 9 13 const [currentIndex, setCurrentIndex] = useState(0); 10 14 11 15 useEffect(() => { 12 16 const interval = setInterval(() => { 13 - setCurrentIndex((prev) => (prev + 1) % words.length); 17 + setCurrentIndex((prev) => (prev + 1) % slides.length); 14 18 }, 3000); 15 19 return () => clearInterval(interval); 16 20 }, []); 17 21 18 22 return ( 19 23 <section className="relative min-h-screen px-6 pt-28 pb-20"> 20 - {/* Tangerine blob */} 21 - <svg 24 + {/* Grained star blob */} 25 + <img 26 + src="/shapes/star-grained.svg" 27 + alt="" 22 28 className="absolute -right-20 top-20 h-[500px] w-[500px] opacity-30" 23 - viewBox="0 0 500 500" 24 - fill="none" 25 - > 26 - <path 27 - d="M400 250C400 340 340 420 250 440C160 460 80 400 60 300C40 200 100 100 200 60C300 20 400 100 420 200C430 230 400 250 400 250Z" 28 - fill="var(--tangerine-dream)" 29 - /> 30 - </svg> 29 + /> 31 30 32 31 <div className="relative mx-auto max-w-7xl"> 33 32 <div className="grid gap-12 lg:grid-cols-12"> ··· 40 39 <span className="stage-heading-1 block text-left">Connect</span> 41 40 <span className="stage-heading-1 block text-right">with</span> 42 41 <span className="relative block text-right"> 43 - {words.map((word, index) => ( 42 + {slides.map((slide, index) => ( 44 43 <span 45 - key={word} 44 + key={slide.word} 46 45 className="stage-heading-2 absolute right-0 top-0 transition-all duration-500" 47 46 style={{ 48 47 opacity: currentIndex === index ? 1 : 0, ··· 54 53 : "translateY(20px)", 55 54 }} 56 55 > 57 - {word} 56 + {slide.word} 58 57 </span> 59 58 ))} 60 59 {/* Invisible placeholder for sizing */} ··· 119 118 className="relative w-[160%] aspect-square -mr-[50%]" 120 119 style={{ clipPath: "url(#hero-blob)", transform: "translate(20%, -14%)" }} 121 120 > 122 - <img 123 - src="/images/pexels-kindelmedia-7148409 1.png" 124 - alt="People connecting" 125 - className="h-full w-full object-cover" 126 - /> 121 + {slides.map((slide, index) => ( 122 + <img 123 + key={slide.word} 124 + src={slide.image} 125 + alt={`People ${slide.word}`} 126 + className="absolute inset-0 h-full w-full object-cover transition-opacity duration-500" 127 + style={{ opacity: currentIndex === index ? 1 : 0 }} 128 + /> 129 + ))} 127 130 </div> 128 131 </div> 129 132 </div>