diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7d7a7147..7cec89f0 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,12 +1,15 @@ import type { Metadata } from "next"; +import type { CSSProperties } from "react"; import { Analytics } from "@vercel/analytics/react"; import "./globals.css"; +import { ApplyBar, APPLY_BAR_HEIGHT } from "@/components/layout/ApplyBar"; import Footer from "@/components/layout/Footer"; import MainContent from "@/components/layout/MainContent"; import { NavMenuProvider } from "@/components/layout/NavMenuContext"; import Navbar from "@/components/layout/Navbar"; import ScrollToTopButton from "@/components/layout/ScrollToTopButton"; import { Geist } from "next/font/google"; +import { APPLICATION_IS_LIVE } from "@constants/applications"; import { cn } from "@/lib/utils"; const geist = Geist({ subsets: ["latin"], variable: "--font-sans" }); @@ -23,9 +26,20 @@ export default function RootLayout({ }: Readonly<{ children: React.ReactNode; }>) { + // When applications are live, the sticky ApplyBar occupies the top of the + // viewport; `--apply-banner-h` offsets the navbar (see Navbar) and pushes all + // page content down by exactly the bar's height. + const bannerStyle = APPLICATION_IS_LIVE + ? ({ "--apply-banner-h": APPLY_BAR_HEIGHT } as CSSProperties) + : undefined; + return ( - + + {children} diff --git a/src/components/layout/ApplyBar.tsx b/src/components/layout/ApplyBar.tsx new file mode 100644 index 00000000..13411979 --- /dev/null +++ b/src/components/layout/ApplyBar.tsx @@ -0,0 +1,45 @@ +"use client"; + +import Link from "next/link"; + +import { + APPLICATION_CLOSE_DATETIME, + APPLICATION_IS_LIVE, + APPLICATION_LINK, +} from "@constants/applications"; + +/** Height of the bar. Kept in sync with `--apply-banner-h` set in the layout. */ +export const APPLY_BAR_HEIGHT = "3.5rem"; + +/** + * Site-wide sticky announcement bar pinned above the navbar. Renders only while + * applications are open; the layout mirrors this with `--apply-banner-h` so the + * navbar and page content offset by exactly this height (0 when hidden). + */ +export function ApplyBar() { + if (!APPLICATION_IS_LIVE) return null; + + const closeDate = APPLICATION_CLOSE_DATETIME.format("MMMM D, YYYY"); + + return ( +
+

+ + Applications to join Blueprint next term are open, and close{" "} + {closeDate}. + + Applications close {closeDate}. +

+ + + Apply now + +
+ ); +} diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx index f49ca8f3..54f9522c 100644 --- a/src/components/layout/Navbar.tsx +++ b/src/components/layout/Navbar.tsx @@ -189,7 +189,7 @@ export default function Navbar() { return (
= 1 ? "none" : "auto", diff --git a/src/components/sections/JoinUsHero.tsx b/src/components/sections/JoinUsHero.tsx index e5d8e628..fd5babaf 100644 --- a/src/components/sections/JoinUsHero.tsx +++ b/src/components/sections/JoinUsHero.tsx @@ -2,7 +2,6 @@ import UnderlineToBackground from "@/components/fancy/text/underline-to-background"; import { scrollToElement } from "@/lib/utils"; -import { ApplyNowBanner } from "../ui/ApplyNowBanner"; const SECTION_LINKS: { id: string; label: string }[] = [ { id: "why-join", label: "why join" }, @@ -35,11 +34,9 @@ export function JoinUsHero() {
+ >
- -

); -} \ No newline at end of file +} diff --git a/src/components/ui/ApplyNowBanner.tsx b/src/components/ui/ApplyNowBanner.tsx deleted file mode 100644 index 3610d747..00000000 --- a/src/components/ui/ApplyNowBanner.tsx +++ /dev/null @@ -1,47 +0,0 @@ -"use client"; - -import Link from "next/link"; - -import { - APPLICATION_CLOSE_DATETIME, - APPLICATION_IS_LIVE, - APPLICATION_LINK, -} from "@constants/applications"; -import { buttonVariants } from "@/components/ui/button-variants"; -import { cn } from "@/lib/utils"; - -export interface ApplyNowBannerProps { - className?: string; -} - -export function ApplyNowBanner({ className }: ApplyNowBannerProps) { - if (!APPLICATION_IS_LIVE) return null; - - const closeDate = APPLICATION_CLOSE_DATETIME.format("MMMM D, YYYY"); - - return ( -
-
-

- Applications to join Blueprint next term are open, and close{" "} - {closeDate}. -

- -
- - Apply - -
-
-
- ); -}