Full Page Navbar

fullpage

A dramatic full-screen overlay navigation with image previews and smooth reveal animations. Perfect for creative agencies and portfolios.

#fullpage#overlay#creative#animated

Preview

full-page-navbar
Interactive Preview
Studio.
Creative Agency

Architects of modern digital experiences & brand identities.

© 2025 Studio Inc.
Click the hamburger icon to reveal full-screen overlay navigation.

Installation

Add this navbar component directly to your project using the shadcn CLI:

$npx shadcn@latest add https://navcn-phi.vercel.app/r/full-page-navbar.json

The component source code is added directly to your project so you can customize it freely.

Source Code

src/components/previews/full-page-navbar.tsx
tsx
"use client";

import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "motion/react";
import { ChevronRight, ArrowUpRight, X } from "lucide-react";
import Image from "next/image";

interface NavItem {
  label: string;
  href: string;
  image: string;
}

const navItems: NavItem[] = [
  {
    label: "Work",
    href: "#work",
    image:
      "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=800&h=600&fit=crop",
  },
  {
    label: "About",
    href: "#about",
    image:
      "https://images.unsplash.com/photo-1618556450994-a6a128ef0d9d?w=800&h=600&fit=crop",
  },
  {
    label: "Services",
    href: "#services",
    image:
      "https://images.unsplash.com/photo-1618556450991-2f1af64e8191?w=800&h=600&fit=crop",
  },
  {
    label: "Contact",
    href: "#contact",
    image:
      "https://images.unsplash.com/photo-1618005198919-d3d4b5a92ead?w=800&h=600&fit=crop",
  },
];

export function FullPageNavbar() {
  const [isMenuOpen, setIsMenuOpen] = useState(false);
  const [hoveredItem, setHoveredItem] = useState<string | null>(null);
  const [activeLink, setActiveLink] = useState(navItems[0].href);
  const [timeString, setTimeString] = useState("");

  useEffect(() => {
    const updateTime = () => {
      setTimeString(
        new Date().toLocaleTimeString([], {
          hour: "2-digit",
          minute: "2-digit",
          hour12: true,
        })
      );
    };
    updateTime();
    const interval = setInterval(updateTime, 1000);
    return () => clearInterval(interval);
  }, []);

  const toggleMenu = () => setIsMenuOpen((s) => !s);

  const handleLinkClick = (href: string) => {
    setActiveLink(href);
    setIsMenuOpen(false);
  };

  return (
    <div className="relative w-full min-h-[360px] sm:min-h-[420px] rounded-3xl border border-border/80 bg-neutral-950 text-white overflow-hidden flex flex-col justify-between p-6 sm:p-8 select-none shadow-xl">
      {/* Background ambient lighting */}
      <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-neutral-800/40 via-neutral-950 to-neutral-950 -z-10" />

      {/* ── CLOSED STATE HERO / HEADER ───────────────────────────────── */}
      <div className="w-full flex items-center justify-between z-10">
        <span className="text-xl font-bold tracking-tight text-white font-sans">
          Studio<span className="text-neutral-500">.</span>
        </span>

        <div className="hidden sm:flex items-center gap-2 text-xs text-neutral-400 font-mono">
          <span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
          <span>New York • {timeString || "12:00 PM"}</span>
        </div>

        {/* Hamburger trigger */}
        <motion.button
          whileHover={{ scale: 1.05 }}
          whileTap={{ scale: 0.95 }}
          onClick={toggleMenu}
          aria-label={isMenuOpen ? "Close menu" : "Open menu"}
          className="flex items-center gap-2 px-3 py-1.5 rounded-full border border-neutral-700 bg-neutral-900/80 hover:bg-neutral-800 text-xs font-medium transition-colors cursor-pointer"
        >
          <span>Menu</span>
          <div className="w-4 h-3 flex flex-col justify-between">
            <span className="w-full h-0.5 bg-white rounded-full" />
            <span className="w-full h-0.5 bg-white rounded-full" />
          </div>
        </motion.button>
      </div>

      {/* Hero stage content inside preview */}
      <div className="my-auto py-8 z-10">
        <span className="text-[11px] font-mono uppercase tracking-widest text-neutral-500 mb-2 block">
          Creative Agency
        </span>
        <h3 className="text-2xl sm:text-3xl font-light text-neutral-200 leading-tight max-w-lg mb-4">
          Architects of modern digital experiences & brand identities.
        </h3>
        <button
          onClick={toggleMenu}
          className="inline-flex items-center gap-2 text-xs font-medium text-neutral-300 hover:text-white underline underline-offset-4 cursor-pointer"
        >
          Explore Navigation Overlay
          <ArrowUpRight size={14} />
        </button>
      </div>

      <div className="w-full flex items-center justify-between text-xs text-neutral-500 border-t border-neutral-800/80 pt-4 z-10 font-mono">
        <span>© 2025 Studio Inc.</span>
        <span className="hidden sm:inline-block">Click Menu to toggle full-screen overlay</span>
      </div>

      {/* ── FULL-PAGE OVERLAY ────────────────────────────────────────── */}
      <AnimatePresence>
        {isMenuOpen && (
          <motion.div
            initial={{ opacity: 0, y: "-100%" }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: "-100%" }}
            transition={{ duration: 0.45, ease: [0.16, 1, 0.3, 1] }}
            className="absolute inset-0 z-30 bg-neutral-950 text-white p-6 sm:p-8 flex flex-col justify-between overflow-hidden"
          >
            {/* Overlay Header */}
            <div className="w-full flex items-center justify-between border-b border-neutral-800/80 pb-4">
              <span className="text-xl font-bold tracking-tight text-white">
                Studio<span className="text-neutral-500">.</span>
              </span>

              <div className="hidden sm:flex items-center gap-2 text-xs text-neutral-400 font-mono">
                <span>New York • {timeString || "12:00 PM"}</span>
              </div>

              <motion.button
                whileHover={{ scale: 1.08 }}
                whileTap={{ scale: 0.95 }}
                onClick={toggleMenu}
                aria-label="Close menu"
                className="flex items-center gap-2 px-3 py-1.5 rounded-full border border-neutral-700 bg-neutral-900 text-xs font-medium text-white hover:bg-neutral-800 transition-colors cursor-pointer"
              >
                <span>Close</span>
                <X size={14} />
              </motion.button>
            </div>

            {/* Overlay Content Grid */}
            <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-center my-auto py-4">
              {/* Left — Nav Items */}
              <div className="space-y-2 sm:space-y-3">
                {navItems.map((item, index) => {
                  const isHovered = hoveredItem === item.label;
                  const isActive = activeLink === item.href;

                  return (
                    <motion.div
                      key={item.label}
                      initial={{ opacity: 0, x: -20 }}
                      animate={{ opacity: 1, x: 0 }}
                      transition={{ delay: 0.08 + index * 0.05 }}
                      onMouseEnter={() => setHoveredItem(item.label)}
                      onMouseLeave={() => setHoveredItem(null)}
                    >
                      <button
                        onClick={() => handleLinkClick(item.href)}
                        className={`group flex items-center gap-3 text-2xl sm:text-3xl font-light transition-all cursor-pointer ${
                          isHovered || isActive
                            ? "text-white font-normal translate-x-2"
                            : "text-neutral-500 hover:text-neutral-300"
                        }`}
                      >
                        <span>{item.label}</span>
                        <ChevronRight className={`w-5 h-5 transition-transform ${isHovered ? "translate-x-1 text-white" : "opacity-0"}`} />
                      </button>
                    </motion.div>
                  );
                })}
              </div>

              {/* Right — Image Preview (Desktop) */}
              <div className="hidden lg:block relative w-full h-48 xl:h-56 rounded-2xl overflow-hidden border border-neutral-800 bg-neutral-900">
                <AnimatePresence mode="wait">
                  <motion.div
                    key={hoveredItem || activeLink}
                    initial={{ opacity: 0, scale: 0.95 }}
                    animate={{ opacity: 1, scale: 1 }}
                    exit={{ opacity: 0, scale: 0.95 }}
                    transition={{ duration: 0.25 }}
                    className="absolute inset-0"
                  >
                    <Image
                      src={
                        navItems.find(
                          (item) =>
                            item.label === hoveredItem ||
                            item.href === activeLink
                        )?.image || navItems[0].image
                      }
                      alt={hoveredItem || "Preview"}
                      fill
                      className="object-cover rounded-2xl"
                      sizes="(max-width: 1024px) 0px, 40vw"
                    />
                  </motion.div>
                </AnimatePresence>
              </div>
            </div>

            {/* Overlay Footer */}
            <div className="w-full flex items-center justify-between text-xs text-neutral-500 border-t border-neutral-800/80 pt-4 font-mono">
              <span>hello@studio-agency.com</span>
              <span className="hidden sm:inline-block">Click any item to select link</span>
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}

export default FullPageNavbar;

This is the exact source file installed into your project. You own and control the code.