Header Navigation Fix Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Replace the hidden "MENU" button pattern with directly visible nav links in the desktop header, matching the premium design system.
Architecture: Desktop header gets a horizontal nav link row below the logo. Mobile stays as-is with the bottom dock. Dropdown menu becomes a full MegaMenu that appears on hover, keeping the existing panel for its rich content layout.
Tech Stack: Next.js 16, Tailwind CSS v4, tailwindcss-animate
File Map
| File | Change |
|---|---|
apps/frontend/components/layout/header.tsx | Replace MENU button with visible nav links, refactor dropdown to MegaMenu on hover |
apps/frontend/components/layout/mobile-bottom-dock.tsx | Add missing nav links, fix click outside handling |
apps/frontend/components/layout/nav-links.ts | Add social links and phone number as exports |
Task 1: Extract config from nav-links.ts
Files:
-
Modify:
apps/frontend/components/layout/nav-links.ts -
Step 1: Add social links and phone number exports
Add at end of nav-links.ts:
export const CONTACT_INFO = {
phone: {
label: "+84 90 123 4567",
href: "tel:+84901234567",
},
socials: {
facebook: {
label: "Facebook",
href: "https://www.facebook.com/houseoflegendsvn",
},
instagram: {
label: "Instagram",
href: "https://www.instagram.com/houseoflegends.vn",
},
tripadvisor: {
label: "TripAdvisor",
href: "https://www.tripadvisor.com",
},
},
} as const;- Step 2: Commit
git add apps/frontend/components/layout/nav-links.ts
git commit -m "feat: extract contact info config from nav-links"Task 2: Refactor desktop header — visible nav links + MegaMenu
Files:
-
Modify:
apps/frontend/components/layout/header.tsx -
Step 1: Rewrite header layout — visible nav row + MegaMenu on hover
Replace the entire header.tsx content between lines 45-137 with:
{
/* Desktop Navigation - Visible Nav Links + MegaMenu */
}
<header
className={cn(
"fixed top-0 left-0 right-0 z-50 transition-colors duration-200",
isScrolled
? "bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"
: "bg-transparent",
)}
>
<div className="flex flex-col">
{/* Top bar: Logo + Nav Links + Language + CTA */}
<div className="flex items-center justify-between px-6 h-20">
{/* Left: Logo */}
<div className="flex items-center gap-4">
<LocaleLink href="/" className="flex items-center">
<img
src="/images/landing-page/hero-logo.png"
alt="House of Legends"
className="h-16 w-auto"
/>
</LocaleLink>
</div>
{/* Center: Visible Nav Links */}
<nav className="hidden lg:flex items-center gap-1">
{navLinks.slice(0, 6).map((link) => (
<LocaleLink
key={link.href}
href={link.href}
className="px-4 py-2 text-sm font-medium text-[var(--color-foreground)] hover:text-[var(--color-gold)] transition-colors rounded-lg hover:bg-[var(--color-surface)]"
>
{link.label}
</LocaleLink>
))}
{/* "More" dropdown for remaining links */}
<div className="relative group">
<button
type="button"
className="px-4 py-2 text-sm font-medium text-[var(--color-foreground)] hover:text-[var(--color-gold)] transition-colors rounded-lg hover:bg-[var(--color-surface)] flex items-center gap-1"
>
More
<ChevronDown className="w-4 h-4" />
</button>
{/* Dropdown panel */}
<div className="absolute top-full left-1/2 -translate-x-1/2 pt-2 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200">
<div className="w-64 bg-background border border-[var(--color-gold-border)] shadow-2xl rounded-xl p-2">
{navLinks.slice(6).map((link) => (
<LocaleLink
key={link.href}
href={link.href}
className="flex items-center gap-3 px-4 py-3 text-sm text-[var(--color-foreground)] hover:text-[var(--color-gold)] hover:bg-[var(--color-surface)] rounded-lg transition-colors"
>
{link.label}
</LocaleLink>
))}
</div>
</div>
</div>
</nav>
{/* Right: Language + CTA */}
<div className="hidden lg:flex items-center gap-4">
<LanguageSwitcher currentLang={locale} />
<LocaleLink
href="/host-an-event"
className="px-7 py-3.5 rounded-xl font-semibold text-background bg-gold hover:bg-gold-light active:scale-[0.97] transition-all duration-150"
>
Host An Event
</LocaleLink>
</div>
</div>
</div>
</header>;- Step 2: Add ChevronDown import
Find import line 19:
import * as React from "react";Add after:
import { ChevronDown } from "lucide-react";- Step 3: Remove the old center menu button section and dropdown (lines 58-124)
Delete the entire lg:flex items-center div that contains the old MENU button logic.
- Step 4: Commit
git add apps/frontend/components/layout/header.tsx
git commit -m "feat: replace hidden menu with visible nav links + hover MegaMenu"Task 3: Fix mobile bottom dock — add missing nav links and contact info
Files:
-
Modify:
apps/frontend/components/layout/mobile-bottom-dock.tsx -
Step 1: Import contact info
Find imports (line 20-21):
import { cn } from "~/lib/utils";Add after:
import { CONTACT_INFO } from "~/components/layout/nav-links";- Step 2: Replace hardcoded phone with config
Find line 161-166:
<a
href="tel:+84901234567"
className="flex items-center justify-center w-11 h-11 rounded-full text-foreground hover:text-[var(--color-gold)] active:scale-[0.98] transition-colors"
aria-label="Call"
>
<Phone className="w-5 h-5" />
</a>Change to:
<a
href={CONTACT_INFO.phone.href}
className="flex items-center justify-center w-11 h-11 rounded-full text-foreground hover:text-[var(--color-gold)] active:scale-[0.98] transition-colors"
aria-label="Call"
>
<Phone className="w-5 h-5" />
</a>- Step 3: Replace hardcoded social URLs
Find lines 183-206 — replace the entire social links section with:
<div className="flex items-center justify-center gap-2">
<a
href={CONTACT_INFO.socials.facebook.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center w-11 h-11 rounded-full text-[var(--color-muted-foreground)] hover:text-foreground hover:bg-[var(--color-surface-hover)] transition-colors"
aria-label={CONTACT_INFO.socials.facebook.label}
>
{SOCIAL_ICONS.facebook}
</a>
<a
href={CONTACT_INFO.socials.instagram.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center w-11 h-11 rounded-full text-[var(--color-muted-foreground)] hover:text-foreground hover:bg-[var(--color-surface-hover)] transition-colors"
aria-label={CONTACT_INFO.socials.instagram.label}
>
{SOCIAL_ICONS.instagram}
</a>
<a
href={CONTACT_INFO.socials.tripadvisor.href}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center w-11 h-11 rounded-full text-[var(--color-muted-foreground)] hover:text-foreground hover:bg-[var(--color-surface-hover)] transition-colors"
aria-label={CONTACT_INFO.socials.tripadvisor.label}
>
{SOCIAL_ICONS.tripadvisor}
</a>
</div>- Step 4: Commit
git add apps/frontend/components/layout/mobile-bottom-dock.tsx
git commit -m "fix: use contact info config in mobile bottom dock"Task 4: Add all 10 mobile nav links
Files:
-
Modify:
apps/frontend/components/layout/mobile-bottom-dock.tsx -
Step 1: Update NAV_LINKS to 10 items
Find lines 29-36:
const NAV_LINKS: NavLink[] = [
{ href: "/", icon: Home, key: "home" },
{ href: "/experiences/dinner-theater", icon: UtensilsCrossed, key: "shows" },
{ href: "/private-events", icon: Briefcase, key: "private" },
{ href: "/experiences/creative-workshop", icon: Settings, key: "workshop" },
{ href: "/experiences/artist-performances", icon: Mic2, key: "performances" },
{ href: "/venue-rental", icon: MapPin, key: "venue" },
];Change to:
const NAV_LINKS: NavLink[] = [
{ href: "/", icon: Home, key: "home" },
{ href: "/experiences/dinner-theater", icon: UtensilsCrossed, key: "shows" },
{ href: "/private-events", icon: Briefcase, key: "private" },
{ href: "/experiences/creative-workshop", icon: Settings, key: "workshop" },
{ href: "/experiences/artist-performances", icon: Mic2, key: "performances" },
{ href: "/venue-rental", icon: MapPin, key: "venue" },
{ href: "/reviews", icon: Star, key: "reviews" },
{ href: "/about", icon: Info, key: "about" },
{ href: "/contact", icon: Mail, key: "contact" },
{ href: "/faq", icon: HelpCircle, key: "faq" },
];- Step 2: Add missing icon imports
Find imports (lines 10-18):
import {
Home,
Settings,
Briefcase,
Phone,
Menu,
X,
UtensilsCrossed,
Mic2,
MapPin,
} from "lucide-react";Change to:
import {
Home,
Settings,
Briefcase,
Phone,
Menu,
X,
UtensilsCrossed,
Mic2,
MapPin,
Star,
Info,
Mail,
HelpCircle,
} from "lucide-react";- Step 3: Add missing labels to NAV_LABELS
Find lines 38-55:
const NAV_LABELS: Record<string, Record<string, string>> = {
en: {
home: "Home",
shows: "Shows",
private: "Private",
workshop: "Workshop",
performances: "Artists",
venue: "Venue",
},
vi: {
home: "Trang chủ",
shows: "Suất diễn",
private: "Sự kiện",
workshop: "Workshop",
performances: "Nghệ sĩ",
venue: "Thuê venue",
},
};Change to:
const NAV_LABELS: Record<string, Record<string, string>> = {
en: {
home: "Home",
shows: "Shows",
private: "Private",
workshop: "Workshop",
performances: "Artists",
venue: "Venue",
reviews: "Reviews",
about: "About",
contact: "Contact",
faq: "FAQ",
},
vi: {
home: "Trang chủ",
shows: "Suất diễn",
private: "Sự kiện",
workshop: "Workshop",
performances: "Nghệ sĩ",
venue: "Thuê venue",
reviews: "Đánh giá",
about: "Giới thiệu",
contact: "Liên hệ",
faq: "Câu hỏi",
},
};- Step 4: Adjust grid for 10 items
Find line 210:
<div className="grid grid-cols-3 gap-3">Change to:
<div className="grid grid-cols-3 gap-3 max-h-[50vh] overflow-y-auto">- Step 5: Commit
git add apps/frontend/components/layout/mobile-bottom-dock.tsx
git commit -m "feat: add all missing nav links to mobile dock"Task 5: Fix click-outside for mobile nav
Files:
-
Modify:
apps/frontend/components/layout/mobile-bottom-dock.tsx:117-127 -
Step 1: Replace mousedown with pointerdown
Find lines 117-127:
// Close on click outside
useEffect(() => {
if (!expanded) return;
const handleClickOutside = (e: MouseEvent) => {
if (dockRef.current && !dockRef.current.contains(e.target as Node)) {
setExpanded(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [expanded]);Change to:
// Close on click outside (pointerdown handles both mouse and touch)
useEffect(() => {
if (!expanded) return;
const handlePointerDown = (e: PointerEvent) => {
if (dockRef.current && !dockRef.current.contains(e.target as Node)) {
setExpanded(false);
}
};
document.addEventListener("pointerdown", handlePointerDown);
return () => document.removeEventListener("pointerdown", handlePointerDown);
}, [expanded]);- Step 2: Commit
git add apps/frontend/components/layout/mobile-bottom-dock.tsx
git commit -m "fix: use pointerdown instead of mousedown for click-outside"Verification
- Step 1: Run type check
cd apps/frontend && npx tsc --noEmitExpected: No errors
- Step 2: Run lint
cd apps/frontend && npm run lintExpected: No errors
- Step 3: Start dev server and verify visually
cd apps/frontend && npm run devCheck:
- Desktop: Nav links visible directly in header (no MENU button)
- Desktop: Hover on "More" shows dropdown with remaining 4 links
- Mobile: FAB opens bottom sheet with all 10 nav items
- Mobile: Social links and phone use config values
Summary of Changes
| Task | What Changed |
|---|---|
| 1 | Added CONTACT_INFO export to nav-links.ts |
| 2 | Replaced hidden MENU button with visible nav links + hover MegaMenu for "More" |
| 3 | Mobile dock uses CONTACT_INFO for phone and socials |
| 4 | Mobile dock shows all 10 nav links with labels |
| 5 | Mobile click-outside uses pointerdown instead of mousedown |