From af8f99feb09061ca04350343893e4477804396fe Mon Sep 17 00:00:00 2001 From: Nathan Cummins Date: Fri, 16 Oct 2026 09:17:58 +1030 Subject: [PATCH] Only hero images for CTA carousels, honour reduced motion --- src/components/ImageCarousel.astro | 63 +++++++++++++++++------------- src/pages/index.astro | 22 ++++++----- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/components/ImageCarousel.astro b/src/components/ImageCarousel.astro index 725f7cd..60d9e6e 100644 --- a/src/components/ImageCarousel.astro +++ b/src/components/ImageCarousel.astro @@ -20,6 +20,7 @@ interface Props { quality?: number; height?: number; shuffle?: boolean; + eagerCount?: number; } const { @@ -37,7 +38,8 @@ const { foregroundOpacity = "opacity-50", quality = "80", height, - shuffle = false + shuffle = false, + eagerCount = 2 } = Astro.props as Props; const imagesArray = shuffle ? shuffleArray(images) : images; @@ -64,7 +66,7 @@ const imagesArray = shuffle ? shuffleArray(images) : images; alt={ typeof altText === "function" ? altText(index) : (altText ?? "") } - loading={index < 2 ? "eager" : "lazy"} + loading={index < eagerCount ? "eager" : "lazy"} aria-hidden={index === 0 ? "false" : "true"} layout="full-width" fit="cover" @@ -91,34 +93,41 @@ const imagesArray = shuffle ? shuffleArray(images) : images; > let currentIndex = 0; - setInterval(() => { - const current = document.querySelectorAll( - `[data-id="${carouselId}-${currentIndex}"]` - )[0]; - const nextIndex = (currentIndex + 1) % imageCount; - const next = document.querySelectorAll( - `[data-id="${carouselId}-${nextIndex}"]` - )[0]; + // Honour reduced-motion: leave the first image showing, no rotation. + const reduceMotion = window.matchMedia( + "(prefers-reduced-motion: reduce)" + ).matches; - if (!current || !next) return; + if (!reduceMotion) { + setInterval(() => { + const current = document.querySelectorAll( + `[data-id="${carouselId}-${currentIndex}"]` + )[0]; + const nextIndex = (currentIndex + 1) % imageCount; + const next = document.querySelectorAll( + `[data-id="${carouselId}-${nextIndex}"]` + )[0]; - // Fade out current, fade in next - current.classList.add("opacity-0"); - current.setAttribute("aria-hidden", "true"); + if (!current || !next) return; - next.classList.remove("opacity-0", "hidden"); - next.setAttribute("aria-hidden", "false"); + // Fade out current, fade in next + current.classList.add("opacity-0"); + current.setAttribute("aria-hidden", "true"); - const preloadIndex = (nextIndex + 1) % imageCount; - const preloadImage = document.querySelectorAll( - `[data-id="${carouselId}-${preloadIndex}"]` - )[0]; - if (preloadImage) { - preloadImage.classList.add("opacity-0"); - preloadImage.classList.remove("hidden"); - preloadImage.setAttribute("aria-hidden", "true"); - } + next.classList.remove("opacity-0", "hidden"); + next.setAttribute("aria-hidden", "false"); - currentIndex = nextIndex; - }, interval); + const preloadIndex = (nextIndex + 1) % imageCount; + const preloadImage = document.querySelectorAll( + `[data-id="${carouselId}-${preloadIndex}"]` + )[0]; + if (preloadImage) { + preloadImage.classList.add("opacity-0"); + preloadImage.classList.remove("hidden"); + preloadImage.setAttribute("aria-hidden", "true"); + } + + currentIndex = nextIndex; + }, interval); + } diff --git a/src/pages/index.astro b/src/pages/index.astro index 6e75e85..a607a6e 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -17,28 +17,28 @@ import { Image } from "astro:assets"; import { Icon } from "astro-icon/components"; -import { convertEagerImagesImportGlobToArray } from "@lib/utils"; +import { + convertEagerImagesImportGlobToArray, + getProjectHero +} from "@lib/utils"; const heroImages = import.meta.glob<{ default: ImageMetadata; eager: true }>( "../assets/img/hero/*" ); const heroImagesArray = await convertEagerImagesImportGlobToArray(heroImages); -const imagesForCTA = import.meta.glob<{ default: ImageMetadata; eager: true }>([ - "../assets/img/hero/*", - "../assets/img/project-heros/*", - "../assets/img/projects/**" -]); - -const imagesForCTAArray = - await convertEagerImagesImportGlobToArray(imagesForCTA); - const projects = ( await getCollection("projects", ({ data }) => data.frontPage) ).sort( (a, b) => (a.data.frontPage?.order || 0) - (b.data.frontPage?.order || 1) ); +// Curated carousel set: site heroes + heroes of featured projects only, instead of every image in the repo. Shared by both CTA carousels. +const imagesForCTAArray = [ + ...heroImagesArray, + ...projects.map((project) => getProjectHero(project)) +]; + const awards = (await getCollection("awards")).sort( (a, b) => b.data.date.valueOf() - a.data.date.valueOf() ); @@ -120,6 +120,7 @@ const tracks = ( quality={5} height={192} shuffle={true} + eagerCount={0} >