.club-team-card:hover {
html[data-motion="ready"] [data-scroll-drift]:not([data-reveal]) {
transform: translate3d(0, var(--club-scroll-drift, 0px), 0);
- transition: transform 120ms linear;
+ transition: none;
will-change: transform;
}
diff --git a/apps/club/src/app/hackathons/hackathon-history.tsx b/apps/club/src/app/hackathons/hackathon-history.tsx
index ed79828d3..f57956f2d 100644
--- a/apps/club/src/app/hackathons/hackathon-history.tsx
+++ b/apps/club/src/app/hackathons/hackathon-history.tsx
@@ -534,13 +534,15 @@ function Timeline({ rows }: { rows: readonly TimelineRow[] }) {
{rows.map((row) => {
diff --git a/apps/club/src/app/layout.tsx b/apps/club/src/app/layout.tsx
index 9dad41852..69f4e32de 100644
--- a/apps/club/src/app/layout.tsx
+++ b/apps/club/src/app/layout.tsx
@@ -6,6 +6,7 @@ import ClubMotionRuntime from "./_components/club-motion-runtime";
import Footer from "./_components/footer";
import JsonLd from "./_components/json-ld";
import Navbar from "./_components/navbar";
+import { CLUB_ASSET_BASE_URL } from "./_lib/assets";
import {
OG_IMAGE_ALT,
OG_IMAGE_HEIGHT,
@@ -28,6 +29,7 @@ const montserrat = Montserrat({
});
const bladeUrl = env.BLADE_URL;
+const clubAssetOrigin = new URL(CLUB_ASSET_BASE_URL).origin;
export const metadata: Metadata = {
metadataBase: new URL(SITE_URL),
@@ -99,6 +101,9 @@ export default function RootLayout({
}>) {
return (
+
+
+
diff --git a/apps/club/src/app/not-found.tsx b/apps/club/src/app/not-found.tsx
index 7d7c227a9..4891904a5 100644
--- a/apps/club/src/app/not-found.tsx
+++ b/apps/club/src/app/not-found.tsx
@@ -30,7 +30,9 @@ export default function NotFound() {
size="lg"
className="club-button mt-7 min-h-[3.35rem] bg-[var(--club-gold)] text-black shadow-[4px_4px_0_#ffffff]"
>
- Go home
+
+ Go home
+
diff --git a/apps/club/src/app/sponsors/sponsors-client.tsx b/apps/club/src/app/sponsors/sponsors-client.tsx
index b62068e88..40a8fbc70 100644
--- a/apps/club/src/app/sponsors/sponsors-client.tsx
+++ b/apps/club/src/app/sponsors/sponsors-client.tsx
@@ -261,7 +261,6 @@ function SponsorHighlight({
src={selectedSlide.imageSrc}
alt={selectedSlide.alt}
fill
- priority={selectedIndex === 0}
sizes="(min-width: 1024px) 30rem, (min-width: 768px) 42vw, 88vw"
className="object-cover transition duration-300"
/>
diff --git a/apps/club/src/app/teams/teams-client.tsx b/apps/club/src/app/teams/teams-client.tsx
index c69998724..6f3303eeb 100644
--- a/apps/club/src/app/teams/teams-client.tsx
+++ b/apps/club/src/app/teams/teams-client.tsx
@@ -171,10 +171,12 @@ function EmptyTeam({ label, status }: { label: string; status: RosterStatus }) {
}
export default function TeamsClient({ bladeUrl }: { bladeUrl: string }) {
+ const rosterSectionRef = useRef(null);
const pendingScrollPosition = useRef<{ x: number; y: number } | null>(null);
const prefersReducedMotion = useReducedMotion();
const [roster, setRoster] = useState(() => createEmptyRoster());
const [status, setStatus] = useState("loading");
+ const [shouldLoadRoster, setShouldLoadRoster] = useState(false);
const [activeTeam, setActiveTeam] = useState(
TEAM_DEFINITIONS[0].slug,
);
@@ -215,12 +217,46 @@ export default function TeamsClient({ bladeUrl }: { bladeUrl: string }) {
pendingScrollPosition.current = null;
window.scrollTo(scrollPosition.x, scrollPosition.y);
- window.requestAnimationFrame(() => {
+ const animationFrameId = window.requestAnimationFrame(() => {
window.scrollTo(scrollPosition.x, scrollPosition.y);
});
+
+ return () => window.cancelAnimationFrame(animationFrameId);
}, [activeTeam]);
useEffect(() => {
+ const rosterSection = rosterSectionRef.current;
+
+ if (!rosterSection || shouldLoadRoster) return;
+
+ if (!("IntersectionObserver" in window)) {
+ const fallbackId = globalThis.setTimeout(() => {
+ setShouldLoadRoster(true);
+ }, 0);
+
+ return () => globalThis.clearTimeout(fallbackId);
+ }
+
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (!entry?.isIntersecting) return;
+
+ setShouldLoadRoster(true);
+ observer.disconnect();
+ },
+ {
+ rootMargin: "0px",
+ },
+ );
+
+ observer.observe(rosterSection);
+
+ return () => observer.disconnect();
+ }, [shouldLoadRoster]);
+
+ useEffect(() => {
+ if (!shouldLoadRoster) return;
+
const abortController = new AbortController();
async function loadRoster() {
@@ -240,7 +276,7 @@ export default function TeamsClient({ bladeUrl }: { bladeUrl: string }) {
void loadRoster();
return () => abortController.abort();
- }, [bladeUrl]);
+ }, [bladeUrl, shouldLoadRoster]);
function selectTeam(team: TeamSlug) {
if (team === activeTeam) return;
@@ -330,6 +366,7 @@ export default function TeamsClient({ bladeUrl }: { bladeUrl: string }) {