Skip to main content

Documentation Index

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

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

The Navigation component is the primary wayfinding element of witch-dev. On desktop (md and above) it appears as a fixed 80px-wide left sidebar housing icon-only route buttons, each revealing a styled tooltip on hover. On mobile it collapses to a single hamburger button that summons a full-screen animated overlay with staggered list items.

Route configuration

All routes are defined in a single array called ua. Each entry carries a URL path, a themed label drawn from the occult aesthetic of the portfolio, and a icon component sourced from Lucide React.
const ua = [
  { path: "/",         label: "Summon",     icon: rh  }, // Hexagon
  { path: "/about",    label: "Origins",    icon: fh  }, // Sparkles
  { path: "/projects", label: "Grimoire",   icon: bp  }, // BookOpen
  { path: "/skills",   label: "Affinities", icon: th  }, // BrainCircuit
  { path: "/writing",  label: "Scrolls",    icon: sh  }, // Scroll
  { path: "/contact",  label: "Raven",      icon: ih  }, // Mail
];
The labels replace conventional navigation copy (“Home”, “About”, “Contact”) with in-world language that reinforces the dark witch theme throughout the portfolio.

Current routes at a glance

PathLabelLucide Icon
/SummonHexagon
/aboutOriginsSparkles
/projectsGrimoireBookOpen
/skillsAffinitiesBrainCircuit
/writingScrollsScroll
/contactRavenMail

Desktop sidebar

On screens md and wider the component renders a fixed sidebar pinned to the left edge of the viewport. It spans the full viewport height and sits at z-50 so it floats above page content without interfering with modals or overlays at higher z-indices.
// Sidebar container
className="hidden md:flex fixed left-0 top-0 h-screen w-20 flex-col items-center justify-center
           z-50 border-r border-coven-purple-900/50 bg-coven-dark/80 backdrop-blur-md"
Key visual traits:
  • Width: w-20 (80px) — just wide enough to contain the 48px icon buttons with breathing room.
  • Background: bg-coven-dark/80 with backdrop-blur-md — semi-transparent so page content scrolls behind it with a frosted-glass effect.
  • Border: a subtle border-r border-coven-purple-900/50 separates the sidebar from the main content without a harsh line.

Icon buttons

Each route is rendered as a square Link button (48 × 48px, rounded-xl) wrapping a single Lucide icon:
<Link
  to={n.path}
  className={({ isActive }) =>
    `group relative flex items-center justify-center w-12 h-12 rounded-xl transition-all duration-300 ${
      isActive
        ? 'bg-coven-purple-900/80 text-coven-green-400 box-glow-green'
        : 'text-coven-purple-500 hover:text-coven-green-300 hover:bg-coven-purple-900/40'
    }`
  }
>
  <n.icon className="w-6 h-6" />
  <div className="absolute left-16 px-3 py-1 rounded bg-coven-purple-900
                  border border-coven-purple-500/30 text-sm font-mono
                  text-coven-green-300 opacity-0 -translate-x-4
                  pointer-events-none transition-all duration-300
                  group-hover:opacity-100 group-hover:translate-x-0 whitespace-nowrap">
    {n.label}
  </div>
</Link>
The tooltip is an absolutely-positioned div anchored left-16 (64px from the button’s left edge, clearing the sidebar). It starts invisible (opacity-0) and shifted left (-translate-x-4), then slides into view on group-hover via a 300ms CSS transition — no JavaScript required.

Active state styling

React Router’s NavLink-style isActive callback drives the active/inactive class split:
// Active route
'bg-coven-purple-900/80 text-coven-green-400 box-glow-green'

// Inactive route (default + hover)
'text-coven-purple-500 hover:text-coven-green-300 hover:bg-coven-purple-900/40'
box-glow-green is a custom utility defined in the Tailwind config. It applies a green outer box-shadow that makes the active icon appear to emit a soft magical glow.

Mobile menu

On viewports narrower than md the sidebar is hidden and replaced by a hamburger button positioned at the top-right corner (z-50). Tapping it mounts a full-screen overlay.

Overlay

// Overlay container
// z-[60] — sits above the sidebar z-50 when open
// bg-coven-black/90 backdrop-blur-[16px]
The overlay is wrapped in Framer Motion’s AnimatePresence so it fades in on mount and fades out on unmount without leaving a DOM ghost. An X button in the top-right corner closes the menu.

Staggered list items

Each route in the mobile menu animates in with a per-item delay:
transition={{ delay: index * 0.1 }}
This means the first item (Summon) appears immediately, the second (Origins) after 100ms, and so on — creating a cascading reveal effect that reinforces the spellcasting aesthetic.

Customizing routes

To add a new route, append an entry to the ua array:
import { Wand2 } from 'lucide-react';

const ua = [
  // ...existing routes
  { path: "/spells", label: "Spellbook", icon: Wand2 },
];
To remove a route, delete its object from the array. No other changes are needed — the sidebar and mobile menu both iterate over ua at render time.
Keep labels short (one word is ideal). The tooltip container uses whitespace-nowrap, so longer labels will extend the tooltip width without wrapping, but very long strings may overflow outside the viewport on smaller desktop screens.

Build docs developers (and LLMs) love