Skip to content

Navigation

Structure

┌──────────────────────────────────────────────────────────────────────────────┐
│ [BJ] brettjohnson.xyz SPEAKER CONSULTING PODCAST MEDIA CONTACT │
└──────────────────────────────────────────────────────────────────────────────┘

The navigation bar is a sticky header that sits at the top of all pages. On mobile it collapses into a hamburger menu [≡].

Layout

<header className="sticky top-0 z-10 border-b border-brand-gray bg-brand-charcoal/95 backdrop-blur-sm">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<nav className="flex h-16 items-center justify-between">
<Logo />
<DesktopLinks />
<MobileMenuButton />
</nav>
</div>
</header>
LabelRouteNotes
Speaker/speakingDrops into services grid section on home
Consulting/consultingLinks to consulting sub-section
Podcast/podcastAnglerPhish + Online Fraudcast page
Media/mediaPress kit and appearances
Training/trainingLaw enforcement training portal
Contact/contactBooking form and Calendly

Active state: border-b-2 border-brand-red text-white (underline in red). Default state: text-white/70 hover:text-white transition-colors.

Mobile Navigation

Breakpoint: < md (768px). The desktop link list is hidden; a hamburger button appears.

On toggle, a full-width dropdown slides down:

┌──────────────────────────────┐
│ [BJ] brettjohnson.xyz [✕] │
├──────────────────────────────┤
│ SPEAKER │
│ CONSULTING │
│ PODCAST │
│ MEDIA │
│ TRAINING │
│ CONTACT │
└──────────────────────────────┘
  • Background: bg-brand-charcoal
  • Each link: py-4 px-6 text-base font-semibold border-b border-brand-gray
  • Active: text-brand-red

Accessibility

  • Nav uses <nav aria-label="Main navigation">
  • Mobile toggle button uses aria-expanded and aria-controls
  • Active page link uses aria-current="page"
  • Focus trap is active while mobile menu is open

Scroll Behavior

The nav background starts fully transparent on the hero section and transitions to bg-brand-charcoal/95 backdrop-blur-sm after scrolling past 60px.

const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 60);
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);