website refactor

This commit is contained in:
2026-01-20 00:10:30 +01:00
parent 92bf97e21a
commit 6df1b50536
14 changed files with 511 additions and 351 deletions

View File

@@ -141,9 +141,9 @@
@layer utilities {
/* Precision Racing Utilities */
.glass-panel {
background: rgba(20, 22, 25, 0.7);
backdrop-filter: blur(12px);
border: 1px solid var(--color-outline);
background: rgba(20, 22, 25, 0.85);
backdrop-filter: blur(16px);
border-bottom: 1px solid var(--color-outline);
}
.subtle-gradient {
@@ -161,7 +161,7 @@
left: 0;
width: 100%;
height: 1px;
background: linear-gradient(90deg, var(--color-primary) 0%, transparent 100%);
background: linear-gradient(90deg, transparent 0%, var(--color-primary) 50%, transparent 100%);
opacity: 0.5;
}

View File

@@ -21,7 +21,6 @@ export function AdminToolbar({
return (
<ControlBar
leftContent={leftContent}
variant="dark"
>
{children}
</ControlBar>

View File

@@ -1,44 +1,72 @@
'use client';
import { Surface } from '@/ui/Surface';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { Link } from '@/ui/Link';
import { AppShellBar } from './AppShellBar';
import { Activity, Database, Server, Wifi } from 'lucide-react';
export function AppFooter() {
return (
<Surface
as="footer"
variant="precision"
paddingY={6}
paddingX={6}
borderTop
backgroundColor="rgba(10, 10, 11, 0.92)"
className="backdrop-blur-xl"
style={{
boxShadow: '0 -1px 0 0 rgba(255, 255, 255, 0.05)',
}}
>
<Box display="flex" justifyContent="between" alignItems="center" width="full" gap={4}>
<Stack direction="row" align="center" gap={3}>
<Text size="xs" variant="low" font="mono" uppercase letterSpacing="0.12em">
© {new Date().getFullYear()} GridPilot
</Text>
<Box w="px" h="3" bg="var(--ui-color-border-muted)" opacity={0.6} />
<Text size="xs" variant="low" font="mono" uppercase letterSpacing="0.12em">
Session ready
</Text>
</Stack>
const currentYear = new Date().getFullYear();
<Box display={{ base: 'none', sm: 'flex' }}>
<Stack direction="row" align="center" gap={2}>
<Box w="1.5" h="1.5" rounded="full" bg="var(--ui-color-intent-success)" />
<Text size="xs" variant="low" font="mono" uppercase letterSpacing="0.12em">
System Normal
</Text>
</Stack>
return (
<AppShellBar position="bottom">
{/* Left: System Info */}
<Box display="flex" alignItems="center" gap={6}>
<Box display="flex" alignItems="center" gap={2}>
<Box className="w-2 h-2 bg-success-green rounded-full animate-pulse" />
<Text size="xs" className="text-text-med font-mono text-[10px] uppercase tracking-wider">
GRIDPILOT OS v2.0
</Text>
</Box>
<Text size="xs" className="text-text-low font-mono text-[10px] uppercase tracking-wider hidden md:block">
© {currentYear}
</Text>
</Box>
{/* Center: Telemetry Status */}
<Box display="flex" alignItems="center" gap={6} className="hidden md:flex">
<StatusIndicator icon={Database} label="DB" status="good" />
<StatusIndicator icon={Server} label="API" status="good" />
<StatusIndicator icon={Wifi} label="WS" status="good" />
<Box className="h-3 w-px bg-outline-steel" />
<Box display="flex" alignItems="center" gap={2}>
<Activity size={12} className="text-text-low" />
<Text size="xs" className="text-text-med font-mono text-[10px]">12ms</Text>
</Box>
</Box>
</Surface>
{/* Right: Legal & Tools */}
<Box display="flex" alignItems="center" gap={2}>
<FooterLink href="/terms">Terms</FooterLink>
<FooterLink href="/privacy">Privacy</FooterLink>
<FooterLink href="/status">Status</FooterLink>
</Box>
</AppShellBar>
);
}
function StatusIndicator({ icon: Icon, label, status }: { icon: any, label: string, status: 'good' | 'warn' | 'bad' }) {
const color = status === 'good' ? 'text-success-green' : status === 'warn' ? 'text-warning-amber' : 'text-critical-red';
return (
<Box display="flex" alignItems="center" gap={2} title={`${label}: ${status.toUpperCase()}`}>
<Icon size={10} className={color} />
<Text size="xs" className="text-text-low font-mono text-[10px] uppercase tracking-wider">
{label}
</Text>
</Box>
);
}
function FooterLink({ href, children }: { href: string, children: React.ReactNode }) {
return (
<Link
href={href}
variant="ghost"
className="px-2 py-1 rounded hover:bg-white/5 text-text-low hover:text-text-high font-mono text-[10px] uppercase tracking-wider transition-colors"
>
{children}
</Link>
);
}

View File

@@ -1,72 +1,119 @@
'use client';
import { BrandMark } from '@/ui/BrandMark';
import { HeaderActions } from '@/components/layout/HeaderActions';
import { PublicNav } from '@/components/layout/PublicNav';
import { Box } from '@/ui/Box';
import { Text } from '@/ui/Text';
import { Search, Bell, User, ChevronDown, Command } from 'lucide-react';
import { usePathname } from 'next/navigation';
import { useCurrentSession } from '@/hooks/auth/useCurrentSession';
import { routes } from '@/lib/routing/RouteConfig';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Surface } from '@/ui/Surface';
import { usePathname } from 'next/navigation';
import { Button } from '@/ui/Button';
import { useState, useEffect } from 'react';
import { AppShellBar } from './AppShellBar';
export function AppHeader() {
const pathname = usePathname();
const { data: session } = useCurrentSession();
const isAuthenticated = !!session;
const homeHref = isAuthenticated ? routes.protected.dashboard : routes.public.home;
// Simple breadcrumb logic
const pathSegments = pathname.split('/').filter(Boolean);
const breadcrumbs = pathSegments.length > 0
? pathSegments.map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' / ')
: 'Home';
// Clock
const [time, setTime] = useState<string>('');
useEffect(() => {
const updateTime = () => {
const now = new Date();
setTime(now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }));
};
updateTime();
const interval = setInterval(updateTime, 60000);
return () => clearInterval(interval);
}, []);
return (
<Surface
as="header"
variant="dark"
height="14"
zIndex={50}
borderBottom
backgroundColor="rgba(13, 13, 14, 0.8)"
className="backdrop-blur-xl"
>
<Box display="flex" alignItems="center" justifyContent="between" width="full" h="full" px={6}>
{/* Left: Brand & Context */}
<Stack direction="row" align="center" gap={4} h="full">
<BrandMark href={homeHref} priority />
{isAuthenticated && (
<Box
display="flex"
alignItems="center"
gap={3}
paddingLeft={4}
borderLeft
h="6"
borderColor="var(--ui-color-border-muted)"
>
<Text size="xs" weight="medium" variant="low" font="mono" uppercase>
Workspace
</Text>
</Box>
)}
</Stack>
<AppShellBar position="top">
{/* Left: Context & Search */}
<Box display="flex" alignItems="center" gap={6} flex={1}>
<Text size="sm" className="text-text-med font-medium tracking-wide whitespace-nowrap min-w-[100px]">
{breadcrumbs}
</Text>
{/* Center: Navigation (if public) */}
{!isAuthenticated && (
<Box display={{ base: 'none', md: 'flex' }} data-testid="public-top-nav">
<PublicNav pathname={pathname} direction="row" />
{/* Command Search - Refined */}
<Box
className="hidden md:flex items-center gap-3 px-3 h-9 bg-surface-charcoal/50 border border-outline-steel rounded-md w-96 text-text-low hover:border-text-low/50 focus-within:border-primary-accent focus-within:ring-1 focus-within:ring-primary-accent/20 transition-all cursor-text group"
>
<Search size={14} className="text-text-low group-hover:text-text-med transition-colors" />
<Box
as="input"
type="text"
placeholder="Search or type a command..."
className="bg-transparent border-none outline-none text-sm w-full text-text-high placeholder:text-text-low/50 h-full"
/>
<Box className="flex items-center gap-1 px-1.5 py-0.5 rounded bg-white/5 border border-white/5 text-[10px] font-mono text-text-low">
<Command size={10} />
<span>K</span>
</Box>
)}
{/* Right: Session Controls */}
<Box display="flex" alignItems="center" gap={4}>
<Box display={{ base: 'none', sm: 'flex' }} alignItems="center" gap={2}>
<Box w="1.5" h="1.5" rounded="full" bg="var(--ui-color-intent-success)" />
<Text size="xs" variant="low" weight="bold" font="mono" letterSpacing="0.1em">
LIVE
</Text>
</Box>
<HeaderActions isAuthenticated={isAuthenticated} />
</Box>
</Box>
</Surface>
{/* Right: System Status & User */}
<Box display="flex" alignItems="center" gap={4}>
{/* System Time */}
<Box className="hidden lg:flex items-center gap-2 text-text-med border-r border-outline-steel pr-4 h-6">
<Text size="sm" font="mono" className="tracking-widest tabular-nums">
{time} UTC
</Text>
</Box>
{/* Notifications */}
<Box
as="button"
className="w-8 h-8 flex items-center justify-center text-text-low hover:text-text-high hover:bg-white/5 transition-colors rounded-md relative"
title="Notifications"
>
<Bell size={16} />
<Box className="absolute top-2 right-2 w-1.5 h-1.5 bg-primary-accent rounded-full ring-2 ring-base-black" />
</Box>
{/* User Menu */}
{isAuthenticated ? (
<Box display="flex" alignItems="center" gap={3} className="pl-2 cursor-pointer group">
<Box
className="w-8 h-8 rounded-full bg-surface-charcoal flex items-center justify-center text-text-med border border-outline-steel group-hover:border-primary-accent transition-colors"
>
<User size={14} />
</Box>
<Box className="hidden md:block text-left">
<Text size="sm" weight="medium" className="text-text-high leading-none group-hover:text-primary-accent transition-colors">
{session.user.displayName || 'Driver'}
</Text>
</Box>
<ChevronDown size={12} className="text-text-low group-hover:text-text-high transition-colors" />
</Box>
) : (
<Box display="flex" gap={2}>
<Button
as="a"
href={routes.auth.login}
variant="ghost"
size="sm"
>
Sign In
</Button>
<Button
as="a"
href={routes.auth.signup}
variant="primary"
size="sm"
>
Join
</Button>
</Box>
)}
</Box>
</AppShellBar>
);
}

View File

@@ -0,0 +1,34 @@
'use client';
import { Box } from '@/ui/Box';
import { ReactNode } from 'react';
interface AppShellBarProps {
position: 'top' | 'bottom';
children: ReactNode;
sidebarOffset?: boolean;
}
export function AppShellBar({ position, children, sidebarOffset = true }: AppShellBarProps) {
const isTop = position === 'top';
return (
<Box
as={isTop ? 'header' : 'footer'}
className={`
fixed ${isTop ? 'top-0' : 'bottom-0'} right-0 z-40
h-14
bg-base-black/70 backdrop-blur-xl
border-${isTop ? 'b' : 't'} border-outline-steel
flex items-center justify-between px-6
transition-all duration-200
`}
// Use style for responsive left offset to ensure it works
// We use a CSS variable or calc if possible, but here we rely on Tailwind classes via className if we could
// But since we need responsive logic that matches Layout, we'll use the Box props
left={sidebarOffset ? { base: 0, lg: 64 } : 0}
>
{children}
</Box>
);
}

View File

@@ -1,50 +1,54 @@
'use client';
import { AuthedNav } from '@/components/layout/AuthedNav';
import { PublicNav } from '@/components/layout/PublicNav';
import { useCurrentSession } from '@/hooks/auth/useCurrentSession';
import { BrandMark } from '@/ui/BrandMark';
import { NavLink } from '@/ui/NavLink';
import { routes } from '@/lib/routing/RouteConfig';
import { Box } from '@/ui/Box';
import { DashboardRail } from '@/components/dashboard/DashboardRail';
import { Text } from '@/ui/Text';
import { Surface } from '@/ui/Surface';
import { Stack } from '@/ui/Stack';
import {
LayoutGrid,
Trophy,
Users,
Calendar,
Flag,
Home
} from 'lucide-react';
import { usePathname } from 'next/navigation';
export function AppSidebar() {
const pathname = usePathname();
const { data: session } = useCurrentSession();
const isAuthenticated = !!session;
const navItems = [
{ label: 'Home', href: routes.public.home, icon: Home },
{ label: 'Leagues', href: routes.public.leagues, icon: Trophy },
{ label: 'Drivers', href: routes.public.drivers, icon: Users },
{ label: 'Leaderboards', href: routes.public.leaderboards, icon: LayoutGrid },
{ label: 'Teams', href: routes.public.teams, icon: Flag },
{ label: 'Races', href: routes.public.races, icon: Calendar },
];
return (
<Surface
as="aside"
variant="dark"
width="64"
h="full"
borderRight
backgroundColor="linear-gradient(180deg, #0d0d0e 0%, #0a0a0b 100%)"
style={{
boxShadow: 'inset -1px 0 0 0 rgba(255, 255, 255, 0.01)',
}}
>
<DashboardRail>
<Box py={8} fullWidth>
<Box px={6} mb={10}>
<Box display="flex" alignItems="center" gap={2} mb={2}>
<Box w="0.5" h="3" bg="var(--ui-color-intent-primary)" />
<Text size="xs" variant="low" weight="bold" font="mono" letterSpacing="0.2em">
DASHBOARD
</Text>
</Box>
</Box>
<Box px={4}>
{isAuthenticated ? (
<AuthedNav pathname={pathname} />
) : (
<PublicNav pathname={pathname} />
)}
</Box>
</Box>
</DashboardRail>
</Surface>
<Box display="flex" flexDirection="col" height="full" className="bg-base-black border-r border-outline-steel">
{/* Brand Header */}
<Box p={6} pb={8}>
<BrandMark />
</Box>
{/* Navigation */}
<Box flex={1} px={2} className="overflow-y-auto">
<Stack gap={1}>
{navItems.map((item) => (
<NavLink
key={item.href}
href={item.href}
label={item.label}
icon={item.icon}
isActive={pathname === item.href}
variant="sidebar"
/>
))}
</Stack>
</Box>
</Box>
);
}

View File

@@ -1,45 +1,49 @@
import { routes } from '@/lib/routing/RouteConfig';
import { Button } from '@/ui/Button';
import { Stack } from '@/ui/Stack';
import { LogIn, UserPlus } from 'lucide-react';
import { Box } from '@/ui/Box';
interface HeaderActionsProps {
isAuthenticated: boolean;
}
/**
* HeaderActions provides the primary actions in the header (Login, Signup, Profile).
*/
export function HeaderActions({ isAuthenticated }: HeaderActionsProps) {
if (isAuthenticated) {
return (
<Stack direction="row" gap={3}>
<Button as="a" href={routes.protected.profile} variant="secondary" size="sm">
Profile
</Button>
</Stack>
<Button
as="a"
href={routes.protected.profile}
variant="ghost"
size="sm"
className="text-text-med hover:text-text-high transition-colors duration-200"
>
Profile
</Button>
);
}
return (
<Stack direction="row" gap={3}>
<Stack direction="row" gap={4} align="center">
<Button
as="a"
href={routes.auth.login}
variant="ghost"
size="sm"
icon={<LogIn size={16} />}
data-testid="public-nav-login"
className="text-text-med hover:text-text-high transition-colors duration-200"
>
Login
Sign In
</Button>
<Box className="w-px h-4 bg-outline-steel" />
<Button
as="a"
href={routes.auth.signup}
variant="primary"
size="sm"
icon={<UserPlus size={16} />}
data-testid="public-nav-signup"
className="px-6 font-medium tracking-wide"
>
Sign Up
</Button>

View File

@@ -1,6 +1,6 @@
import { routes } from '@/lib/routing/RouteConfig';
import { Stack } from '@/ui/Stack';
import { Calendar, Home, Layout, Trophy, Users } from 'lucide-react';
import { Calendar, Home, LayoutGrid, Trophy, Users, Flag } from 'lucide-react';
import { NavLink } from '@/ui/NavLink';
interface PublicNavProps {
@@ -8,21 +8,18 @@ interface PublicNavProps {
direction?: 'row' | 'col';
}
/**
* PublicNav displays navigation items for unauthenticated users.
*/
export function PublicNav({ pathname, direction = 'col' }: PublicNavProps) {
const items = [
{ label: 'Home', href: routes.public.home, icon: Home },
{ label: 'Leagues', href: routes.public.leagues, icon: Trophy },
{ label: 'Drivers', href: routes.public.drivers, icon: Users },
{ label: 'Leaderboards', href: routes.public.leaderboards, icon: Layout },
{ label: 'Teams', href: routes.public.teams, icon: Users },
{ label: 'Leaderboards', href: routes.public.leaderboards, icon: LayoutGrid },
{ label: 'Teams', href: routes.public.teams, icon: Flag },
{ label: 'Races', href: routes.public.races, icon: Calendar },
];
return (
<Stack direction={direction} gap={direction === 'row' ? 4 : 1}>
<Stack direction={direction} gap={direction === 'row' ? 1 : 1}>
{items.map((item) => (
<NavLink
key={item.href}

View File

@@ -3,11 +3,8 @@
import { AppFooter } from '@/components/layout/AppFooter';
import { AppHeader } from '@/components/layout/AppHeader';
import { AppSidebar } from '@/components/layout/AppSidebar';
import { useCurrentSession } from '@/hooks/auth/useCurrentSession';
import { routes } from '@/lib/routing/RouteConfig';
import { Layout } from '@/ui/Layout';
import { Container } from '@/ui/Container';
import { usePathname } from 'next/navigation';
import { Box } from '@/ui/Box';
import React from 'react';
export interface RootAppShellViewData {
@@ -16,31 +13,26 @@ export interface RootAppShellViewData {
/**
* RootAppShellTemplate orchestrates the top-level semantic shells of the application.
* It uses the canonical ui/Layout component to define the app frame.
* Redesigned for the "Cockpit" layout.
*/
export function RootAppShellTemplate({ children }: RootAppShellViewData) {
const pathname = usePathname();
const { data: session } = useCurrentSession();
const isAuthenticated = !!session;
// Hide sidebar on landing page for unauthenticated users
const isLandingPage = pathname === routes.public.home;
const showSidebar = isAuthenticated && !isLandingPage;
return (
<Layout
header={<AppHeader />}
sidebar={showSidebar ? <AppSidebar /> : undefined}
sidebar={<AppSidebar />}
footer={<AppFooter />}
fixedHeader
fixedSidebar
fixedHeader={true}
fixedSidebar={true}
fixedFooter={false}
>
<Container
size={isLandingPage ? 'full' : 'xl'}
py={isLandingPage ? 0 : 8}
<Box
width="full"
className="max-w-[1920px] mx-auto"
px={8}
py={8}
>
{children}
</Container>
</Box>
</Layout>
);
}

View File

@@ -0,0 +1,2 @@
components/admin/AdminToolbar.tsx(24,7): error TS2322: Type '{ children: ReactNode; leftContent: ReactNode; variant: string; }' is not assignable to type 'IntrinsicAttributes & ControlBarProps'.
Property 'variant' does not exist on type 'IntrinsicAttributes & ControlBarProps'.

View File

@@ -7,29 +7,20 @@ interface BrandMarkProps {
priority?: boolean;
}
/**
* BrandMark provides the consistent logo/wordmark for the application.
* Aligned with "Precision Racing Minimal" theme.
*/
export function BrandMark({ href = '/' }: BrandMarkProps) {
return (
<Link href={href} variant="inherit" underline="none">
<Box position="relative" display="inline-flex" alignItems="center">
<Box height={{ base: '1.5rem', md: '1.75rem' }} style={{ transition: 'opacity 0.2s' }}>
<Image
src="/images/logos/wordmark-rectangle-dark.svg"
alt="GridPilot"
style={{ height: '100%', width: 'auto', display: 'block' }}
/>
</Box>
<Box
position="absolute"
bottom="-4px"
left="0"
width="0"
height="2px"
bg="var(--ui-color-intent-primary)"
style={{ transition: 'width 0.2s' }}
<Box display="flex" alignItems="center" gap={2}>
<Image
src="/images/logos/square-logo-dark.svg"
alt=""
style={{ height: '1.5rem', width: 'auto' }}
/>
<Image
src="/images/logos/wordmark-rectangle-dark.svg"
alt="GridPilot"
style={{ height: '1.125rem', width: 'auto' }}
/>
</Box>
</Link>

View File

@@ -6,78 +6,71 @@ export interface LayoutProps {
header?: ReactNode;
footer?: ReactNode;
sidebar?: ReactNode;
/**
* Whether the sidebar should be fixed to the side.
* If true, the main content will be offset by the sidebar width.
*/
fixedSidebar?: boolean;
/**
* Whether the header should be fixed to the top.
* If true, the main content will be offset by the header height.
*/
fixedHeader?: boolean;
fixedFooter?: boolean;
}
/**
* Layout is the canonical app frame component.
* It orchestrates the high-level structure: Header, Sidebar, Main Content, and Footer.
* Redesigned for "Cockpit" layout: Sidebar is primary (full height), Header and Content sit to the right.
*/
export const Layout = ({
children,
header,
footer,
sidebar,
fixedSidebar = false,
fixedHeader = false
fixedSidebar = true,
fixedHeader = true,
fixedFooter = true // Default to true for AppShellBar
}: LayoutProps) => {
return (
<Box display="flex" flexDirection="col" minHeight="100vh">
{header && (
<Box display="flex" minHeight="100vh" className="bg-base-black text-text-high">
{/* Sidebar - Primary Vertical Axis - Solid Background */}
{sidebar && (
<Box
as="header"
position={fixedHeader ? "fixed" : "relative"}
top={fixedHeader ? 0 : undefined}
left={fixedHeader ? 0 : undefined}
right={fixedHeader ? 0 : undefined}
as="aside"
width="64" // 16rem / 256px
display={{ base: 'none', lg: 'block' }}
position={fixedSidebar ? "fixed" : "relative"}
top={0}
bottom={0}
left={0}
zIndex={50}
className="bg-base-black border-r border-outline-steel"
>
{header}
{sidebar}
</Box>
)}
<Box display="flex" flex={1} marginTop={fixedHeader ? 14 : undefined}>
{sidebar && (
<Box
as="aside"
width="64"
display={{ base: 'none', lg: 'block' }}
position={fixedSidebar ? "fixed" : "relative"}
top={fixedSidebar ? (fixedHeader ? 14 : 0) : undefined}
bottom={fixedSidebar ? 0 : undefined}
left={fixedSidebar ? 0 : undefined}
zIndex={40}
>
{sidebar}
</Box>
)}
{/* Main Content Area - Right of Sidebar */}
<Box
display="flex"
flexDirection="col"
flex={1}
marginLeft={fixedSidebar && sidebar ? { lg: 64 } : undefined}
minWidth="0" // Prevent flex child overflow
>
{/* Header - Rendered directly as it contains AppShellBar (fixed) */}
{header}
{/* Main Scrollable Content */}
<Box
as="main"
flex={1}
display="flex"
flexDirection="col"
marginLeft={fixedSidebar ? { lg: 64 } : undefined}
position="relative"
marginTop={fixedHeader ? 14 : 0} // Offset for fixed header (h-14)
paddingBottom={fixedFooter ? 14 : 0} // Offset for fixed footer (h-14)
>
<Box flex={1}>
<Box flex={1} p={0}>
{children}
</Box>
{footer && (
<Box as="footer">
{footer}
</Box>
)}
</Box>
{/* Footer - Rendered directly as it contains AppShellBar (fixed) */}
{footer}
</Box>
</Box>
);

View File

@@ -12,25 +12,56 @@ interface NavLinkProps {
variant?: 'sidebar' | 'top';
}
/**
* NavLink provides a consistent link component for navigation.
* Supports both sidebar and top navigation variants.
*/
export function NavLink({ href, label, icon, isActive, variant = 'sidebar' }: NavLinkProps) {
const isTop = variant === 'top';
// Dieter Rams style: Unobtrusive, Honest, Thorough.
// No glows. No shadows. Just clear contrast and alignment.
const content = (
<Box display="flex" alignItems="center" gap={variant === 'top' ? 2 : 3} paddingX={3} paddingY={2} rounded={variant === 'sidebar' ? 'md' : undefined} style={{ transition: 'all 0.2s' }}>
{icon && <Icon icon={icon} size={variant === 'top' ? 4 : 5} intent={isActive ? 'primary' : 'low'} />}
<Text size="sm" weight={isActive ? 'bold' : 'medium'} variant={isActive ? 'primary' : 'med'}>
<Box
display="flex"
alignItems="center"
gap={3}
paddingX={isTop ? 4 : 4}
paddingY={isTop ? 2 : 3}
className={`
relative group transition-all duration-200 ease-out
${isActive
? 'text-text-high bg-white/5'
: 'text-text-med hover:text-text-high hover:bg-white/5'
}
${!isTop && 'rounded-md mx-2'}
`}
>
{icon && (
<Icon
icon={icon}
size={4}
className={`transition-colors duration-200 ${isActive ? 'text-primary-accent' : 'text-text-low group-hover:text-text-med'}`}
/>
)}
<Text
size="sm"
weight={isActive ? 'medium' : 'normal'}
variant="inherit"
className="tracking-wide"
>
{label}
</Text>
{variant === 'sidebar' && isActive && (
<Box marginLeft="auto" width="4px" height="1rem" bg="var(--ui-color-intent-primary)" rounded="full" />
{/* Minimal Active Indicator */}
{!isTop && isActive && (
<Box
className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-primary-accent rounded-r-full"
/>
)}
</Box>
);
return (
<Link href={href} variant="inherit" underline="none" block={variant === 'sidebar'}>
<Link href={href} variant="inherit" underline="none" block={!isTop}>
{content}
</Link>
);