product detail pages, footer, category and legal routes

- /shop/:slug product detail pages with green hero, per-product locale files (de.js + en.js), and viewport-fit hero sizing
- footer component integrated across home/shop/checkout/success
- /pflege, /haushalt, /impressum, /datenschutz routes via CategoryPage / LegalPage
- scrollBehavior honours savedPosition (back-button restores y); smooth scrolling removed everywhere for accessibility
- ProductCard + Hero CTAs use RouterLink for internal paths so SPA history survives clicks
- homepage hero image cap reduced ~20% on width and height
- dist/ rebuild

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-05-03 14:33:22 +01:00
parent 16910c49d4
commit e5bd1f65fc
163 changed files with 855 additions and 76 deletions

View File

@@ -0,0 +1,114 @@
<script setup>
import { computed } from 'vue'
import { useI18n } from '@/i18n/index.js'
import Logo from './Logo.vue'
const props = defineProps({
variant: {
type: String,
default: 'cream',
validator: (v) => ['cream', 'brand', 'paper'].includes(v),
},
})
const { t } = useI18n()
const tone = computed(() => {
if (props.variant === 'brand') {
return {
surface: 'bg-brand text-cream',
topRule: 'border-cream-line',
bottomRule: 'border-cream-line/40',
muted: 'text-cream/80',
logo: 'text-cream',
link: 'hover:text-accent',
}
}
if (props.variant === 'paper') {
return {
surface: 'bg-paper text-brand',
topRule: 'border-line',
bottomRule: 'border-line/60',
muted: 'text-muted',
logo: 'text-brand',
link: 'hover:text-brand-hover',
}
}
return {
surface: 'bg-cream text-brand',
topRule: 'border-line',
bottomRule: 'border-line/60',
muted: 'text-muted',
logo: 'text-brand',
link: 'hover:text-brand-hover',
}
})
const legalLinks = [
{ key: 'footer.legal.impressum', href: '/impressum' },
{ key: 'footer.legal.datenschutz', href: '/datenschutz' },
]
const exploreLinks = [
{ key: 'footer.explore.shop', href: '/shop' },
{ key: 'footer.explore.bundles', href: '/#bundles' },
{ key: 'footer.explore.about', href: '/#about' },
]
const year = new Date().getFullYear()
</script>
<template>
<footer :class="['border-t', tone.surface, tone.topRule]">
<div
class="mx-auto w-full max-w-6xl px-6 py-12 sm:px-8 sm:py-14 md:px-12 md:py-16 lg:px-16"
>
<div class="grid gap-10 md:grid-cols-[1.4fr_1fr_1fr] md:gap-12">
<div class="flex flex-col gap-4 max-w-sm">
<Logo :class="tone.logo" />
<p :class="['text-sm leading-relaxed', tone.muted]">
{{ t('footer.tagline') }}
</p>
</div>
<nav :aria-label="t('footer.explore.heading')" class="flex flex-col gap-3">
<p :class="['eyebrow', tone.muted]">{{ t('footer.explore.heading') }}</p>
<ul class="flex flex-col gap-2">
<li v-for="link in exploreLinks" :key="link.href">
<a
:href="link.href"
:class="['text-sm underline-offset-4 decoration-1 hover:underline', tone.link]"
>{{ t(link.key) }}</a>
</li>
</ul>
</nav>
<nav :aria-label="t('footer.legal.heading')" class="flex flex-col gap-3">
<p :class="['eyebrow', tone.muted]">{{ t('footer.legal.heading') }}</p>
<ul class="flex flex-col gap-2">
<li v-for="link in legalLinks" :key="link.href">
<a
:href="link.href"
:class="['text-sm underline-offset-4 decoration-1 hover:underline', tone.link]"
>{{ t(link.key) }}</a>
</li>
</ul>
</nav>
</div>
<div
:class="[
'mt-12 pt-6 border-t flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3',
tone.bottomRule,
]"
>
<p :class="['text-xs', tone.muted]">
© {{ year }} {{ t('footer.copyright') }}
</p>
<p :class="['text-xs', tone.muted]">
{{ t('footer.madeIn') }}
</p>
</div>
</div>
</footer>
</template>