@@ -36,6 +36,16 @@ export default function Navbar() {
3636 : [ ] ) ,
3737 ] , [ profile ?. role ] ) ;
3838
39+ const mobileNavLinks = useMemo ( ( ) => [
40+ { name : 'Fonts' , path : '/fonts' , icon : Type } ,
41+ { name : 'Pairing' , path : '/pairing' , icon : Combine } ,
42+ { name : 'CLI' , path : '/cli' , icon : Terminal , badge : 'NEW' } ,
43+ ...( profile ?. role === 'member' || profile ?. role === 'admin'
44+ ? [ { name : 'Members' , path : '/members' , icon : Users } ,
45+ { name : 'Upload' , path : '/upload' , icon : Upload } , ]
46+ : [ ] ) ,
47+ ] , [ profile ?. role ] ) ;
48+
3949 return (
4050 < >
4151 < nav
@@ -224,32 +234,50 @@ export default function Navbar() {
224234
225235 { /* Mobile Bottom Dock */ }
226236 < div className = "md:hidden fixed bottom-0 left-1/2 -translate-x-1/2 z-100" >
227- < nav className = "flex items-center justify-evenly w-screen py-2.5 bg-[rgb(var(--color-background)/0.6)] backdrop-blur-3xl border-t border-[rgb(var(--color-border)/0.3)] shadow-2xl" >
228- { navLinks . map ( ( link ) => {
237+ < nav className = "flex items-center justify-evenly w-screen px-2 py-2.5 bg-[rgb(var(--color-background)/0.6)] backdrop-blur-3xl border-t border-[rgb(var(--color-border)/0.3)] shadow-2xl" >
238+ { mobileNavLinks . map ( ( link ) => {
229239 const Icon = link . icon ;
230240 const isActive = location . pathname === link . path ;
231241 return (
232242 < Link
233243 key = { link . path }
234244 to = { link . path }
235245 onClick = { async ( ) => {
236- await Haptics . impact ( { style : ImpactStyle . Light } ) ;
246+ await Haptics . selectionChanged ( )
237247 } }
238248 className = { cn (
239- "relative flex flex-col items-center justify-center w -12 h-12 rounded-full transition-all duration-300" ,
249+ "relative flex flex-col items-center justify-center h -12 px-4 rounded-full transition-all duration-300" ,
240250 isActive
241251 ? "text-[rgb(var(--color-foreground))]"
242252 : "text-[rgb(var(--color-muted-foreground))] hover:text-[rgb(var(--color-foreground))] hover:bg-[rgb(var(--color-foreground)/0.1)]"
243253 ) }
254+ style = { { WebkitTapHighlightColor : 'transparent' } }
244255 >
245- < motion . div
246- whileTap = { { scale : 0.9 } }
247- className = "flex flex-col items-center justify-center"
248- >
249- < Icon size = { 20 } className = { cn ( "transition-transform duration-300" , isActive && "scale-110" ) } />
250- < div className = { cn ( `uppercase text-xs font-bricolage-grotesque pt-1 ${ profile ?. role === 'admin' ? 'hidden' : 'block' } ` ) } > { link . name } </ div >
251- </ motion . div >
252- < div className = { cn ( "absolute inset-0 bg-[rgb(var(--color-foreground)/0.3)] blur-md rounded-full -z-10 opacity-0 group-hover:opacity-100 transition-opacity" , isActive && "opacity-100" ) } />
256+ { /* Animated Background Pill */ }
257+ { isActive && (
258+ < motion . div
259+ layoutId = "active-nav-pill"
260+ className = "absolute inset-0 bg-white/10 rounded-full"
261+ transition = { { type : 'spring' , stiffness : 300 , damping : 25 } }
262+ />
263+ ) }
264+
265+ { /* Icon & Label */ }
266+ < span className = "relative z-10 flex items-center gap-2" >
267+ < Icon size = { 20 } strokeWidth = { isActive ? 3.5 : 2 } />
268+
269+ { isActive && (
270+ < motion . span
271+ initial = { { opacity : 0 , width : 0 } }
272+ animate = { { opacity : 1 , width : 'auto' } }
273+ exit = { { opacity : 0 , width : 0 } }
274+ className = "font-black text-md overflow-hidden whitespace-nowrap"
275+ >
276+ { link . name }
277+ </ motion . span >
278+ ) }
279+ </ span >
280+
253281 { link . badge && (
254282 < span className = "absolute top-1 right-1 flex h-2 w-2" >
255283 < span className = "animate-ping absolute inline-flex h-full w-full rounded-full bg-[rgb(var(--color-success)/0.75)]" > </ span >
@@ -259,31 +287,6 @@ export default function Navbar() {
259287 </ Link >
260288 ) ;
261289 } ) }
262-
263- { /* Mobile Upload Button */ }
264- { ( profile ?. role === 'member' || profile ?. role === 'admin' ) && (
265- < Link
266- to = "/upload"
267- onClick = { async ( ) => {
268- await Haptics . impact ( { style : ImpactStyle . Light } ) ;
269- } }
270- className = { cn (
271- "relative flex flex-col items-center justify-center w-12 h-12 rounded-full transition-all duration-300" ,
272- location . pathname === '/upload'
273- ? "text-[rgb(var(--color-foreground))]"
274- : "text-[rgb(var(--color-muted-foreground))] hover:text-[rgb(var(--color-foreground))] hover:bg-[rgb(var(--color-foreground)/0.1)]"
275- ) }
276- >
277- < motion . div
278- whileTap = { { scale : 0.9 } }
279- className = "flex flex-col items-center justify-center"
280- >
281- < Upload size = { 18 } className = { cn ( "transition-transform duration-300" , location . pathname === '/upload' && "scale-110" ) } />
282- < div className = { cn ( "uppercase text-[10px] font-bricolage-grotesque pt-1" ) } > Upload</ div >
283- </ motion . div >
284- < div className = { cn ( "absolute inset-0 bg-[rgb(var(--color-background)/0.3)] blur-xl rounded-full -z-10 opacity-0 group-hover:opacity-100 transition-opacity" , location . pathname === '/upload' && "opacity-100" ) } />
285- </ Link >
286- ) }
287290 </ nav >
288291 </ div >
289292 </ >
0 commit comments