Skip to content
Open
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
83 changes: 83 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,89 @@
}
}

/* NAVBAR BASE */
.navbar {
position: sticky;
top: 0;
z-index: 1000;

display: flex;
justify-content: space-between;
align-items: center;

padding: 14px 40px;

/* Glassmorphism effect */
background: rgba(255, 255, 255, 0.65);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);

border-bottom: 1px solid rgba(0, 0, 0, 0.06);
transition: all 0.3s ease;
}

/* LOGO */
.logo {
font-size: 20px;
font-weight: 600;
letter-spacing: 0.5px;
}

/* MENU */
.menu {
display: flex;
list-style: none;
gap: 36px; /* better spacing */
}

/* LINKS */
.menu li a {
position: relative;
text-decoration: none;
color: #333;
font-size: 15px;
font-weight: 500;

display: flex;
align-items: center;
gap: 6px;

transition: all 0.25s ease;
}

/* HOVER EFFECT (smooth slide underline) */
.menu li a::after {
content: "";
position: absolute;
bottom: -6px;
left: 50%;
width: 0%;
height: 2px;
background: #6366f1;
transition: all 0.3s ease;
transform: translateX(-50%);
}

.menu li a:hover {
color: #6366f1;
}

.menu li a:hover::after {
width: 100%;
}

/* SUBTLE ICON STYLE (for dropdown icons) */
.menu li a svg {
opacity: 0.6;
transition: transform 0.3s ease;
}

/* ICON ANIMATION ON HOVER */
.menu li a:hover svg {
transform: translateY(2px);
opacity: 1;
}

@media (max-width: 1024px) {
margin-top: 20px;
flex-wrap: wrap;
Expand Down
109 changes: 79 additions & 30 deletions src/components/layout/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import React, { useState, useRef, useEffect } from "react";
import { Link, useLocation } from "react-router-dom";
import { FileText, Menu, X, LogOut, Crown, Copy, Check, Star, ChevronDown } from "lucide-react";
Expand Down Expand Up @@ -35,7 +36,7 @@ function WalletMenu({ address, isPremium }) {
<div className="relative" ref={ref}>
<button
onClick={() => setOpen((o) => !o)}
className="flex items-center gap-2 h-9 pl-3 pr-3.5 rounded-full bg-white/5 border border-white/10 hover:bg-white/10 hover:border-white/20 transition-all text-sm font-medium text-white"
className="flex items-center gap-2 h-9 pl-3 pr-3.5 rounded-full bg-white/5 border border-white/10 hover:bg-white/10 hover:border-white/30 transition-all duration-200 text-sm font-medium text-white"
>
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-60" />
Expand All @@ -51,12 +52,14 @@ function WalletMenu({ address, isPremium }) {

<AnimatePresence>
{open && (
<motion.div
initial={{ opacity: 0, y: 8, scale: 0.96 }}
<Motion.div
initial={{ opacity: 0, y: 6, scale: 0.97 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 6, scale: 0.97 }}
transition={{ duration: 0.15 }}
className="absolute right-0 mt-2 w-56 bg-[#0a0a0a] border border-white/10 rounded-2xl shadow-2xl overflow-hidden z-50"
transition={{ duration: 0.18 }}
className="absolute right-0 mt-3 w-56 bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl shadow-[0_10px_40px_rgba(0,0,0,0.6)] overflow-hidden z-50"
>
<div className="px-4 pt-4 pb-3 border-b border-white/[0.06]">
<p className="text-[11px] text-zinc-600 uppercase tracking-widest font-semibold mb-1">
Expand All @@ -66,15 +69,8 @@ function WalletMenu({ address, isPremium }) {
<span className="font-mono text-xs text-zinc-300 truncate mr-2">
{truncate(address)}
</span>
<button
onClick={handleCopy}
className="shrink-0 text-zinc-500 hover:text-white transition-colors"
aria-label="Copy address"
>
{copied
? <Check className="w-3.5 h-3.5 text-emerald-400" />
: <Copy className="w-3.5 h-3.5" />
}
<button onClick={handleCopy}>
{copied ? <Check className="w-3.5 h-3.5 text-emerald-400" /> : <Copy className="w-3.5 h-3.5" />}
</button>
</div>
</div>
Expand Down Expand Up @@ -104,7 +100,8 @@ function WalletMenu({ address, isPremium }) {
);
}

// Edit Dropdown Component
/* ===== ALL DROPDOWNS FULLY INCLUDED ===== */

function EditDropdown() {
const [isOpen, setIsOpen] = useState(false);
const [locked, setLocked] = useState(false);
Expand Down Expand Up @@ -138,6 +135,8 @@ function EditDropdown() {
];

return (
<div className="relative" onMouseEnter={() => setIsOpen(true)} onMouseLeave={() => setIsOpen(false)}>
<button className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-all duration-200 whitespace-nowrap group relative px-1 py-1">
<div
className="relative"
onMouseEnter={() => !locked && setIsOpen(true)}
Expand All @@ -153,11 +152,23 @@ function EditDropdown() {
}}
className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-colors whitespace-nowrap group cursor-pointer">
Edit
<ChevronDown className={`w-4 h-4 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`} />
<ChevronDown className={`w-4 h-4 transition-all duration-300 ${isOpen ? "rotate-180 scale-110 text-white" : "opacity-60 group-hover:opacity-100"}`} />
<span className="absolute left-0 -bottom-1 h-[2px] w-0 bg-white transition-all duration-300 group-hover:w-full opacity-70"></span>
</button>

<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 8, scale: 0.96 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 6, scale: 0.97 }}
transition={{ duration: 0.18 }}
className="absolute left-0 mt-3 w-48 bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl shadow-[0_10px_40px_rgba(0,0,0,0.6)] overflow-hidden z-50"
>
<div className="py-2">
{tools.map((tool) => (
<Link key={tool.name} to={tool.path}
className="block px-4 py-2.5 text-sm text-zinc-400 hover:text-white hover:bg-white/10 transition-all duration-200 rounded-lg mx-1">
<Motion.div
initial={{ opacity: 0, y: -10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
Expand Down Expand Up @@ -187,7 +198,6 @@ function EditDropdown() {
);
}

// Convert Dropdown Component
function ConvertDropdown() {
const [isOpen, setIsOpen] = useState(false);
const [locked, setLocked] = useState(false);
Expand Down Expand Up @@ -218,6 +228,8 @@ function ConvertDropdown() {
];

return (
<div className="relative" onMouseEnter={() => setIsOpen(true)} onMouseLeave={() => setIsOpen(false)}>
<button className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-all duration-200 whitespace-nowrap group relative px-1 py-1">
<div
className="relative"
onMouseEnter={() => !locked && setIsOpen(true)}
Expand All @@ -233,11 +245,23 @@ function ConvertDropdown() {
}}
className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-colors whitespace-nowrap group cursor-pointer">
Convert
<ChevronDown className={`w-4 h-4 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`} />
<ChevronDown className={`w-4 h-4 transition-all duration-300 ${isOpen ? "rotate-180 scale-110 text-white" : "opacity-60 group-hover:opacity-100"}`} />
<span className="absolute left-0 -bottom-1 h-[2px] w-0 bg-white transition-all duration-300 group-hover:w-full opacity-70"></span>
</button>

<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 8, scale: 0.96 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 6, scale: 0.97 }}
transition={{ duration: 0.18 }}
className="absolute left-0 mt-3 w-48 bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl shadow-[0_10px_40px_rgba(0,0,0,0.6)] overflow-hidden z-50"
>
<div className="py-2">
{tools.map((tool) => (
<Link key={tool.name} to={tool.path}
className="block px-4 py-2.5 text-sm text-zinc-400 hover:text-white hover:bg-white/10 transition-all duration-200 rounded-lg mx-1">
<Motion.div
initial={{ opacity: 0, y: -10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
Expand Down Expand Up @@ -267,7 +291,6 @@ function ConvertDropdown() {
);
}

// Optimize Dropdown Component
function OptimizeDropdown() {
const [isOpen, setIsOpen] = useState(false);
const [locked, setLocked] = useState(false);
Expand Down Expand Up @@ -299,6 +322,8 @@ function OptimizeDropdown() {
];

return (
<div className="relative" onMouseEnter={() => setIsOpen(true)} onMouseLeave={() => setIsOpen(false)}>
<button className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-all duration-200 whitespace-nowrap group relative px-1 py-1">
<div
className="relative"
onMouseEnter={() => !locked && setIsOpen(true)}
Expand All @@ -314,11 +339,23 @@ function OptimizeDropdown() {
}}
className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-colors whitespace-nowrap group cursor-pointer">
Optimize
<ChevronDown className={`w-4 h-4 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`} />
<ChevronDown className={`w-4 h-4 transition-all duration-300 ${isOpen ? "rotate-180 scale-110 text-white" : "opacity-60 group-hover:opacity-100"}`} />
<span className="absolute left-0 -bottom-1 h-[2px] w-0 bg-white transition-all duration-300 group-hover:w-full opacity-70"></span>
</button>

<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 8, scale: 0.96 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 6, scale: 0.97 }}
transition={{ duration: 0.18 }}
className="absolute left-0 mt-3 w-48 bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl shadow-[0_10px_40px_rgba(0,0,0,0.6)] overflow-hidden z-50"
>
<div className="py-2">
{tools.map((tool) => (
<Link key={tool.name} to={tool.path}
className="block px-4 py-2.5 text-sm text-zinc-400 hover:text-white hover:bg-white/10 transition-all duration-200 rounded-lg mx-1">
<Motion.div
initial={{ opacity: 0, y: -10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
Expand Down Expand Up @@ -348,7 +385,6 @@ function OptimizeDropdown() {
);
}

// Security Dropdown Component
function SecurityDropdown() {
const [isOpen, setIsOpen] = useState(false);
const [locked, setLocked] = useState(false);
Expand Down Expand Up @@ -378,6 +414,8 @@ function SecurityDropdown() {
];

return (
<div className="relative" onMouseEnter={() => setIsOpen(true)} onMouseLeave={() => setIsOpen(false)}>
<button className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-all duration-200 whitespace-nowrap group relative px-1 py-1">
<div
className="relative"
onMouseEnter={() => !locked && setIsOpen(true)}
Expand All @@ -393,11 +431,23 @@ function SecurityDropdown() {
}}
className="flex items-center gap-1.5 text-sm font-medium text-zinc-400 hover:text-white transition-colors whitespace-nowrap group cursor-pointer">
Security
<ChevronDown className={`w-4 h-4 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`} />
<ChevronDown className={`w-4 h-4 transition-all duration-300 ${isOpen ? "rotate-180 scale-110 text-white" : "opacity-60 group-hover:opacity-100"}`} />
<span className="absolute left-0 -bottom-1 h-[2px] w-0 bg-white transition-all duration-300 group-hover:w-full opacity-70"></span>
</button>

<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 8, scale: 0.96 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 6, scale: 0.97 }}
transition={{ duration: 0.18 }}
className="absolute left-0 mt-3 w-48 bg-white/5 backdrop-blur-xl border border-white/10 rounded-2xl shadow-[0_10px_40px_rgba(0,0,0,0.6)] overflow-hidden z-50"
>
<div className="py-2">
{tools.map((tool) => (
<Link key={tool.name} to={tool.path}
className="block px-4 py-2.5 text-sm text-zinc-400 hover:text-white hover:bg-white/10 transition-all duration-200 rounded-lg mx-1">
<Motion.div
initial={{ opacity: 0, y: -10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
Expand Down Expand Up @@ -428,6 +478,8 @@ function SecurityDropdown() {
);
}

/* ===== MAIN NAVBAR ===== */

export function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const { address, isConnected } = useAccount();
Expand All @@ -447,17 +499,19 @@ export function Navbar() {
];

return (
<nav className="border-b border-white/10 bg-black/60 backdrop-blur-xl sticky top-0 z-50">
<nav className="border-b border-white/10 bg-black/80 backdrop-blur-md fixed top-0 left-0 right-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16 items-center gap-4 relative">

<Link to="/" className="flex items-center gap-2 group z-50 shrink-0">
<div className="bg-white text-black p-1.5 rounded-md group-hover:scale-105 transition-transform">
<div className="bg-white text-black p-1.5 rounded-md group-hover:scale-110 group-hover:rotate-1 transition-all duration-300">
<FileText className="h-5 w-5" />
</div>
<span className="font-bold text-xl tracking-tight text-white">QuickPDF</span>
</Link>

<div className="hidden lg:flex gap-10 flex-1 justify-center items-center">
{/* Desktop Navigation - 4 separate hover dropdowns */}
<div className="hidden lg:flex gap-6 absolute left-1/2 transform -translate-x-1/2">
<EditDropdown />
Expand All @@ -472,7 +526,7 @@ export function Navbar() {
href="https://github.com/JhaSourav07/QuickPDF"
target="_blank"
rel="noopener noreferrer"
className="hidden sm:flex items-center gap-1.5 h-9 px-3.5 rounded-full bg-white/5 border border-white/10 hover:bg-white/10 hover:border-white/25 transition-all text-sm font-medium text-zinc-300 hover:text-white group"
className="hidden sm:flex items-center gap-1.5 h-9 px-4 rounded-full bg-white/5 border border-white/10 hover:bg-white/10 hover:border-white/30 transition-all duration-200 text-sm font-medium text-zinc-300 hover:text-white group hover:shadow-[0_0_20px_rgba(255,255,255,0.1)]"
>
<Star className="w-3.5 h-3.5 text-amber-400 group-hover:fill-amber-400 transition-all" />
Star us
Expand All @@ -482,19 +536,13 @@ export function Navbar() {
<WalletMenu address={address} isPremium={isPremium} />
) : (
<div className="hidden sm:block">
<ConnectButton
accountStatus="hidden"
chainStatus="none"
showBalance={false}
label="Connect Wallet"
/>
<ConnectButton label="Connect Wallet" />
</div>
)}

<button
onClick={() => setIsOpen(!isOpen)}
className="lg:hidden p-2 text-zinc-400 hover:text-white transition-colors z-50"
aria-label="Toggle Menu"
>
{isOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
Expand Down Expand Up @@ -551,4 +599,5 @@ export function Navbar() {
</AnimatePresence>
</nav>
);
}
}

Loading