diff --git a/app/layout.tsx b/app/layout.tsx index b4c68a4..05cd552 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,25 +2,36 @@ import type React from "react" import type { Metadata } from "next" import { Analytics } from "@vercel/analytics/next" import "./globals.css" +import LadderClimb from "@/components/ladderclimber" +import Navbar from "@/components/navbar" export const metadata: Metadata = { title: "School For Schools", description: "Combining expertise in education with cutting-edge technology", - generator: "v0.app", - } -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode -}>) { +export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + + + + + {/* LadderClimb is now global */} + + + {/* All page content */} {children} ) -} +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 2773717..7bd9aef 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,17 +5,19 @@ import Testimonials from "@/components/testimonials" import OurOfferings from "@/components/our-offerings" import TrustedBy from "@/components/trusted-by" import Footer from "@/components/footer" +import LadderClimb from "@/components/ladderclimber" export default function Home() { return ( -
- - - {/* This is the 'Why SFS' section */} - - - -
-
+ <> +
+ + {/* This is the 'Why SFS' section */} + + + +
+
+ ) } diff --git a/components/ladderclimber.tsx b/components/ladderclimber.tsx new file mode 100644 index 0000000..d46b69d --- /dev/null +++ b/components/ladderclimber.tsx @@ -0,0 +1,143 @@ +"use client" + +import React, { useEffect, useRef, useState } from "react" + +export default function LadderClimb() { + const charRef = useRef(null) + const ladderRef = useRef(null) + + const [animation, setAnimation] = useState<"idle" | "climbing">("idle") + const [visible, setVisible] = useState(true) + + const ladderHeight = useRef(0) + const scrollTimeout = useRef(null) + + const updateDimensions = () => { + if (ladderRef.current) { + ladderHeight.current = ladderRef.current.clientHeight + } + if (charRef.current) { + charRef.current.style.transform = `translateY(0px)` + } + } + + useEffect(() => { + updateDimensions() + setAnimation("idle") + + window.addEventListener("resize", updateDimensions) + + const handleScroll = () => { + if (!charRef.current || !ladderRef.current) return + + // show ladder & climbing animation + setVisible(true) + setAnimation("climbing") + + const scrollTop = window.scrollY + const docHeight = document.body.scrollHeight + const viewportHeight = window.innerHeight + + let progress = scrollTop / (docHeight - viewportHeight) + progress = Math.max(0, Math.min(1, progress)) + + const topOffset = 174 + const bottomOffset = 30 + const maxClimb = ladderHeight.current - topOffset - bottomOffset + + const y = progress * maxClimb + charRef.current.style.transform = `translateY(-${y}px)` + + // hide smoothly after scroll stops + if (scrollTimeout.current) clearTimeout(scrollTimeout.current) + scrollTimeout.current = setTimeout(() => { + setAnimation("idle") + setVisible(false) + }, 500) + } + + window.addEventListener("scroll", handleScroll) + + return () => { + window.removeEventListener("scroll", handleScroll) + window.removeEventListener("resize", updateDimensions) + if (scrollTimeout.current) clearTimeout(scrollTimeout.current) + } + }, []) + + return ( + <> + {/* Ladder container */} +
+ {/* Ladder background */} +
+ + {/* Character */} +
+ {/* CLIMBING */} + {animation === "climbing" && ( + Climbing + )} + + {/* IDLE */} + {animation === "idle" && ( + Idle + )} +
+
+ + ) +} diff --git a/components/navbar.tsx b/components/navbar.tsx index 3e133fa..b092667 100644 --- a/components/navbar.tsx +++ b/components/navbar.tsx @@ -75,7 +75,7 @@ export default function Navbar() { }, []) return ( -