feat: init
This commit is contained in:
106
app/components/Navbar.vue
Normal file
106
app/components/Navbar.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<script setup lang="ts">
|
||||
const scrolled = ref(false)
|
||||
|
||||
function onScroll() {
|
||||
scrolled.value = window.scrollY > 20
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', onScroll, { passive: true })
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
})
|
||||
|
||||
const links = [
|
||||
{ label: 'Features', href: '#features' },
|
||||
{ label: 'Rates', href: '#rates' },
|
||||
{ label: 'API Docs', href: '#playground' },
|
||||
]
|
||||
|
||||
const mobileOpen = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav
|
||||
class="fixed top-0 left-0 right-0 z-50 transition-all duration-300"
|
||||
:class="scrolled
|
||||
? 'bg-white/80 backdrop-blur-xl shadow-soft border-b border-surface-border'
|
||||
: 'bg-transparent'"
|
||||
>
|
||||
<div class="section-container flex items-center justify-between h-16">
|
||||
<!-- Logo -->
|
||||
<a href="#" class="flex items-center gap-2 group">
|
||||
<span class="inline-flex items-center justify-center w-8 h-8 rounded-lg bg-eu-blue text-white font-heading font-bold text-sm transition-transform group-hover:scale-105">
|
||||
V
|
||||
</span>
|
||||
<span class="font-heading font-bold text-lg text-ink">
|
||||
vat-api<span class="text-eu-blue">.eu</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<!-- Desktop links -->
|
||||
<div class="hidden md:flex items-center gap-8">
|
||||
<a
|
||||
v-for="link in links"
|
||||
:key="link.href"
|
||||
:href="link.href"
|
||||
class="text-sm font-medium text-ink-secondary hover:text-eu-blue transition-colors"
|
||||
>
|
||||
{{ link.label }}
|
||||
</a>
|
||||
<a
|
||||
href="#playground"
|
||||
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-eu-blue text-white text-sm font-semibold hover:bg-eu-blue-dark transition-colors"
|
||||
>
|
||||
Try API
|
||||
<svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Mobile toggle -->
|
||||
<button
|
||||
class="md:hidden p-2 -mr-2 text-ink-secondary hover:text-ink"
|
||||
@click="mobileOpen = !mobileOpen"
|
||||
:aria-label="mobileOpen ? 'Close menu' : 'Open menu'"
|
||||
>
|
||||
<svg v-if="!mobileOpen" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
<svg v-else class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" d="M6 18 18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Mobile menu -->
|
||||
<Transition
|
||||
enter-active-class="transition duration-200 ease-out"
|
||||
enter-from-class="opacity-0 -translate-y-2"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
leave-active-class="transition duration-150 ease-in"
|
||||
leave-from-class="opacity-100 translate-y-0"
|
||||
leave-to-class="opacity-0 -translate-y-2"
|
||||
>
|
||||
<div
|
||||
v-if="mobileOpen"
|
||||
class="md:hidden bg-white/95 backdrop-blur-xl border-b border-surface-border"
|
||||
>
|
||||
<div class="section-container py-4 flex flex-col gap-3">
|
||||
<a
|
||||
v-for="link in links"
|
||||
:key="link.href"
|
||||
:href="link.href"
|
||||
class="text-sm font-medium text-ink-secondary hover:text-eu-blue py-2 transition-colors"
|
||||
@click="mobileOpen = false"
|
||||
>
|
||||
{{ link.label }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</nav>
|
||||
</template>
|
||||
Reference in New Issue
Block a user