@@ -9,7 +9,7 @@ const HeartIcon = ({ isWishlisted = false, animate = false, className = 'h-6 w-6
99 viewBox = "0 0 18 17"
1010 fill = "none"
1111 xmlns = "http://www.w3.org/2000/svg"
12- className = { `${ className } transition-transform duration-200 ease-out ${ animate ? 'animate-double-pop' : ( isWishlisted ? 'scale-110' : '' ) } ${ isWishlisted ? 'text-white' : 'text-current' } ` }
12+ className = { `${ className } transition-transform duration-200 ease-out ${ animate ? 'animate-double-pop' : ( isWishlisted ? 'scale-110' : '' ) } ${ isWishlisted ? 'text-white' : 'text-current' } ` }
1313 aria-hidden = "true"
1414 focusable = "false"
1515 >
@@ -96,20 +96,21 @@ const ProductCard = forwardRef(({ product }, ref) => {
9696 }
9797
9898 return (
99- < div
99+ < div
100100 ref = { ref }
101- className = "bg-white rounded-2xl p-4 transition-all duration-300 shadow-[0_8px_30px_rgb(0,0,0,0.15)] hover:shadow-[0_8px_30px_rgb(0,0,0,0.25)] flex flex-col select-none"
101+ className = "bg-white rounded-2xl p-3 sm:p- 4 transition-all duration-300 shadow-[0_8px_30px_rgb(0,0,0,0.15)] hover:shadow-[0_8px_30px_rgb(0,0,0,0.25)] flex flex-col select-none max-w-full "
102102 >
103103 { /* --- IMAGE CONTAINER --- */ }
104- < div
105- className = "relative aspect-square overflow-hidden rounded-xl mb-4 bg-gray-100 group cursor-pointer"
106- role = "button"
107- tabIndex = { 0 }
108- aria-label = { `View details for ${ product . name } ` }
109- onClick = { handleProductClick }
110- onKeyDown = { ( e ) => ( e . key === 'Enter' || e . key === ' ' ) && handleProductClick ( ) }
111- >
112-
104+ < div
105+ className = "relative w-full overflow-hidden rounded-xl mb-3 bg-gray-100 group cursor-pointer"
106+ style = { { paddingTop : '100%' } }
107+ role = "button"
108+ tabIndex = { 0 }
109+ aria-label = { `View details for ${ product . name } ` }
110+ onClick = { handleProductClick }
111+ onKeyDown = { ( e ) => ( e . key === 'Enter' || e . key === ' ' ) && handleProductClick ( ) }
112+ >
113+
113114 { /* Badges for NEW and SALE */ }
114115 < div className = "absolute top-3 left-3 z-10 flex flex-col gap-2" >
115116 { product . isNew && (
@@ -123,7 +124,7 @@ const ProductCard = forwardRef(({ product }, ref) => {
123124 </ span >
124125 ) }
125126 </ div >
126-
127+
127128 { /* State 1: Image is loading */ }
128129 { ! imageLoaded && ! imageError && (
129130 < div className = "absolute inset-0 bg-gradient-to-br from-gray-100 to-gray-200 animate-pulse flex items-center justify-center" >
@@ -146,7 +147,7 @@ const ProductCard = forwardRef(({ product }, ref) => {
146147 < img
147148 src = { product . imageUrl || 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgdmlld0JveD0iMCAwIDMwMCAzMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMDAiIGhlaWdodD0iMzAwIiBmaWxsPSIjRjNGNEY2Ii8+CjxwYXRoIGQ9Ik0xMjUgMTI1SDEzNVYxMzVIMTI1VjEyNVpNMTM1IDEyNUgxNDVWMTM1SDEzNVYxMjVaTTE0NSAxMjVIMTU1VjEzNUgxNDVWMTI1Wk0xNTUgMTI1SDE2NVYxMzVIMTU1VjEyNVpNMTY1IDEyNUgxNzVWMTM1SDE2NVYxMjVaIiBmaWxsPSIjOUI5QkEzIi8+CjxwYXRoIGQ9Ik0xMzUgMTQ1SDE0NVYxNTVIMTM1VjE0NVpNMTQ1IDE0NUgxNTVWMTU1SDE0NVYxNDVaTTE1NSAxNDVIMTY1VjE1NUgxNTVWMTQ1WiIgZmlsbD0iIzlCOUJBMyIvPgo8L3N2Zz4K' }
148149 alt = { product . name }
149- className = { `w-full h-full object-contain transition-opacity duration-500 ${ imageLoaded && ! imageError ? 'opacity-100' : 'opacity-0' } ` }
150+ className = { `absolute inset-0 w-full h-full object-cover transition-opacity duration-500 ${ imageLoaded && ! imageError ? 'opacity-100' : 'opacity-0' } ` }
150151 onLoad = { ( ) => setImageLoaded ( true ) }
151152 onError = { ( ) => {
152153 setImageLoaded ( true ) ;
@@ -185,7 +186,7 @@ const ProductCard = forwardRef(({ product }, ref) => {
185186 < span className = "font-bold text-lg text-gray-800" > { formatPrice ( product . price ) } </ span >
186187 </ div >
187188 </ div >
188-
189+
189190 < div className = "mb-2" >
190191 < h3 className = "font-bold text-2xl text-gray-800 leading-tight line-clamp-2" > { product . name } </ h3 >
191192 </ div >
@@ -216,15 +217,15 @@ const ProductCard = forwardRef(({ product }, ref) => {
216217 </ option >
217218 ) ) }
218219 </ select >
219- < div className = "absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none" >
220- < svg className = "w-4 h-4 text-gray-500 " fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
220+ < div className = "absolute inset-y-0 right-3 flex items-center pointer-events-none" >
221+ < svg className = "w-4 h-4 text-gray-400 " fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" aria-hidden >
221222 < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M19 9l-7 7-7-7" />
222223 </ svg >
223224 </ div >
224225 </ div >
225226 ) }
226227 </ div >
227-
228+
228229 < div className = "flex items-center gap-3 mt-auto" >
229230 < button
230231 onClick = { ( e ) => handleActionClick ( e , handleWishlistToggle ) }
@@ -238,40 +239,40 @@ const ProductCard = forwardRef(({ product }, ref) => {
238239 < HeartIcon isWishlisted = { isWishlisted } animate = { animateLike } className = "h-5 w-5" />
239240 </ button >
240241
241- { /* --- Add to Cart Button --- */ }
242- < button
243- onClick = { ( e ) => handleActionClick ( e , handleAddToCart ) }
244- className = { `
242+ { /* --- Add to Cart Button --- */ }
243+ < button
244+ onClick = { ( e ) => handleActionClick ( e , handleAddToCart ) }
245+ className = { `
245246 -ml-px flex-grow flex items-center justify-center gap-2
246247 bg-[#023e8a] text-white font-medium
247248 py-3 px-4 rounded-r-xl
248249 hover:bg-[#1054ab] transition-colors duration-150 hover:shadow-lg cursor-pointer
249250 focus:outline-none focus:z-10
250251 ` }
251- aria-live = "polite"
252- >
253- { cartLoading ? (
254- < span className = "ml-2 flex items-center gap-2 font-semibold" >
255- < svg className = "w-5 h-5 animate-spin" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" aria-hidden = "true" >
256- < circle cx = "12" cy = "12" r = "10" strokeWidth = "3" stroke = "currentColor" opacity = "0.25" />
257- < path d = "M22 12a10 10 0 00-10-10" strokeWidth = "3" strokeLinecap = "round" strokeLinejoin = "round" />
258- </ svg >
259- < span > ADDING...</ span >
260- </ span >
261- ) : cartAdded ? (
262- < span className = "ml-2 flex items-center gap-2 font-semibold" >
263- < svg className = "w-5 h-5" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" aria-hidden = "true" >
264- < path strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" d = "M5 13l4 4L19 7" />
265- </ svg >
266- < span > ADDED</ span >
267- </ span >
268- ) : (
269- < >
270- < CartIcon />
271- < span className = "ml-2" > ADD TO CART</ span >
272- </ >
273- ) }
274- </ button >
252+ aria-live = "polite"
253+ >
254+ { cartLoading ? (
255+ < span className = "ml-2 flex items-center gap-2 font-semibold" >
256+ < svg className = "w-5 h-5 animate-spin" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" aria-hidden = "true" >
257+ < circle cx = "12" cy = "12" r = "10" strokeWidth = "3" stroke = "currentColor" opacity = "0.25" />
258+ < path d = "M22 12a10 10 0 00-10-10" strokeWidth = "3" strokeLinecap = "round" strokeLinejoin = "round" />
259+ </ svg >
260+ < span > ADDING...</ span >
261+ </ span >
262+ ) : cartAdded ? (
263+ < span className = "ml-2 flex items-center gap-2 font-semibold" >
264+ < svg className = "w-5 h-5" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" aria-hidden = "true" >
265+ < path strokeWidth = "2" strokeLinecap = "round" strokeLinejoin = "round" d = "M5 13l4 4L19 7" />
266+ </ svg >
267+ < span > ADDED</ span >
268+ </ span >
269+ ) : (
270+ < >
271+ < CartIcon />
272+ < span className = "ml-2" > ADD TO CART</ span >
273+ </ >
274+ ) }
275+ </ button >
275276
276277 </ div >
277278 </ div >
0 commit comments