Files
Portfolio-Game/frontend/app/components/layout/AppHeader.vue
skycel 9fd66def12 🎲 Add Pinia progression store & GDPR consent banner (Story 1.6)
Implements useProgressionStore with conditional localStorage persistence
(only after RGPD consent), immersive ConsentBanner with narrator style,
WelcomeBack component for returning visitors, and connects progress bar
in header to store.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 21:00:49 +01:00

114 lines
3.3 KiB
Vue

<template>
<header
class="sticky top-0 z-50 transition-colors duration-300"
:class="scrolled ? 'bg-sky-dark/90 backdrop-blur-sm shadow-lg' : 'bg-transparent'"
>
<div class="max-w-7xl mx-auto px-4 py-3 flex items-center justify-between">
<NuxtLink :to="localePath('/')" class="text-xl font-narrative text-sky-accent font-bold">
Skycel
</NuxtLink>
<!-- Desktop nav -->
<nav class="hidden md:flex items-center gap-6" aria-label="Main navigation">
<NuxtLink
v-for="item in navItems"
:key="item.path"
:to="localePath(item.path)"
class="text-sm font-ui text-sky-text/70 hover:text-sky-accent transition-colors"
active-class="!text-sky-accent"
>
{{ $t(item.label) }}
</NuxtLink>
</nav>
<div class="flex items-center gap-3">
<!-- Barre de progression -->
<div class="hidden md:block w-24 h-1.5 bg-sky-text/10 rounded-full overflow-hidden">
<div
class="h-full bg-sky-accent/40 rounded-full transition-all duration-500"
:style="{ width: progressPercent + '%' }"
/>
</div>
<UiLanguageSwitcher />
<!-- Mobile hamburger -->
<button
class="md:hidden text-sky-text p-1"
aria-label="Menu"
@click="mobileOpen = !mobileOpen"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
v-if="!mobileOpen"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"
/>
<path
v-else
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
</div>
<!-- Mobile menu -->
<Transition name="slide-down">
<nav
v-if="mobileOpen"
class="md:hidden bg-sky-dark/95 backdrop-blur-sm border-t border-sky-text/10 px-4 py-4"
aria-label="Mobile navigation"
>
<NuxtLink
v-for="item in navItems"
:key="item.path"
:to="localePath(item.path)"
class="block py-2 text-sm font-ui text-sky-text/70 hover:text-sky-accent transition-colors"
active-class="!text-sky-accent"
@click="mobileOpen = false"
>
{{ $t(item.label) }}
</NuxtLink>
</nav>
</Transition>
</header>
</template>
<script setup lang="ts">
import { useProgressionStore } from '~/stores/progression'
const localePath = useLocalePath()
const store = useProgressionStore()
const progressPercent = computed(() => store.progressPercent)
const mobileOpen = ref(false)
const scrolled = ref(false)
const navItems = [
{ path: '/projets', label: 'nav.projects' },
{ path: '/competences', label: 'nav.skills' },
{ path: '/parcours', label: 'nav.journey' },
{ path: '/temoignages', label: 'nav.testimonials' },
{ path: '/contact', label: 'nav.contact' },
]
onMounted(() => {
window.addEventListener('scroll', handleScroll, { passive: true })
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
function handleScroll() {
scrolled.value = window.scrollY > 20
}
</script>