Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/faq/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function FAQPage() {
>
<button
onClick={() => toggleQuestion(category.id, questionIndex)}
className="w-full px-6 py-4 text-left flex items-center justify-between gap-4 hover:bg-(--color-surface) transition-colors"
className="w-full cursor-pointer px-6 py-4 text-left flex items-center justify-between gap-4 hover:bg-(--color-surface) transition-colors"
>
<span className="font-semibold text-(--color-gray)">{item.question}</span>
<span
Expand Down
141 changes: 102 additions & 39 deletions components/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,129 @@

import Link from "next/link"
import { usePathname } from "next/navigation"
import { useState, useEffect } from "react"
import { useEffect, useState } from "react"
import navigationData from "@/data/navigation.json"

export function Navigation() {
const pathname = usePathname()
const [scrolled, setScrolled] = useState(false)
const [mobileOpen, setMobileOpen] = useState(false)

useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 20)
}
const handleScroll = () => setScrolled(window.scrollY > 20)
window.addEventListener("scroll", handleScroll)
return () => window.removeEventListener("scroll", handleScroll)
}, [])

// Close mobile menu when route changes
useEffect(() => {
setMobileOpen(false)
}, [pathname])

// Close on Escape
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") setMobileOpen(false)
}
window.addEventListener("keydown", onKeyDown)
return () => window.removeEventListener("keydown", onKeyDown)
}, [])

const navClass = `fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
scrolled ? "bg-white/95 dark:bg-gray-900/95 backdrop-blur-md shadow-md" : "bg-white dark:bg-gray-900"
}`

return (
<nav
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
scrolled ? "bg-white/95 dark:bg-gray-900/95 backdrop-blur-md shadow-md" : "bg-white dark:bg-gray-900"
}`}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<Link href="/" className="flex items-center gap-3 group transition-transform hover:scale-105">
<img
src="/images/image.png"
alt="Safe Exam Browser"
className="h-10 w-10 transition-transform group-hover:rotate-12"
/>
<span className="font-bold text-lg text-(--color-primary) dark:text-blue-400">Safe Exam Browser</span>
</Link>
<nav className={navClass}>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<Link href="/" className="flex items-center gap-3 group transition-transform hover:scale-105">
<img
src="/images/image.png"
alt="Safe Exam Browser"
className="h-10 w-10 transition-transform group-hover:rotate-12"
/>
<span className="font-bold text-lg text-(--color-primary) dark:text-blue-400">Safe Exam Browser</span>
</Link>

{/* Desktop nav */}
<div className="hidden md:flex items-center gap-3">
<div className="flex items-center gap-1">
{navigationData.main.map((item, index) => {
const isActive = pathname === item.href
const isExternal = item.external

return (
<Link
key={item.id}
href={item.href}
target={isExternal ? "_blank" : undefined}
rel={isExternal ? "noopener noreferrer" : undefined}
className={`px-4 py-2 rounded-lg font-medium transition-all duration-200 ${
isActive
? "bg-(--color-primary) dark:bg-blue-600 text-white"
: "text-(--color-gray) dark:text-gray-300 hover:bg-(--color-surface) dark:hover:bg-gray-800 hover:text-(--color-primary) dark:hover:text-blue-400"
}`}
style={{ animationDelay: `${index * 50}ms` }}
>
{item.label}
{isExternal && <span className="ml-1 text-xs">↗</span>}
</Link>
)
})}
</div>
</div>

{/* Mobile button */}
<button
type="button"
className="md:hidden inline-flex items-center justify-center rounded-lg p-2
text-(--color-gray) dark:text-gray-300 hover:bg-(--color-surface) dark:hover:bg-gray-800"
aria-label="Toggle navigation menu"
aria-expanded={mobileOpen}
aria-controls="mobile-nav"
onClick={() => setMobileOpen((v) => !v)}
>
{/* simple icon swap */}
<span className="text-xl leading-none">{mobileOpen ? "✕" : "☰"}</span>
</button>
</div>
</div>

<div className="flex items-center gap-3">
<div className="flex items-center gap-1">
{navigationData.main.map((item, index) => {
{/* Mobile dropdown */}
<div
id="mobile-nav"
className={`md:hidden overflow-hidden transition-all duration-300 ${
mobileOpen ? "max-h-96 opacity-100" : "max-h-0 opacity-0"
}`}
>
<div className="px-4 sm:px-6 lg:px-8 pb-4">
<div className="mt-2 rounded-xl border border-black/5 dark:border-white/10 bg-white/90 dark:bg-gray-900/90 backdrop-blur-md shadow-sm p-2">
{navigationData.main.map((item) => {
const isActive = pathname === item.href
const isExternal = item.external

return (
<Link
key={item.id}
href={item.href}
target={isExternal ? "_blank" : undefined}
rel={isExternal ? "noopener noreferrer" : undefined}
className={`px-4 py-2 rounded-lg font-medium transition-all duration-200 ${
isActive
? "bg-(--color-primary) dark:bg-blue-600 text-white"
: "text-(--color-gray) dark:text-gray-300 hover:bg-(--color-surface) dark:hover:bg-gray-800 hover:text-(--color-primary) dark:hover:text-blue-400"
}`}
style={{ animationDelay: `${index * 50}ms` }}
>
{item.label}
{isExternal && <span className="ml-1 text-xs">↗</span>}
</Link>
<Link
key={item.id}
href={item.href}
target={isExternal ? "_blank" : undefined}
rel={isExternal ? "noopener noreferrer" : undefined}
onClick={() => setMobileOpen(false)}
className={`flex items-center justify-between w-full px-4 py-3 rounded-lg font-medium transition-all duration-200 ${
isActive
? "bg-(--color-primary) dark:bg-blue-600 text-white"
: "text-(--color-gray) dark:text-gray-300 hover:bg-(--color-surface) dark:hover:bg-gray-800 hover:text-(--color-primary) dark:hover:text-blue-400"
}`}
>
<span>{item.label}</span>
{isExternal && <span className="text-sm">↗</span>}
</Link>
)
})}
</div>
</div>
</div>
</div>
</nav>
</nav>
)
}