@@ -8,7 +8,7 @@ import styles from './styles.module.css';
88type Project = {
99 title : string ;
1010 description : string ;
11- image : string ;
11+ image : string ; // path under /static, e.g. 'img/foo.png'
1212 link : string ;
1313} ;
1414
@@ -75,34 +75,23 @@ export default function ProjectCarousel(): ReactNode {
7575
7676 useEffect ( ( ) => {
7777 if ( ! isAutoPlaying ) return ;
78-
79- const interval = setInterval ( ( ) => {
80- goToNext ( ) ;
81- } , 5000 ) ;
82-
83- return ( ) => clearInterval ( interval ) ;
78+ const id = setInterval ( goToNext , 5000 ) ;
79+ return ( ) => clearInterval ( id ) ;
8480 } , [ isAutoPlaying , goToNext ] ) ;
8581
86- const handleMouseEnter = useCallback ( ( ) => {
87- setIsAutoPlaying ( false ) ;
88- } , [ ] ) ;
89-
90- const handleMouseLeave = useCallback ( ( ) => {
91- setIsAutoPlaying ( true ) ;
92- } , [ ] ) ;
82+ const active = projects [ currentIndex ] ;
83+ const activeSrc = useBaseUrl ( active . image ) ;
9384
9485 return (
9586 < section className = { styles . carousel } >
9687 < div className = "container" >
9788 < Heading as = "h2" className = { styles . title } > TeXlyre Ecosystem</ Heading >
98- < p className = { styles . subtitle } >
99- Explore the open-source projects that power TeXlyre
100- </ p >
89+ < p className = { styles . subtitle } > Explore the open-source projects that power TeXlyre</ p >
10190
10291 < div
10392 className = { styles . carouselContainer }
104- onMouseEnter = { handleMouseEnter }
105- onMouseLeave = { handleMouseLeave }
93+ onMouseEnter = { ( ) => setIsAutoPlaying ( false ) }
94+ onMouseLeave = { ( ) => setIsAutoPlaying ( true ) }
10695 >
10796 < button
10897 className = { clsx ( styles . navButton , styles . navButtonPrev ) }
@@ -113,31 +102,32 @@ export default function ProjectCarousel(): ReactNode {
113102 </ button >
114103
115104 < div className = { styles . carouselContent } >
116- { projects . map ( ( project , index ) => (
117- < div
118- key = { index }
119- className = { clsx ( styles . slide , {
120- [ styles . slideActive ] : index === currentIndex ,
121- } ) }
105+ { /* Only render the active slide */ }
106+ < div className = { clsx ( styles . slide , styles . slideActive ) } >
107+ < a
108+ href = { active . link }
109+ target = "_blank"
110+ rel = "noopener noreferrer"
111+ className = { styles . slideLink }
122112 >
123- < a
124- href = { project . link }
125- target = "_blank"
126- rel = "noopener noreferrer"
127- className = { styles . slideLink }
128- >
129- < img
130- src = { useBaseUrl ( project . image ) }
131- alt = { project . title }
132- className = { styles . slideImage }
133- />
134- < div className = { styles . slideInfo } >
135- < h3 className = { styles . slideTitle } > { project . title } </ h3 >
136- < p className = { styles . slideDescription } > { project . description } </ p >
137- </ div >
138- </ a >
139- </ div >
140- ) ) }
113+ < img
114+ src = { activeSrc }
115+ alt = { active . title }
116+ className = { styles . slideImage }
117+ decoding = "async"
118+ loading = "eager" // ensure it loads even inside a hidden-ish container
119+ onError = { ( e ) => {
120+ // Helpful in prod if something blocks images unexpectedly
121+ // eslint-disable-next-line no-console
122+ console . warn ( 'Carousel image failed to load:' , ( e . target as HTMLImageElement ) . src ) ;
123+ } }
124+ / >
125+ < div className = { styles . slideInfo } >
126+ < h3 className = { styles . slideTitle } > { active . title } </ h3 >
127+ < p className = { styles . slideDescription } > { active . description } </ p >
128+ </ div >
129+ </ a >
130+ </ div >
141131 </ div >
142132
143133 < button
0 commit comments