From 8a4f10340698d27ca3e339d032565832f208b2a6 Mon Sep 17 00:00:00 2001 From: Alex Bilozor Date: Wed, 17 Sep 2025 18:54:00 -0700 Subject: [PATCH 1/3] feat: Add animated masonry layout to hero section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implemented responsive masonry grid layout for hero images - 2 columns on mobile - 3 columns on tablet - 4 columns on desktop - Added smooth scroll animations with varied speeds per column - Duplicated images for seamless infinite scroll effect - Added accessibility support with prefers-reduced-motion - Preserved image aspect ratios using dynamic styles 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/src/app/globals.css | 40 ++++++++ app/src/app/page.tsx | 214 ++++++++++++++++++++++++++++++++++------ 2 files changed, 222 insertions(+), 32 deletions(-) diff --git a/app/src/app/globals.css b/app/src/app/globals.css index e8bbd29..67ec254 100644 --- a/app/src/app/globals.css +++ b/app/src/app/globals.css @@ -185,4 +185,44 @@ .border-brand-orange { border-color: var(--brand-orange); } + + /* Masonry scroll animations */ + @keyframes scroll-up { + 0% { + transform: translateY(0); + } + 100% { + transform: translateY(-50%); + } + } + + .animate-scroll-slow { + animation: scroll-up 80s linear infinite; + } + + .animate-scroll-medium { + animation: scroll-up 70s linear infinite; + } + + .animate-scroll-fast { + animation: scroll-up 60s linear infinite; + } + + .animate-scroll-slower { + animation: scroll-up 90s linear infinite; + } + + .animate-scroll-faster { + animation: scroll-up 50s linear infinite; + } + + @media (prefers-reduced-motion: reduce) { + .animate-scroll-slow, + .animate-scroll-medium, + .animate-scroll-fast, + .animate-scroll-slower, + .animate-scroll-faster { + animation: none; + } + } } diff --git a/app/src/app/page.tsx b/app/src/app/page.tsx index dc7b150..1dd4fcf 100644 --- a/app/src/app/page.tsx +++ b/app/src/app/page.tsx @@ -251,40 +251,190 @@ export default async function HomePage() { } function LandingHero({ images }: { images: Image[] }) { + // Distribute images across columns for masonry effect + const distributeImagesAcrossColumns = (images: Image[], columnCount: number) => { + const columns: Image[][] = Array.from({ length: columnCount }, () => []); + + images.forEach((image, index) => { + const columnIndex = index % columnCount; + columns[columnIndex].push(image); + }); + + return columns; + }; + + // Create different column counts for different screen sizes + // We'll use CSS to show/hide columns based on screen size + const desktopColumns = distributeImagesAcrossColumns(images, 4); + const tabletColumns = distributeImagesAcrossColumns(images, 3); + const mobileColumns = distributeImagesAcrossColumns(images, 2); + + // Animation speed classes for each column + const animationSpeeds = [ + 'animate-scroll-slow', + 'animate-scroll-medium', + 'animate-scroll-fast', + 'animate-scroll-slower' + ]; + return ( -
-
- {images.map((image, index) => ( -
- -
- ))} -
+
+ {/* Desktop Layout - 4 columns */} +
+ {desktopColumns.map((columnImages, columnIndex) => ( +
+ {/* First set of images */} + {columnImages.map((image, index) => ( +
+ +
+ ))} + {/* Duplicate set for seamless loop */} + {columnImages.map((image, index) => ( +
+ +
+ ))} +
+ ))} +
- {/* Content */} -
-

- All Things Web 🚀 -

-

- Discover exciting web development events in the Bay Area and San - Francisco. Join us for hackathons, hangouts, and meetups to connect - with fellow developers and web enthusiasts. -

-
-
+ {/* Tablet Layout - 3 columns */} +
+ {tabletColumns.map((columnImages, columnIndex) => ( +
+ {/* First set of images */} + {columnImages.map((image, index) => ( +
+ +
+ ))} + {/* Duplicate set for seamless loop */} + {columnImages.map((image, index) => ( +
+ +
+ ))} +
+ ))} +
+ + {/* Mobile Layout - 2 columns */} +
+ {mobileColumns.map((columnImages, columnIndex) => ( +
+ {/* First set of images */} + {columnImages.map((image, index) => ( +
+ +
+ ))} + {/* Duplicate set for seamless loop */} + {columnImages.map((image, index) => ( +
+ +
+ ))} +
+ ))} +
+ + {/* Content Overlay */} +
+

+ All Things Web 🚀 +

+

+ Discover exciting web development events in the Bay Area and San + Francisco. Join us for hackathons, hangouts, and meetups to connect + with fellow developers and web enthusiasts. +

+
+
); } From be054ac9e94f57e0e05e7df7a0eea652097521a7 Mon Sep 17 00:00:00 2001 From: Alex Bilozor Date: Wed, 17 Sep 2025 19:16:46 -0700 Subject: [PATCH 2/3] fix: Add safe aspect ratio helper to prevent invalid CSS values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created safeAspectRatio() helper function that validates width/height - Returns fallback ratio "1/1" when dimensions are null/undefined - Logs warning once when missing dimensions are detected - Updated all 6 aspect ratio occurrences in masonry layout - Prevents zero-height tiles and layout breakage This addresses PR feedback about potential invalid aspect ratio values. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/src/app/page.tsx | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/app/src/app/page.tsx b/app/src/app/page.tsx index 1dd4fcf..18fd29f 100644 --- a/app/src/app/page.tsx +++ b/app/src/app/page.tsx @@ -250,6 +250,27 @@ export default async function HomePage() { ); } +// Track if we've already warned about missing dimensions +let missingDimensionsWarned = false; + +// Helper function to safely calculate aspect ratio +function safeAspectRatio( + width: number | null | undefined, + height: number | null | undefined, + fallback = "1/1" +): string { + if (width && height && width > 0 && height > 0) { + return `${width}/${height}`; + } + + if (!missingDimensionsWarned) { + console.warn("Missing or invalid image dimensions detected, using fallback aspect ratio"); + missingDimensionsWarned = true; + } + + return fallback; +} + function LandingHero({ images }: { images: Image[] }) { // Distribute images across columns for masonry effect const distributeImagesAcrossColumns = (images: Image[], columnCount: number) => { @@ -291,7 +312,7 @@ function LandingHero({ images }: { images: Image[] }) {
Date: Wed, 17 Sep 2025 19:22:36 -0700 Subject: [PATCH 3/3] perf: Optimize masonry animation performance and accessibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CSS improvements: - Namespaced keyframes to atw-scroll-up to avoid conflicts - Added will-change and backface-visibility for GPU optimization - Improved animation performance to reduce paint jank Accessibility improvements: - Added aria-hidden="true" to decorative image grids - Prevents screen readers from announcing decorative images Visual improvements: - Added staggered animation delay per column (0s, -5s, -10s, -15s) - Creates more natural, desynchronized scrolling effect Addresses all CSS-related PR review feedback. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/src/app/globals.css | 24 +++++++++++++++++------- app/src/app/page.tsx | 9 ++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/src/app/globals.css b/app/src/app/globals.css index 67ec254..6954860 100644 --- a/app/src/app/globals.css +++ b/app/src/app/globals.css @@ -186,8 +186,8 @@ border-color: var(--brand-orange); } - /* Masonry scroll animations */ - @keyframes scroll-up { + /* Masonry scroll animations - namespaced to avoid conflicts */ + @keyframes atw-scroll-up { 0% { transform: translateY(0); } @@ -197,23 +197,33 @@ } .animate-scroll-slow { - animation: scroll-up 80s linear infinite; + animation: atw-scroll-up 80s linear infinite; + will-change: transform; + backface-visibility: hidden; } .animate-scroll-medium { - animation: scroll-up 70s linear infinite; + animation: atw-scroll-up 70s linear infinite; + will-change: transform; + backface-visibility: hidden; } .animate-scroll-fast { - animation: scroll-up 60s linear infinite; + animation: atw-scroll-up 60s linear infinite; + will-change: transform; + backface-visibility: hidden; } .animate-scroll-slower { - animation: scroll-up 90s linear infinite; + animation: atw-scroll-up 90s linear infinite; + will-change: transform; + backface-visibility: hidden; } .animate-scroll-faster { - animation: scroll-up 50s linear infinite; + animation: atw-scroll-up 50s linear infinite; + will-change: transform; + backface-visibility: hidden; } @media (prefers-reduced-motion: reduce) { diff --git a/app/src/app/page.tsx b/app/src/app/page.tsx index 18fd29f..8296b91 100644 --- a/app/src/app/page.tsx +++ b/app/src/app/page.tsx @@ -301,11 +301,12 @@ function LandingHero({ images }: { images: Image[] }) { return (
{/* Desktop Layout - 4 columns */} -
+