- Implemented a button for toggling between dark and light modes in the navigation component. - Adjusted sidebar width and improved active link indicators with shadows for better visibility. - Updated layout colors for a more vibrant and cohesive theme, focusing on a "Pure Neutral Black + Neon" aesthetic. - Refined ambient gradient accents for improved content readability across devices. - Enhanced console layout for better responsiveness and user experience.
241 lines
13 KiB
PHP
241 lines
13 KiB
PHP
<script data-navigate-track>
|
|
// Apply lazy loading to images - runs on initial load and Livewire navigation
|
|
(function() {
|
|
const applyLazyLoading = () => {
|
|
document.querySelectorAll('img:not([loading])')
|
|
.forEach(img => img.loading = 'lazy');
|
|
};
|
|
|
|
// Run immediately if DOM is ready, otherwise wait
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', applyLazyLoading);
|
|
} else {
|
|
applyLazyLoading();
|
|
}
|
|
|
|
// Also run on Livewire navigation
|
|
document.addEventListener('livewire:navigated', applyLazyLoading);
|
|
})();
|
|
</script>
|
|
|
|
<style>
|
|
/* ── Brand text animated gradient ── */
|
|
.text-flow-effect {
|
|
background: linear-gradient(
|
|
90deg,
|
|
hsl(var(--color-primary)) 0%,
|
|
hsl(var(--color-secondary)) 40%,
|
|
hsl(var(--color-primary)) 60%,
|
|
hsl(var(--color-secondary)) 100%
|
|
);
|
|
background-size: 300% auto;
|
|
background-clip: text;
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
animation: textFlowAnimation 6s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes textFlowAnimation {
|
|
0% { background-position: 0% center; }
|
|
50% { background-position: 300% center; }
|
|
100% { background-position: 0% center; }
|
|
}
|
|
|
|
/* ── Nav link animated underline ── */
|
|
.nav-link-modern {
|
|
position: relative;
|
|
}
|
|
.nav-link-modern::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 50%;
|
|
width: 0;
|
|
height: 2px;
|
|
border-radius: 1px;
|
|
background: hsl(var(--color-primary));
|
|
transition: width 0.25s ease, left 0.25s ease;
|
|
}
|
|
.nav-link-modern:hover::after {
|
|
width: 60%;
|
|
left: 20%;
|
|
}
|
|
|
|
/* ── Avatar ring using theme colors ── */
|
|
.avatar-ring {
|
|
padding: 2px;
|
|
background: linear-gradient(135deg, hsl(var(--color-primary)), hsl(var(--color-secondary)));
|
|
border-radius: 9999px;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
.avatar-ring:hover {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 0 10px hsl(var(--color-primary) / 0.3);
|
|
}
|
|
.avatar-ring img {
|
|
display: block;
|
|
border-radius: 9999px;
|
|
border: 2px solid hsl(var(--color-background-secondary));
|
|
}
|
|
</style>
|
|
|
|
<nav x-data="{ scrolled: false, slideOverOpen: false }"
|
|
x-init="
|
|
window.addEventListener('scroll', () => { scrolled = window.scrollY > 20 });
|
|
$watch('slideOverOpen', value => { document.documentElement.style.overflow = value ? 'hidden' : '' });
|
|
"
|
|
:class="{
|
|
'shadow-lg shadow-black/5 border-neutral/80': scrolled,
|
|
'border-neutral': !scrolled
|
|
}"
|
|
class="w-full px-4 lg:px-8 bg-background-secondary border-b flex flex-col lg:flex-row justify-between fixed top-0 z-20 h-auto transition-all duration-300">
|
|
<div class="relative z-50 w-full h-auto">
|
|
|
|
<div class="flex flex-row items-center justify-between w-full h-16">
|
|
|
|
<!-- Logo -->
|
|
<div class="flex flex-row items-center">
|
|
<a href="{{ route('home') }}" class="flex items-center h-10 gap-2.5 group" wire:navigate>
|
|
<x-logo class="h-10 transition-transform duration-200 group-hover:scale-[1.03]" />
|
|
<span class="text-xl font-extrabold tracking-tight leading-none flex items-center text-flow-effect">{{ config('app.name') }}</span>
|
|
</a>
|
|
|
|
<!-- Desktop menu: lg and above -->
|
|
<div class="hidden lg:flex flex-row items-center ml-8 gap-0.5">
|
|
@foreach (\App\Classes\Navigation::getLinks() as $nav)
|
|
@if (isset($nav['children']) && count($nav['children']) > 0)
|
|
<div class="relative">
|
|
<x-dropdown>
|
|
<x-slot:trigger>
|
|
<span class="nav-link-modern flex flex-row items-center px-3 py-2 text-sm font-semibold whitespace-nowrap text-base hover:text-primary transition-colors duration-200">{{ $nav['name'] }}</span>
|
|
</x-slot:trigger>
|
|
<x-slot:content>
|
|
@foreach ($nav['children'] as $child)
|
|
<x-navigation.link :href="route($child['route'], $child['params'] ?? null)"
|
|
:spa="isset($child['spa']) ? $nav['spa'] : true">
|
|
{{ $child['name'] }}
|
|
</x-navigation.link>
|
|
@endforeach
|
|
</x-slot:content>
|
|
</x-dropdown>
|
|
</div>
|
|
@else
|
|
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? null)"
|
|
:spa="isset($nav['spa']) ? $nav['spa'] : true"
|
|
class="nav-link-modern flex items-center px-3 py-2">
|
|
{{ $nav['name'] }}
|
|
</x-navigation.link>
|
|
@endif
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right-side controls -->
|
|
<div class="flex items-center gap-3">
|
|
|
|
<livewire:components.cart />
|
|
|
|
<!-- Dark/Light mode toggle (left of user icon / login & register) -->
|
|
<button @click="darkMode = !darkMode" type="button"
|
|
class="w-10 h-10 flex items-center justify-center rounded-lg hover:bg-neutral/50 transition-colors duration-200"
|
|
:aria-label="darkMode ? 'Switch to light mode' : 'Switch to dark mode'">
|
|
<svg x-show="!darkMode" xmlns="http://www.w3.org/2000/svg" class="size-4 text-base" viewBox="0 0 24 24" fill="currentColor"><path d="M12 18C8.68629 18 6 15.3137 6 12C6 8.68629 8.68629 6 12 6C15.3137 6 18 8.68629 18 12C18 15.3137 15.3137 18 12 18ZM11 1H13V4H11V1ZM11 20H13V23H11V20ZM3.51472 4.92893L4.92893 3.51472L7.05025 5.63604L5.63604 7.05025L3.51472 4.92893ZM16.9497 18.364L18.364 16.9497L20.4853 19.0711L19.0711 20.4853L16.9497 18.364ZM19.0711 3.51472L20.4853 4.92893L18.364 7.05025L16.9497 5.63604L19.0711 3.51472ZM5.63604 16.9497L7.05025 18.364L4.92893 20.4853L3.51472 19.0711L5.63604 16.9497ZM23 11V13H20V11H23ZM4 11V13H1V11H4Z"/></svg>
|
|
<svg x-show="darkMode" x-cloak xmlns="http://www.w3.org/2000/svg" class="size-4 text-base" viewBox="0 0 24 24" fill="currentColor"><path d="M10 7C10 10.866 13.134 14 17 14C18.9584 14 20.729 13.1957 21.9995 11.8995C22 11.933 22 11.9665 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C12.0335 2 12.067 2 12.1005 2.00049C10.8043 3.27098 10 5.04157 10 7Z"/></svg>
|
|
</button>
|
|
|
|
<!-- User account dropdown (desktop) -->
|
|
@if(auth()->check())
|
|
<div class="hidden lg:block">
|
|
<x-dropdown>
|
|
<x-slot:trigger>
|
|
<div class="flex items-center gap-2.5 cursor-pointer group">
|
|
<div class="avatar-ring">
|
|
<img src="{{ auth()->user()->avatar }}" class="w-8 h-8" alt="avatar" />
|
|
</div>
|
|
<span class="text-sm font-semibold text-base group-hover:text-primary transition-colors duration-200">{{ Auth::user()->first_name }}</span>
|
|
</div>
|
|
</x-slot:trigger>
|
|
<x-slot:content>
|
|
@foreach (\App\Classes\Navigation::getAccountDropdownLinks() as $nav)
|
|
<x-navigation.link :href="route($nav['route'], $nav['params'] ?? null)" :spa="isset($nav['spa']) ? $nav['spa'] : true">
|
|
{{ $nav['name'] }}
|
|
</x-navigation.link>
|
|
@endforeach
|
|
<div class="border-t border-neutral/50 mt-1 pt-1">
|
|
<livewire:auth.logout />
|
|
</div>
|
|
</x-slot:content>
|
|
</x-dropdown>
|
|
</div>
|
|
@else
|
|
<div class="hidden lg:flex items-center gap-2.5">
|
|
<a href="{{ route('login') }}" wire:navigate>
|
|
<button class="px-4 py-2 text-sm font-medium text-base hover:text-primary rounded-lg transition-all duration-200">
|
|
{{ __('navigation.login') }}
|
|
</button>
|
|
</a>
|
|
@if(!config('settings.registration_disabled', false))
|
|
<a href="{{ route('register') }}" wire:navigate>
|
|
<button class="px-5 py-2 text-sm font-semibold text-white bg-gradient-to-r from-primary to-secondary rounded-lg transition-all duration-200 shadow-md shadow-primary/15 hover:shadow-lg hover:shadow-primary/25 hover:brightness-110 active:scale-[0.98]">
|
|
{{ __('navigation.register') }}
|
|
</button>
|
|
</a>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Mobile menu toggle -->
|
|
<button @click="slideOverOpen = !slideOverOpen"
|
|
class="relative w-10 h-10 flex lg:hidden items-center justify-center rounded-xl bg-background hover:bg-neutral/30 transition-all duration-200 group"
|
|
aria-label="Toggle Menu">
|
|
<span x-show="!slideOverOpen" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 rotate-90 scale-75" x-transition:enter-end="opacity-100 rotate-0 scale-100" class="absolute inset-0 flex items-center justify-center">
|
|
<x-ri-menu-fill class="size-5 text-base/80 group-hover:text-foreground transition-colors duration-200" />
|
|
</span>
|
|
<span x-show="slideOverOpen" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 -rotate-90 scale-75" x-transition:enter-end="opacity-100 rotate-0 scale-100" class="absolute inset-0 flex items-center justify-center">
|
|
<x-ri-close-fill class="size-5 text-base/80 group-hover:text-foreground transition-colors duration-200" />
|
|
</span>
|
|
</button>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mobile slide-over menu (<1024px) -->
|
|
<template x-teleport="body">
|
|
<div x-show="slideOverOpen" @keydown.window.escape="slideOverOpen=false" x-cloak class="fixed inset-0 z-[99]" aria-modal="true">
|
|
{{-- Backdrop with blur and fade --}}
|
|
<div x-show="slideOverOpen"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0"
|
|
x-transition:enter-end="opacity-100"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100"
|
|
x-transition:leave-end="opacity-0"
|
|
@click="slideOverOpen = false"
|
|
class="absolute inset-0 bg-black/40 backdrop-blur-sm"></div>
|
|
|
|
{{-- Slide-over panel --}}
|
|
<div x-show="slideOverOpen"
|
|
x-transition:enter="transition ease-out duration-300"
|
|
x-transition:enter-start="opacity-0 translate-y-full"
|
|
x-transition:enter-end="opacity-100 translate-y-0"
|
|
x-transition:leave="transition ease-in duration-200"
|
|
x-transition:leave-start="opacity-100 translate-y-0"
|
|
x-transition:leave-end="opacity-0 translate-y-full"
|
|
@click.stop
|
|
class="absolute left-0 right-0 top-16 bg-background-secondary/95 backdrop-blur-xl border-t border-neutral/50 shadow-2xl overflow-y-auto flex flex-col rounded-t-2xl"
|
|
style="height:calc(100dvh - 4rem);">
|
|
<div class="flex flex-col h-full p-6">
|
|
{{-- Swipe handle indicator --}}
|
|
<div class="flex justify-center mb-4">
|
|
<div class="w-10 h-1 bg-neutral/40 rounded-full"></div>
|
|
</div>
|
|
<div class="flex-1 min-h-0 overflow-y-auto">
|
|
<x-navigation.sidebar-links />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
</div>
|
|
</nav> |