Files
demo-directus-storefront/src/layouts/Base.astro
T

182 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
import '@fontsource-variable/fraunces';
import '@fontsource-variable/public-sans';
const { title = 'Hofladen Heidekorn', desc = 'Regionale Spezialitäten aus der Lüneburger Heide — Demoshop.' } = Astro.props;
---
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title} · Hofladen Heidekorn</title>
<meta name="description" content={desc} />
<meta name="robots" content="noindex" />
<style is:global>
:root{
--petrol: oklch(0.50 0.075 205);
--petrol-deep: oklch(0.38 0.07 212);
--light: oklch(0.975 0.008 200);
--soft: oklch(0.93 0.018 195);
--slate: oklch(0.55 0.02 230);
--graphit: oklch(0.33 0.012 240);
--ink: oklch(0.21 0.012 250);
--weiss: oklch(0.995 0 0);
--line: oklch(0.88 0.012 215);
--radius: 14px;
--wrap: 1140px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:var(--light);color:var(--ink);
font-family:'Public Sans Variable',system-ui,sans-serif;line-height:1.6;-webkit-font-smoothing:antialiased}
h1,h2,h3{font-family:'Fraunces Variable',Georgia,serif;font-weight:560;line-height:1.12;letter-spacing:-0.01em;margin:0 0 .4em}
a{color:inherit;text-decoration:none}
img{display:block;max-width:100%}
.wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:22px}
.u{position:relative;display:inline-block}
.u::after{content:"_";color:var(--petrol);font-weight:700}
.btn{display:inline-flex;align-items:center;gap:.5em;background:var(--petrol);color:var(--weiss);
border:none;border-radius:999px;padding:.7em 1.3em;font:inherit;font-weight:600;cursor:pointer;transition:background .18s,transform .18s}
.btn:hover{background:var(--petrol-deep);transform:translateY(-1px)}
.btn.ghost{background:transparent;color:var(--petrol);border:1.5px solid var(--line)}
.btn.ghost:hover{border-color:var(--petrol);background:transparent}
/* header */
header.site{position:sticky;top:0;z-index:40;background:color-mix(in oklch,var(--light) 86%, transparent);
backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
.nav{display:flex;align-items:center;justify-content:space-between;height:68px}
.brand{font-family:'Fraunces Variable',serif;font-size:1.3rem;font-weight:600}
.brand small{display:block;font-family:'Public Sans Variable',sans-serif;font-size:.62rem;letter-spacing:.18em;
text-transform:uppercase;color:var(--slate);font-weight:600}
.nav-links{display:flex;gap:1.6rem;align-items:center;font-weight:500}
.nav-links a:hover{color:var(--petrol)}
.cart-btn{position:relative;background:none;border:none;cursor:pointer;color:var(--ink);font:inherit;display:flex;align-items:center;gap:.4rem;font-weight:600}
.cart-badge{position:absolute;top:-8px;right:-10px;background:var(--petrol);color:#fff;border-radius:999px;
min-width:18px;height:18px;font-size:.7rem;display:grid;place-items:center;padding:0 4px}
/* drawer */
.drawer-bg{position:fixed;inset:0;background:oklch(0.2 0.02 250/.45);opacity:0;pointer-events:none;transition:opacity .25s;z-index:50}
.drawer-bg.open{opacity:1;pointer-events:auto}
.drawer{position:fixed;top:0;right:0;height:100%;width:min(420px,92vw);background:var(--weiss);z-index:60;
transform:translateX(100%);transition:transform .28s ease;display:flex;flex-direction:column;box-shadow:-12px 0 40px oklch(0.2 0.02 250/.18)}
.drawer.open{transform:none}
.drawer header{display:flex;justify-content:space-between;align-items:center;padding:20px;border-bottom:1px solid var(--line)}
.drawer h3{margin:0;font-size:1.15rem}
.drawer .items{flex:1;overflow:auto;padding:8px 20px}
.ci{display:flex;gap:12px;padding:14px 0;border-bottom:1px solid var(--line)}
.ci img{width:62px;height:62px;object-fit:cover;border-radius:10px;flex:none}
.ci .meta{flex:1;min-width:0}
.ci .nm{font-weight:600;font-size:.92rem}
.ci .qty{display:flex;align-items:center;gap:8px;margin-top:6px}
.ci .qty button{width:26px;height:26px;border:1px solid var(--line);background:var(--light);border-radius:7px;cursor:pointer;font-size:1rem;line-height:1}
.ci .rm{background:none;border:none;color:var(--slate);cursor:pointer;font-size:.78rem;text-decoration:underline}
.drawer footer{padding:18px 20px;border-top:1px solid var(--line)}
.sumline{display:flex;justify-content:space-between;margin-bottom:4px;color:var(--graphit);font-size:.9rem}
.sumline.total{color:var(--ink);font-weight:700;font-size:1.1rem;margin-top:8px}
.empty{color:var(--slate);text-align:center;padding:40px 0}
.close{background:none;border:none;font-size:1.5rem;cursor:pointer;color:var(--slate);line-height:1}
/* footer */
footer.site{margin-top:80px;background:var(--graphit);color:oklch(0.9 0.01 220);padding:54px 0 30px}
footer.site a{color:oklch(0.9 0.01 220)}
footer.site a:hover{color:#fff}
.fgrid{display:grid;grid-template-columns:2fr 1fr 1fr;gap:32px}
footer h4{font-family:'Public Sans Variable',sans-serif;text-transform:uppercase;letter-spacing:.12em;font-size:.72rem;color:oklch(0.72 0.02 220);margin:0 0 12px}
.fcol a{display:block;padding:3px 0;font-size:.92rem}
.fnote{border-top:1px solid oklch(0.45 0.01 240);margin-top:34px;padding-top:18px;font-size:.8rem;color:oklch(0.72 0.02 220);display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px}
.demo-pill{display:inline-block;background:var(--petrol);color:#fff;font-size:.7rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:3px 10px;border-radius:999px}
@media(max-width:760px){.fgrid{grid-template-columns:1fr}.nav-links a:not(.cart-link){display:none}}
.legal{max-width:740px;padding-top:30px;padding-bottom:20px}
.legal h1{font-size:clamp(1.8rem,3.5vw,2.6rem);margin:.3em 0 .6em}
.legal h3{margin:1.4em 0 .3em;font-size:1.15rem}
.legal p{color:var(--graphit);margin:0 0 .9em}
.demo-note{display:inline-block;background:oklch(0.92 0.06 80);color:oklch(0.4 0.08 60);font-size:.8rem;font-weight:600;padding:6px 14px;border-radius:8px;margin-bottom:8px}
.legal .src{margin-top:24px;padding-top:16px;border-top:1px solid var(--line);font-size:.82rem;color:var(--slate)}
</style>
</head>
<body>
<header class="site"><div class="wrap nav">
<a href="/" class="brand">Hofladen Heidekorn<small>Lüneburger Heide</small></a>
<nav class="nav-links">
<a href="/#shop">Shop</a>
<a href="/#magazin">Magazin</a>
<button class="cart-btn" id="cartOpen" aria-label="Warenkorb öffnen">
Warenkorb <span class="cart-badge" id="cartBadge">0</span>
</button>
</nav>
</div></header>
<main><slot /></main>
<div class="drawer-bg" id="drawerBg"></div>
<aside class="drawer" id="drawer" aria-label="Warenkorb">
<header><h3>Dein Warenkorb</h3><button class="close" id="cartClose">×</button></header>
<div class="items" id="cartItems"></div>
<footer>
<div class="sumline"><span>Zwischensumme</span><span id="sumNet">0,00 €</span></div>
<div class="sumline"><span>inkl. MwSt.</span><span id="sumTax">0,00 €</span></div>
<div class="sumline total"><span>Gesamt</span><span id="sumTotal">0,00 €</span></div>
<button class="btn" style="width:100%;justify-content:center;margin-top:14px" id="checkoutBtn">Zur Kasse (Demo)</button>
<p style="font-size:.74rem;color:var(--slate);text-align:center;margin:.7em 0 0">Demoshop · keine echte Bestellung möglich</p>
</footer>
</aside>
<footer class="site"><div class="wrap">
<div class="fgrid">
<div>
<div class="brand" style="color:#fff">Hofladen Heidekorn<small>Lüneburger Heide</small></div>
<p style="max-width:34ch;color:oklch(0.78 0.02 220);font-size:.92rem;margin-top:14px">
Regionale Spezialitäten, direkt vom Hof. Dieser Shop ist ein technischer Demonstrator.</p>
<span class="demo-pill">Demo · Directus + Astro</span>
</div>
<div class="fcol"><h4>Shop</h4>
<a href="/#shop">Alle Produkte</a><a href="/#magazin">Magazin</a><a href="/warenkorb">Warenkorb</a>
</div>
<div class="fcol"><h4>Rechtliches</h4>
<a href="/impressum">Impressum</a><a href="/datenschutz">Datenschutz</a><a href="/agb">AGB</a><a href="/widerruf">Widerruf</a>
</div>
</div>
<div class="fnote"><span>© 2026 Hofladen Heidekorn (Demo) · Heidrich Digital</span><span>Preise inkl. MwSt., zzgl. Versand</span></div>
</div></footer>
<script is:inline>
const KEY='heidekorn_cart';
const load=()=>{try{return JSON.parse(localStorage.getItem(KEY))||[]}catch{return[]}};
const save=c=>localStorage.setItem(KEY,JSON.stringify(c));
const eur=n=>new Intl.NumberFormat('de-DE',{style:'currency',currency:'EUR'}).format(n);
let cart=load();
function add(p){const f=cart.find(i=>i.slug===p.slug);if(f)f.qty++;else cart.push({...p,qty:1});save(cart);render();open()}
function setQty(slug,d){const f=cart.find(i=>i.slug===slug);if(!f)return;f.qty+=d;if(f.qty<=0)cart=cart.filter(i=>i.slug!==slug);save(cart);render()}
function remove(slug){cart=cart.filter(i=>i.slug!==slug);save(cart);render()}
function totals(){let total=0,tax=0;cart.forEach(i=>{const line=i.preis*i.qty;total+=line;const r=(i.mwst||19)/100;tax+=line-(line/(1+r))});return{total,tax,net:total-tax}}
function render(){
const badge=document.getElementById('cartBadge');
const n=cart.reduce((s,i)=>s+i.qty,0);
if(badge)badge.textContent=n;
const wrap=document.getElementById('cartItems');
if(wrap){
if(!cart.length){wrap.innerHTML='<p class="empty">Dein Warenkorb ist leer.</p>';}
else{wrap.innerHTML=cart.map(i=>`<div class="ci"><img src="${i.bild_url}" alt=""><div class="meta"><div class="nm">${i.name}</div><div style="color:var(--slate);font-size:.8rem">${eur(i.preis)} · ${i.einheit||''}</div><div class="qty"><button data-dec="${i.slug}"></button><span>${i.qty}</span><button data-inc="${i.slug}">+</button><button class="rm" data-rm="${i.slug}">entfernen</button></div></div><div style="font-weight:600">${eur(i.preis*i.qty)}</div></div>`).join('');}
}
const t=totals();
const set=(id,v)=>{const e=document.getElementById(id);if(e)e.textContent=eur(v)};
set('sumNet',t.net);set('sumTax',t.tax);set('sumTotal',t.total);
document.dispatchEvent(new CustomEvent('cart:changed',{detail:{cart,totals:t}}));
}
const drawer=document.getElementById('drawer'),bg=document.getElementById('drawerBg');
function open(){drawer&&drawer.classList.add('open');bg&&bg.classList.add('open')}
function close(){drawer&&drawer.classList.remove('open');bg&&bg.classList.remove('open')}
document.getElementById('cartOpen')?.addEventListener('click',open);
document.getElementById('cartClose')?.addEventListener('click',close);
bg?.addEventListener('click',close);
document.getElementById('checkoutBtn')?.addEventListener('click',()=>{alert('Demoshop — hier würde der Checkout starten. Es findet keine echte Bestellung statt.')});
document.addEventListener('click',e=>{
const a=e.target.closest('[data-add]');
if(a){e.preventDefault();add(JSON.parse(a.getAttribute('data-add')));return}
const inc=e.target.closest('[data-inc]');if(inc){setQty(inc.getAttribute('data-inc'),1);return}
const dec=e.target.closest('[data-dec]');if(dec){setQty(dec.getAttribute('data-dec'),-1);return}
const rm=e.target.closest('[data-rm]');if(rm){remove(rm.getAttribute('data-rm'));return}
});
window.HK={add,setQty,remove,get:()=>cart,totals};
render();
</script>
</body>
</html>