fix: align navbar profile button styling with other nav links

This commit is contained in:
2026-04-26 08:03:50 -05:00
parent 8dfbd3ca5e
commit a49e0d8ff0
+188 -174
View File
@@ -18,6 +18,7 @@ export default function Navbar() {
const searchParams = useSearchParams()
const isLanding = pathname === "/"
const isAuthRoute = pathname.startsWith("/login") || pathname.startsWith("/signup") || pathname.startsWith("/forgot-password") || pathname.startsWith("/reset-password")
const [searchQuery, setSearchQuery] = useState(
() => searchParams.get("q") || "",
@@ -117,193 +118,206 @@ export default function Navbar() {
(route) => route.href !== "/" && route.href !== "/search",
)
// Hide navbar on auth routes
if (isAuthRoute) {
return null
}
return (
<>
<motion.nav
initial={{ opacity: 0, y: "-100%" }}
animate={{ opacity: 1, y: 0 }}
className='w-full flex flex-row px-4 md:px-[10svw] py-2 items-center gap-4 md:gap-8 justify-between border-b border-border sticky top-0 z-50 backdrop-blur-sm bg-background/80'
>
<Link
href='/'
className='flex flex-row gap-2 items-center font-bold text-lg py-1 shrink-0'
<AnimatePresence>
<motion.nav
key='navbar'
initial={{ opacity: 0, y: "-100%" }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: "-100%" }}
className='w-full flex flex-row px-4 md:px-[10svw] py-2 items-center gap-4 md:gap-8 justify-between border-b border-border sticky top-0 z-50 backdrop-blur-sm bg-background/80'
>
<Image
src={logo}
alt='DeckyVault Logo'
className='h-6 my-1 w-auto'
/>
{!isLanding && (
<motion.span className='hidden md:inline-block'>
DeckyVault
</motion.span>
)}
</Link>
<Link
href='/'
className='flex flex-row gap-2 items-center font-bold text-lg py-1 shrink-0'
>
<Image
src={logo}
alt='DeckyVault Logo'
className='h-6 my-1 w-auto'
/>
{!isLanding && (
<motion.span className='hidden md:inline-block'>
DeckyVault
</motion.span>
)}
</Link>
<AnimatePresence>
{!isLanding && (
<motion.label
key='search-bar'
layoutId='search-bar'
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
className={`flex flex-row items-center gap-2 flex-1 bg-text/5 px-2 py-1 rounded-md border placeholder:text-text/60 group transition-colors cursor-text ${
isFocused || forceFocusStyles
? "border-primary/80 ring-2 ring-primary/50 ring-offset-2 ring-offset-background"
: "border-border hover:border-border-active"
}`}
>
<Gamepad2Icon
className={`h-4 w-4 transition-colors shrink-0 ${
<AnimatePresence>
{!isLanding && (
<motion.label
key='search-bar'
layoutId='search-bar'
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
className={`flex flex-row items-center gap-2 flex-1 bg-text/5 px-2 py-1 rounded-md border placeholder:text-text/60 group transition-colors cursor-text ${
isFocused || forceFocusStyles
? "stroke-accent"
: ""
? "border-primary/80 ring-2 ring-primary/50 ring-offset-2 ring-offset-background"
: "border-border hover:border-border-active"
}`}
/>
<input
ref={inputRef}
type='text'
placeholder='search by game or appid...'
value={searchQuery}
onChange={handleSearchChange}
onKeyDown={handleKeyDown}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
className='flex-1 outline-none bg-transparent text-sm min-w-0'
/>
<motion.button>
<CircleXIcon
onClick={() => {
setSearchQuery("")
if (!isLanding) {
const params = new URLSearchParams(
searchParams.toString(),
)
params.delete("q")
router.replace(
`/search?${params.toString()}`,
{
scroll: false,
},
)
}
}}
className={`h-3 w-3 transition-color cursor-default hover:stroke-accent transition-all ${
>
<Gamepad2Icon
className={`h-4 w-4 transition-colors shrink-0 ${
isFocused || forceFocusStyles
? "opacity-100"
: "opacity-0"
? "stroke-accent"
: ""
}`}
/>
</motion.button>
</motion.label>
)}
</AnimatePresence>
{/* Desktop Navigation Links */}
<ul className='hidden md:flex flex-row items-center gap-6 shrink-0'>
{navbarRoutes.map((route) => (
<Link
key={route.href}
href={route.href}
title={`Navigate to ${route.title}`}
className='text-sm font-medium hover:text-primary transition-colors uppercase'
>
{route.title}
</Link>
))}
<AnimatePresence mode='popLayout' initial={false}>
{isSessionLoading ? (
<motion.li
key='loading-placeholder'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className='w-20 h-8 rounded-lg bg-white/3 animate-pulse'
/>
) : session ? (
<motion.li
key='dashboard-link'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className='relative'
>
<button
onClick={() =>
setUserMenuOpen(!userMenuOpen)
}
className='flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-white/5 transition-colors'
>
{session.user.image ? (
<Image
src={session.user.image}
alt=''
width={28}
height={28}
unoptimized
className='h-7 w-7 rounded-full'
/>
) : (
<div className='h-7 w-7 rounded-full bg-secondary flex items-center justify-center'>
<User className='h-3.5 w-3.5 text-text' />
</div>
)}
<span className='text-sm text-text max-w-25 truncate'>
{session.user.name}
</span>
</button>
{userMenuOpen && (
<>
<div
className='fixed inset-0 z-40'
onClick={() =>
setUserMenuOpen(false)
<input
ref={inputRef}
type='text'
placeholder='search by game or appid...'
value={searchQuery}
onChange={handleSearchChange}
onKeyDown={handleKeyDown}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
className='flex-1 outline-none bg-transparent text-sm min-w-0'
/>
<motion.button>
<CircleXIcon
onClick={() => {
setSearchQuery("")
if (!isLanding) {
const params =
new URLSearchParams(
searchParams.toString(),
)
params.delete("q")
router.replace(
`/search?${params.toString()}`,
{
scroll: false,
},
)
}
/>
<div className='absolute right-0 top-full mt-1 w-48 bg-[#1a1020] border border-white/10 rounded-lg shadow-lg z-50 py-1'>
<button
onClick={async () => {
setUserMenuOpen(false)
await authClient.signOut()
}}
className='w-full flex items-center gap-2 px-3 py-2 text-sm text-text/70 hover:text-text hover:bg-text/5 transition-colors'
>
<LogOut className='h-4 w-4' />
Sign out
</button>
</div>
</>
)}
</motion.li>
) : (
<motion.li
key='sign-in-link'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Link
href='/login'
className='text-sm font-medium hover:text-primary transition-colors uppercase'
>
login
</Link>
</motion.li>
}}
className={`h-3 w-3 transition-color cursor-default hover:stroke-accent transition-all ${
isFocused || forceFocusStyles
? "opacity-100"
: "opacity-0"
}`}
/>
</motion.button>
</motion.label>
)}
</AnimatePresence>
</ul>
{/* Mobile Hamburger Button */}
<button
onClick={() => setMobileMenuOpen(true)}
className='flex md:hidden flex-row items-center justify-center p-2 -mr-2 rounded-md hover:bg-text/5 transition-colors'
aria-label='Open menu'
>
<MenuIcon className='h-5 w-5' />
</button>
</motion.nav>
{/* Desktop Navigation Links */}
<ul className='hidden md:flex flex-row items-center gap-6 shrink-0'>
{navbarRoutes.map((route) => (
<Link
key={route.href}
href={route.href}
title={`Navigate to ${route.title}`}
className='text-sm font-medium hover:text-primary transition-colors uppercase'
>
{route.title}
</Link>
))}
<AnimatePresence
mode='popLayout'
initial={false}
>
{isSessionLoading ? (
<motion.li
key='loading-placeholder'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className='w-20 h-8 rounded-lg bg-white/3 animate-pulse'
/>
) : session ? (
<motion.li
key='dashboard-link'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className='relative'
>
<button
onClick={() =>
setUserMenuOpen(!userMenuOpen)
}
className='flex items-center gap-2 text-sm font-medium hover:text-primary transition-colors uppercase'
>
{session.user.image ? (
<Image
src={session.user.image}
alt=''
width={20}
height={20}
unoptimized
className='h-5 w-5 rounded-full'
/>
) : (
<div className='h-5 w-5 rounded-full bg-secondary flex items-center justify-center'>
<User className='h-3 w-3 text-text' />
</div>
)}
<span className='max-w-25 truncate'>
{session.user.name}
</span>
</button>
{userMenuOpen && (
<>
<div
className='fixed inset-0 z-40'
onClick={() =>
setUserMenuOpen(false)
}
/>
<div className='absolute right-0 top-full mt-1 w-48 bg-[#1a1020] border border-white/10 rounded-lg shadow-lg z-50 py-1'>
<button
onClick={async () => {
setUserMenuOpen(false)
await authClient.signOut()
}}
className='w-full flex items-center gap-2 px-3 py-2 text-sm text-text/70 hover:text-text hover:bg-text/5 transition-colors'
>
<LogOut className='h-4 w-4' />
Sign out
</button>
</div>
</>
)}
</motion.li>
) : (
<motion.li
key='sign-in-link'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Link
href='/login'
className='text-sm font-medium hover:text-primary transition-colors uppercase'
>
login
</Link>
</motion.li>
)}
</AnimatePresence>
</ul>
{/* Mobile Hamburger Button */}
<button
onClick={() => setMobileMenuOpen(true)}
className='flex md:hidden flex-row items-center justify-center p-2 -mr-2 rounded-md hover:bg-text/5 transition-colors'
aria-label='Open menu'
>
<MenuIcon className='h-5 w-5' />
</button>
</motion.nav>
</AnimatePresence>
{/* Mobile Sidebar Overlay */}
<AnimatePresence>