diff --git a/components/layout/v2ComponentWrapper.tsx b/components/layout/v2ComponentWrapper.tsx index e7e6280e48..eb983d2121 100644 --- a/components/layout/v2ComponentWrapper.tsx +++ b/components/layout/v2ComponentWrapper.tsx @@ -1,10 +1,8 @@ "use client"; import classNames from "classnames"; -import { useInView } from "framer-motion"; import Image from "next/image"; -import { UseInViewOptions } from "framer-motion"; -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { backgroundOptions } from "../blocksSubtemplates/tinaFormElements/colourOptions/blockBackgroundOptions"; type BackgroundData = { @@ -18,6 +16,38 @@ type BackgroundData = { }; }; +// "pending" until the observer's first report, so a block never goes +// visible→hidden after paint — hiding painted content disqualifies it as an +// LCP candidate. +const useFadeIn = (rootMargin: string) => { + const ref = useRef(null); + const [state, setState] = useState<"pending" | "hidden" | "visible">( + "pending" + ); + + useEffect(() => { + const element = ref.current; + if (!element) return; + + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setState("visible"); + observer.disconnect(); + } else { + setState((current) => (current === "pending" ? "hidden" : current)); + } + }, + { rootMargin } + ); + + observer.observe(element); + return () => observer.disconnect(); + }, [rootMargin]); + + return [ref, state] as const; +}; + const V2ComponentWrapper = ({ data, children, @@ -26,7 +56,7 @@ const V2ComponentWrapper = ({ }: { data: BackgroundData; children: React.ReactNode; - fadeInMargin?: UseInViewOptions["margin"]; + fadeInMargin?: string; className?: string; }) => { //Bleed effect setup @@ -47,13 +77,7 @@ const V2ComponentWrapper = ({ }; }, []); - //Fade-in effect setup - const ref = useRef(null); - const isInView = useInView(ref, { once: true, margin: fadeInMargin }); - useEffect(() => { - setIsInInitialViewport(isInView); - }, [isInView]); - const [isInInitialViewport, setIsInInitialViewport] = React.useState(null); + const [ref, fadeState] = useFadeIn(fadeInMargin); return (