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 (
+
+ );
+};
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 */}