diff --git a/src/App.css b/src/App.css index f7c95b5..4db9e8d 100644 --- a/src/App.css +++ b/src/App.css @@ -126,6 +126,8 @@ body { @apply bg-background text-foreground font-space-mono; + padding-top: var(--pycon-banner-height, 0px); + transition: padding-top 0.2s ease; } h1, diff --git a/src/components/PyConBanner.tsx b/src/components/PyConBanner.tsx new file mode 100644 index 0000000..922a55b --- /dev/null +++ b/src/components/PyConBanner.tsx @@ -0,0 +1,103 @@ +import { useEffect, useRef, useState } from "react"; +import { PartyPopper } from "lucide-react"; + +const PYCON_URL = "https://cm.pycon.org"; +const EVENT_START = new Date("2026-09-17T00:00:00+01:00"); + +interface TimeLeft { + days: number; + hours: number; + minutes: number; + seconds: number; +} + +const getTimeLeft = (): TimeLeft => { + const diff = Math.max(0, EVENT_START.getTime() - Date.now()); + return { + days: Math.floor(diff / (1000 * 60 * 60 * 24)), + hours: Math.floor((diff / (1000 * 60 * 60)) % 24), + minutes: Math.floor((diff / (1000 * 60)) % 60), + seconds: Math.floor((diff / 1000) % 60), + }; +}; + +const TimeUnit = ({ value, label }: { value: number; label: string }) => ( +
+ + {String(value).padStart(2, "0")} + + + {label} + +
+); + +export const PyConBanner = () => { + const rootRef = useRef(null); + const [timeLeft, setTimeLeft] = useState(getTimeLeft); + + useEffect(() => { + const interval = setInterval(() => setTimeLeft(getTimeLeft()), 1000); + return () => clearInterval(interval); + }, []); + + const visible = Date.now() < EVENT_START.getTime(); + + useEffect(() => { + const el = rootRef.current; + if (!visible || !el) { + document.documentElement.style.setProperty("--pycon-banner-height", "0px"); + return; + } + + const updateHeight = () => + document.documentElement.style.setProperty( + "--pycon-banner-height", + `${el.offsetHeight}px` + ); + + updateHeight(); + const observer = new ResizeObserver(updateHeight); + observer.observe(el); + + return () => { + observer.disconnect(); + document.documentElement.style.setProperty("--pycon-banner-height", "0px"); + }; + }, [visible]); + + if (!visible) return null; + + return ( +
+ + + + PyCon Cameroon 2026 · Sept 17–19 + + +
+ + : + + : + + : + +
+ + + Learn more → + +
+
+ ); +}; diff --git a/src/layouts/Navbar.tsx b/src/layouts/Navbar.tsx index 87e1c19..4ffdb62 100644 --- a/src/layouts/Navbar.tsx +++ b/src/layouts/Navbar.tsx @@ -18,6 +18,7 @@ import { Menu } from "lucide-react"; import { ModeToggle } from "@/components/mode-toggle"; import { LogoIcon } from "@/components/Icons"; import { LanguageSwitcher } from "@/components/language"; // Import Language Switcher +import { PyConBanner } from "@/components/PyConBanner"; import { motion, AnimatePresence } from "framer-motion"; interface RouteProps { @@ -122,6 +123,8 @@ export const Navbar = () => { animate={{ y: 0 }} transition={{ duration: 0.3 }} > + + {/* Background glow effect */}