Reset carousels on page load, tidy navbar state across transitions, proper preloading for carousels

This commit is contained in:
encie22 committed 2026-10-16 12:32:07 +10:30
1 parent 57c906082f
commit 2001963035
3 files changed
+50 -14

No files matched your search

+43 -11
View File
@@ -43,6 +43,9 @@ const {
} = Astro.props as Props;
const imagesArray = shuffle ? shuffleArray(images) : images;
// Slides kept in the layout (first visible, rest preloaded) so their eager loads actually fire; anything past this stays hidden until its turn.
const preloadCount = Math.max(eagerCount, 2);
---
<div class:list={[className, "relative overflow-hidden"]}>
@@ -60,7 +63,7 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
transitionStyle,
transitionDuration,
index > 0 ? "opacity-0" : "",
index < 2 ? "" : "hidden"
index < preloadCount ? "" : "hidden"
]}
src={image}
alt={
@@ -89,27 +92,53 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
</div>
<script
define:vars={{ imageCount: imagesArray.length, carouselId: id, interval }}
define:vars={{
imageCount: imagesArray.length,
carouselId: id,
interval,
preloadCount
}}
>
let currentIndex = 0;
window.__carousels = window.__carousels || {};
// Reset the carousel and (re)start it. Runs on initial load and after
// every page transition, so navigation always restarts the fade sequence.
function initCarousel() {
const state = (window.__carousels[carouselId] = window.__carousels[
carouselId
] || { index: 0, timer: null });
state.index = 0;
if (state.timer) {
clearInterval(state.timer);
state.timer = null;
}
const slides = Array.from(
document.querySelectorAll(`[data-id^="${carouselId}-"]`)
);
slides.forEach((slide, index) => {
slide.classList.toggle("opacity-0", index !== 0);
slide.classList.toggle("hidden", index >= preloadCount);
slide.setAttribute("aria-hidden", index === 0 ? "false" : "true");
});
// Honour reduced-motion: leave the first image showing, no rotation.
const reduceMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)"
).matches;
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
if (!reduceMotion) {
setInterval(() => {
state.timer = setInterval(() => {
const current = document.querySelectorAll(
`[data-id="${carouselId}-${currentIndex}"]`
`[data-id="${carouselId}-${state.index}"]`
)[0];
const nextIndex = (currentIndex + 1) % imageCount;
const nextIndex = (state.index + 1) % imageCount;
const next = document.querySelectorAll(
`[data-id="${carouselId}-${nextIndex}"]`
)[0];
if (!current || !next) return;
// Wait until the incoming image has loaded, or the fade pops in.
if (next.tagName === "IMG" && !next.complete) return;
// Fade out current, fade in next
current.classList.add("opacity-0");
current.setAttribute("aria-hidden", "true");
@@ -127,7 +156,10 @@ const imagesArray = shuffle ? shuffleArray(images) : images;
preloadImage.setAttribute("aria-hidden", "true");
}
currentIndex = nextIndex;
state.index = nextIndex;
}, interval);
}
document.addEventListener("astro:page-load", initCarousel);
initCarousel();
</script>
+5 -1
View File
@@ -64,7 +64,7 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
<div class="md:hidden">
<button
id="menu-toggle"
class="text-2xl focus:outline-none"
class="focus-visible:ring-primary text-2xl focus:outline-none focus-visible:ring-2"
aria-label="Toggle navigation menu"
aria-expanded="false"
aria-controls="mobile-menu"
@@ -139,6 +139,10 @@ const { navbarDisplay = "normal" } = Astro.props as Props;
nav = document.getElementById("nav");
toggle = document.getElementById("menu-toggle");
// The nav persists across transitions: always start with a closed menu.
nav?.setAttribute("data-mobile-menu", "hidden");
toggle?.setAttribute("aria-expanded", "false");
if (toggle && !toggle.dataset.bound) {
toggle.dataset.bound = "true";
toggle.addEventListener("click", () => {
+2 -2
View File
@@ -120,7 +120,7 @@ const tracks = (
quality={5}
height={192}
shuffle={true}
eagerCount={0}
eagerCount={4}
><div class="flex h-full w-full items-center">
<a
href="/contact/"
@@ -208,7 +208,7 @@ const tracks = (
quality={5}
height={384}
shuffle={true}
eagerCount={0}
eagerCount={4}
/>
</div>
</section>