Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/aurora-drift/llms.txt

Use this file to discover all available pages before exploring further.

Nav renders the fixed top navigation bar that persists across all pages of the Aurora Drift portfolio. It reads the current route from React Router’s useLocation hook to highlight the active link, then animates a shared underline element between links using Framer Motion’s layoutId — so the highlight glides smoothly rather than jumping. The bar itself uses a glassmorphism treatment (semi-transparent navy background + backdrop-blur-md) that lets the aurora background bleed through subtly. Like AuroraBackground and CursorGlow, the component is zero-prop and self-contained; all routing and animation logic lives inside it.

Usage

Place Nav inside your router context (inside <BrowserRouter> or equivalent), above the page outlet. It should be outside AnimatePresence so it stays mounted and can animate the underline across route changes.
// src/App.jsx
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom'
import { AnimatePresence } from 'framer-motion'
import Nav            from './components/Nav'
import AuroraBackground from './components/AuroraBackground'
import CursorGlow     from './components/CursorGlow'
import PageTransition from './components/PageTransition'
import Home           from './pages/Home'
import About          from './pages/About'

function AppInner() {
  const location = useLocation()
  return (
    <>
      <AuroraBackground />
      <CursorGlow />
      <Nav />                         {/* ← always mounted, outside AnimatePresence */}

      <AnimatePresence mode="wait">
        <Routes location={location} key={location.pathname}>
          <Route path="/"      element={<Home />} />
          <Route path="/about" element={<About />} />
          {/* … */}
        </Routes>
      </AnimatePresence>
    </>
  )
}

export default function App() {
  return (
    <BrowserRouter>
      <AppInner />
    </BrowserRouter>
  )
}

Props

Nav accepts no props. It derives active-route state entirely from useLocation.

Route list

The component renders the following eight navigation links. Active detection differs for the root route to avoid false positives from startsWith.
PathLabelActive rule
/HomeExact match — pathname === '/'
/aboutAboutpathname.startsWith('/about')
/projectsProjectspathname.startsWith('/projects')
/skillsSkillspathname.startsWith('/skills')
/workWorkpathname.startsWith('/work')
/case-studiesCase Studiespathname.startsWith('/case-studies')
/blogBlogpathname.startsWith('/blog')
/contactContactpathname.startsWith('/contact')
The active-detection function used internally is:
const isActive = (path) =>
  path === '/' ? location.pathname === '/' : location.pathname.startsWith(path)

Active underline animation

When a link becomes active, a Framer Motion motion.div with layoutId="nav-underline" is rendered as an absolutely-positioned element at the bottom of that link. Because all instances share the same layoutId, Framer Motion automatically moves the single underline element between links with a spring transition.
// Spring config for the underline
transition: { type: 'spring', stiffness: 300, damping: 30 }
The underline itself is a gradient bar running turquoise → cyan → magenta (from-aurora-turquoise via-aurora-cyan to-aurora-magenta).
If you ever render more than one Nav on the page (for example, a sidebar nav in addition to the top bar), give each a unique layoutId — e.g. "top-nav-underline" and "sidebar-nav-underline" — so Framer Motion does not try to animate the underline between the two navs simultaneously.

Visual specification

Header bar

position: fixed; top: 0; left: 0; right: 0; z-index: 40
bg-navy/50 backdrop-blur-md border-b border-white/5
Padding: px-6 py-4 — flex row, space-between

Logo

Link to / rendering a turquoise dot followed by the text N.L.
Dot: w-2 h-2 rounded-full bg-aurora-turquoise
Hover: shadow-[0_0_10px_rgba(45,212,191,0.8)]

Inactive link

text-slate-400 hover:text-aurora-mint
text-sm font-medium transition-colors duration-300

Active link

text-white — no hover override needed
Underline motion.div rendered below with gradient fill

Mobile behaviour

On screens narrower than the md breakpoint (768 px), all navigation links are hidden with hidden md:flex. A hamburger button is rendered in their place:
{/* Hamburger — visible only on mobile */}
<button className="md:hidden ...">
  <svg>/* three-line icon */</svg>
</button>
The hamburger button currently has no onClick handler wired to a mobile menu. It is a UI stub. To add a mobile drawer, connect the button to a state variable, then conditionally render a full-screen overlay or a slide-in sheet component toggled by that state. A common pattern is to use a Framer Motion AnimatePresence + motion.div drawer, or a headless UI Dialog.

How it works

1

Location read

useLocation() from React Router returns the current { pathname }. This is called on every render so isActive always reflects the live route without any additional state.
2

Link rendering

The route array is mapped to <Link> elements from React Router. Each link receives the appropriate active/inactive text class based on isActive(path).
3

Shared underline

Each active link renders a <motion.div layoutId="nav-underline" /> absolutely positioned at bottom: -1px (class -bottom-px). When the active route changes, Framer Motion detects the new instance of the layoutId element and animates the shared node to its new bounding box.
4

Spring transition

The layout animation uses type: 'spring' with stiffness: 300 and damping: 30, producing a responsive but not jarring slide as focus moves between links.

Build docs developers (and LLMs) love