Skip to content

Commit c650ed9

Browse files
authored
Merge pull request #295 from codeunia-dev/enhance/mobileresponsive
Enhance: Improve Mobile Responsiveness & Navigation Experience
2 parents 1b8368d + 6a1bf51 commit c650ed9

File tree

2 files changed

+153
-125
lines changed

2 files changed

+153
-125
lines changed

components/PremiumButton.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import { User } from '@supabase/supabase-js';
1010
interface PremiumButtonProps {
1111
user: User;
1212
className?: string;
13+
compact?: boolean;
1314
}
1415

15-
export default function PremiumButton({ user, className = '' }: PremiumButtonProps) {
16+
export default function PremiumButton({ user, className = '', compact = false }: PremiumButtonProps) {
1617
const [isPremium, setIsPremium] = useState(false);
1718
const [isLoading, setIsLoading] = useState(true);
1819
const supabase = createClient();
@@ -66,7 +67,7 @@ export default function PremiumButton({ user, className = '' }: PremiumButtonPro
6667
{isPremium ? (
6768
<button
6869
onClick={handlePremiumClick}
69-
className={`group relative inline-flex items-center px-3 py-1.5 text-sm font-semibold rounded-lg transition-all duration-300 bg-gradient-to-r from-yellow-400 via-yellow-500 to-orange-500 text-white shadow-lg hover:shadow-xl hover:scale-105 transform overflow-hidden ${className}`}
70+
className={`group relative inline-flex items-center px-3 py-1.5 text-sm font-semibold rounded-lg transition-all duration-300 bg-gradient-to-r from-yellow-400 via-yellow-500 to-orange-500 text-white shadow-lg hover:shadow-xl hover:scale-105 transform overflow-hidden ${compact ? 'px-2.5 py-1 text-xs' : ''} ${className}`}
7071
>
7172
{/* Animated background effect */}
7273
<div className="absolute inset-0 bg-gradient-to-r from-yellow-300 via-yellow-400 to-orange-400 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
@@ -78,25 +79,27 @@ export default function PremiumButton({ user, className = '' }: PremiumButtonPro
7879
<div className="absolute -bottom-1 -left-2 w-1.5 h-1.5 bg-white rounded-full opacity-0 group-hover:opacity-100 group-hover:animate-ping" style={{ animationDelay: '0.4s' }}></div>
7980
</div>
8081

81-
<Crown className="mr-1.5 h-4 w-4 relative z-10" />
82+
<Crown className={`relative z-10 ${compact ? 'h-3.5 w-3.5 mr-1' : 'mr-1.5 h-4 w-4'}`} />
8283
<span className="relative z-10">Premium</span>
8384
</button>
8485
) : (
8586
<Link href="/premium">
8687
<button
87-
className={`group relative inline-flex items-center px-3 py-1.5 text-sm font-semibold rounded-lg transition-all duration-300 bg-gradient-to-r from-blue-500 via-purple-600 to-indigo-600 text-white hover:from-blue-600 hover:via-purple-700 hover:to-indigo-700 shadow-lg hover:shadow-xl hover:scale-105 transform overflow-hidden ${className}`}
88+
className={`group relative inline-flex items-center px-3 py-1.5 text-sm font-semibold rounded-lg transition-all duration-300 bg-gradient-to-r from-blue-500 via-purple-600 to-indigo-600 text-white hover:from-blue-600 hover:via-purple-700 hover:to-indigo-700 shadow-lg hover:shadow-xl hover:scale-105 transform overflow-hidden ${compact ? 'px-2.5 py-1 text-xs' : ''} ${className}`}
8889
>
8990
{/* Animated background effect */}
9091
<div className="absolute inset-0 bg-gradient-to-r from-blue-400 via-purple-500 to-indigo-500 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
9192

9293
{/* Glow effect */}
9394
<div className="absolute inset-0 bg-gradient-to-r from-blue-400 via-purple-500 to-indigo-500 blur-lg opacity-0 group-hover:opacity-50 transition-opacity duration-300"></div>
9495

95-
<Star className="mr-1.5 h-4 w-4 relative z-10" />
96+
<Star className={`relative z-10 ${compact ? 'h-3.5 w-3.5 mr-1' : 'mr-1.5 h-4 w-4'}`} />
9697
<span className="relative z-10">Premium</span>
9798

9899
{/* Arrow indicator */}
99-
<Sparkles className="ml-1 h-3 w-3 relative z-10 opacity-0 group-hover:opacity-100 group-hover:translate-x-1 transition-all duration-300" />
100+
{!compact && (
101+
<Sparkles className="ml-1 h-3 w-3 relative z-10 opacity-0 group-hover:opacity-100 group-hover:translate-x-1 transition-all duration-300" />
102+
)}
100103
</button>
101104
</Link>
102105
)}

components/header.tsx

Lines changed: 144 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -65,137 +65,162 @@ export default function Header() {
6565
]
6666

6767
return (
68-
<header className="fixed top-0 z-50 w-full border-b bg-background/80 backdrop-blur-xl supports-[backdrop-filter]:bg-background/60 shadow-sm">
69-
<div className="container flex h-14 sm:h-16 items-center justify-between px-3 sm:px-4">
70-
{/* logo section - left */}
71-
<div className="flex items-center flex-shrink-0">
72-
<Link href="/" className="hover:scale-105 transition-transform duration-200">
73-
<CodeuniaLogo size="md" noLink={true} showText={true} instanceId="header" />
74-
</Link>
75-
</div>
76-
77-
{/* desktop nav - center */}
78-
<nav className="hidden md:flex items-center space-x-8 mx-auto">
79-
{navItems.map((item) => (
80-
<Link
81-
key={item.href}
82-
href={item.href}
83-
className={`text-sm font-medium transition-colors relative group ${
84-
isActive(item.href)
85-
? "text-primary"
86-
: "text-foreground hover:text-primary"
87-
}`}
88-
>
89-
{item.label}
90-
<span
91-
className={`absolute -bottom-1 left-0 h-0.5 bg-primary transition-all duration-300 ${
68+
<>
69+
<header className="fixed top-0 z-50 w-full border-b bg-background/80 backdrop-blur-xl supports-[backdrop-filter]:bg-background/60 shadow-sm">
70+
<div className="container flex h-14 sm:h-16 items-center justify-between px-2 sm:px-4">
71+
{/* logo section - left */}
72+
<div className="flex items-center flex-shrink-0">
73+
<Link href="/" className="hover:scale-105 transition-transform duration-200">
74+
<CodeuniaLogo size="md" noLink={true} showText={true} instanceId="header" />
75+
</Link>
76+
</div>
77+
78+
{/* desktop nav - center */}
79+
<nav className="hidden md:flex items-center space-x-8 mx-auto">
80+
{navItems.map((item) => (
81+
<Link
82+
key={item.href}
83+
href={item.href}
84+
className={`text-sm font-medium transition-colors relative group ${
9285
isActive(item.href)
93-
? "w-full"
94-
: "w-0 group-hover:w-full"
86+
? "text-primary"
87+
: "text-foreground hover:text-primary"
9588
}`}
96-
></span>
97-
</Link>
98-
))}
99-
</nav>
100-
101-
{/* desktop auth & theme - right */}
102-
<div className="hidden md:flex items-center space-x-4 flex-shrink-0">
103-
{/* <ThemeToggle /> */}
104-
{loading ? (
105-
<div className="text-sm text-muted-foreground">Loading...</div>
106-
) : user ? (
107-
<div className="flex items-center space-x-2">
108-
<PremiumButton user={user} />
109-
<UserDisplay userId={user.id} showCodeuniaId={false} />
110-
<UserIcon />
111-
</div>
112-
) : (
113-
<>
114-
<Button variant="ghost" asChild className="hover:scale-105 transition-transform">
115-
<Link href={`/auth/signin?returnUrl=${encodeURIComponent(pathname)}`}>Sign In</Link>
116-
</Button>
117-
<Button asChild className="glow-effect hover:scale-105 transition-all duration-300">
118-
<Link href={`/auth/signup?returnUrl=${encodeURIComponent(pathname)}`}>Sign Up</Link>
119-
</Button>
120-
</>
121-
)}
122-
</div>
89+
>
90+
{item.label}
91+
<span
92+
className={`absolute -bottom-1 left-0 h-0.5 bg-primary transition-all duration-300 ${
93+
isActive(item.href)
94+
? "w-full"
95+
: "w-0 group-hover:w-full"
96+
}`}
97+
></span>
98+
</Link>
99+
))}
100+
</nav>
123101

124-
{/* mobile menu button */}
125-
<div className="flex md:hidden items-center space-x-1">
126-
{/* <ThemeToggle /> */}
127-
{!loading && user && (
128-
<div className="flex items-center space-x-1">
129-
<PremiumButton user={user} />
130-
<UserIcon />
131-
</div>
132-
)}
133-
<Button
134-
variant="ghost"
135-
size="icon"
136-
onClick={() => setIsMenuOpen(!isMenuOpen)}
137-
className={`hover:scale-105 transition-all duration-200 ml-1 ${
138-
isMenuOpen ? 'bg-muted/50' : ''
139-
}`}
140-
>
141-
{isMenuOpen ? <X className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
142-
</Button>
102+
{/* desktop auth & theme - right */}
103+
<div className="hidden md:flex items-center space-x-3 flex-shrink-0">
104+
{/* <ThemeToggle /> */}
105+
{loading ? (
106+
<div className="text-sm text-muted-foreground">Loading...</div>
107+
) : user ? (
108+
<div className="flex items-center space-x-3">
109+
<PremiumButton user={user} />
110+
<UserDisplay userId={user.id} showCodeuniaId={false} />
111+
<UserIcon />
112+
</div>
113+
) : (
114+
<>
115+
<Button variant="ghost" asChild className="hover:scale-105 transition-transform px-3 py-1.5 h-auto text-sm">
116+
<Link href={`/auth/signin?returnUrl=${encodeURIComponent(pathname)}`}>Sign In</Link>
117+
</Button>
118+
<Button asChild className="glow-effect hover:scale-105 transition-all duration-300 px-3 py-1.5 h-auto text-sm">
119+
<Link href={`/auth/signup?returnUrl=${encodeURIComponent(pathname)}`}>Sign Up</Link>
120+
</Button>
121+
</>
122+
)}
123+
</div>
124+
125+
{/* mobile menu button */}
126+
<div className="flex md:hidden items-center space-x-1">
127+
{/* <ThemeToggle /> */}
128+
{!loading && user && (
129+
<div className="flex items-center space-x-1">
130+
<PremiumButton user={user} compact />
131+
<UserIcon />
132+
</div>
133+
)}
134+
<Button
135+
variant="ghost"
136+
size="icon"
137+
onClick={() => setIsMenuOpen(!isMenuOpen)}
138+
className={`hover:scale-105 transition-all duration-200 ml-1 w-8 h-8 ${
139+
isMenuOpen ? 'bg-muted/50' : ''
140+
}`}
141+
>
142+
{isMenuOpen ? <X className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
143+
</Button>
144+
</div>
143145
</div>
144-
</div>
146+
</header>
145147

146-
{/* mobile nav*/}
148+
{/* mobile nav - slide from right (outside header for full page overlay) */}
147149
{isMenuOpen && (
148-
<div className="mobile-menu-container md:hidden border-t bg-background/95 backdrop-blur-xl animate-in slide-in-from-top-2 duration-200">
149-
<nav className="container px-4 py-4 space-y-3">
150-
{/* Navigation Links */}
151-
<div className="space-y-1">
152-
{navItems.map((item) => (
153-
<Link
154-
key={item.href}
155-
href={item.href}
156-
className={`block text-sm font-medium transition-colors py-2.5 px-3 rounded-md relative ${
157-
isActive(item.href)
158-
? "text-primary font-semibold bg-primary/10"
159-
: "text-foreground hover:text-primary hover:bg-muted/50"
160-
}`}
150+
<>
151+
{/* Backdrop overlay */}
152+
<div
153+
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-[60] md:hidden animate-in fade-in duration-200"
154+
onClick={() => setIsMenuOpen(false)}
155+
/>
156+
157+
{/* Side drawer */}
158+
<div className="mobile-menu-container fixed top-0 right-0 bottom-0 w-[280px] max-w-[85vw] bg-background border-l shadow-2xl z-[70] md:hidden animate-in slide-in-from-right duration-300">
159+
<nav className="flex flex-col h-full">
160+
{/* Header with close button */}
161+
<div className="flex items-center justify-between px-4 py-4 border-b">
162+
<span className="text-sm font-semibold">Menu</span>
163+
<Button
164+
variant="ghost"
165+
size="icon"
161166
onClick={() => setIsMenuOpen(false)}
167+
className="h-8 w-8 hover:bg-muted"
162168
>
163-
{item.label}
164-
{isActive(item.href) && (
165-
<span className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-primary rounded-full"></span>
166-
)}
167-
</Link>
168-
))}
169-
</div>
170-
171-
{/* User Actions */}
172-
{!loading && user && (
173-
<div className="pt-3 border-t border-border">
174-
<div className="flex items-center space-x-2 py-2 px-3">
175-
<UserIcon />
176-
<div className="flex-1 min-w-0">
177-
<UserDisplay userId={user.id} showCodeuniaId={false} />
178-
</div>
179-
</div>
169+
<X className="h-4 w-4" />
170+
</Button>
180171
</div>
181-
)}
182172

183-
{/* Auth Buttons for non-authenticated users */}
184-
{!loading && !user && (
185-
<div className="pt-3 border-t border-border">
186-
<div className="flex space-x-2">
187-
<Button variant="ghost" asChild className="flex-1 text-sm">
188-
<Link href={`/auth/signin?returnUrl=${encodeURIComponent(pathname)}`}>Sign In</Link>
189-
</Button>
190-
<Button asChild className="flex-1 glow-effect text-sm">
191-
<Link href={`/auth/signup?returnUrl=${encodeURIComponent(pathname)}`}>Sign Up</Link>
192-
</Button>
173+
{/* Scrollable content */}
174+
<div className="flex-1 overflow-y-auto px-4 py-4">
175+
{/* Navigation Links */}
176+
<div className="space-y-1">
177+
{navItems.map((item) => (
178+
<Link
179+
key={item.href}
180+
href={item.href}
181+
className={`block text-sm font-medium transition-colors py-2.5 px-3 rounded-md relative ${
182+
isActive(item.href)
183+
? "text-primary font-semibold bg-primary/10"
184+
: "text-foreground hover:text-primary hover:bg-muted/50"
185+
}`}
186+
onClick={() => setIsMenuOpen(false)}
187+
>
188+
{item.label}
189+
{isActive(item.href) && (
190+
<span className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-primary rounded-full"></span>
191+
)}
192+
</Link>
193+
))}
193194
</div>
195+
196+
{/* User Actions */}
197+
{!loading && user && (
198+
<div className="pt-3 mt-3 border-t border-border">
199+
<div className="flex items-center space-x-2 py-2 px-3">
200+
<UserIcon />
201+
<div className="flex-1 min-w-0">
202+
<UserDisplay userId={user.id} showCodeuniaId={false} />
203+
</div>
204+
</div>
205+
</div>
206+
)}
207+
208+
{/* Auth Buttons for non-authenticated users */}
209+
{!loading && !user && (
210+
<div className="pt-3 mt-3 border-t border-border space-y-2">
211+
<Button variant="ghost" asChild className="w-full text-sm">
212+
<Link href={`/auth/signin?returnUrl=${encodeURIComponent(pathname)}`}>Sign In</Link>
213+
</Button>
214+
<Button asChild className="w-full glow-effect text-sm">
215+
<Link href={`/auth/signup?returnUrl=${encodeURIComponent(pathname)}`}>Sign Up</Link>
216+
</Button>
217+
</div>
218+
)}
194219
</div>
195-
)}
196-
</nav>
197-
</div>
220+
</nav>
221+
</div>
222+
</>
198223
)}
199-
</header>
224+
</>
200225
)
201226
}

0 commit comments

Comments
 (0)