initial: rezepte, einkaufsliste, vorrat, website template

This commit is contained in:
Meleki committed 2026-02-17 06:30:14 +01:00
commit 07428b0456
22 files changed
+2071

No files matched your search

+363
View File
@@ -0,0 +1,363 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kochstube</title>
<style>
:root {
--bg: #f5f0e8;
--card: #ffffff;
--accent: #d94f30;
--accent2: #fef3e2;
--text: #2d2a26;
--muted: #7a7470;
--tag-bg: #f0e6d6;
--tag-text: #6b5e4f;
--quick: #e8960c;
--border: #e8e0d4;
--shadow: rgba(120, 100, 70, .1);
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--text);
min-height: 100vh;
}
header {
text-align: center;
padding: 2.5rem 1rem 1.5rem;
background: linear-gradient(135deg, #fff8ef, #fef3e2);
border-bottom: 3px solid var(--accent);
}
header h1 { font-size: 2.4rem; letter-spacing: 2px; color: #2d2a26; }
header h1 span { color: var(--accent); }
header p { color: var(--muted); margin-top: .4rem; font-size: .95rem; }
/* Filter bar */
.filters {
display: flex;
flex-wrap: wrap;
gap: .5rem;
justify-content: center;
padding: 1.2rem 1rem;
position: sticky;
top: 0;
background: var(--bg);
z-index: 10;
border-bottom: 1px solid var(--border);
box-shadow: 0 2px 8px var(--shadow);
}
.filters button {
background: var(--card);
color: var(--text);
border: 1px solid var(--border);
padding: .45rem 1rem;
border-radius: 20px;
cursor: pointer;
font-size: .85rem;
transition: all .2s;
}
.filters button:hover { background: var(--accent2); border-color: var(--accent); }
.filters button.active {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.filters input {
background: var(--card);
color: var(--text);
border: 1px solid var(--border);
padding: .45rem 1rem;
border-radius: 20px;
font-size: .85rem;
width: 220px;
outline: none;
}
.filters input:focus { border-color: var(--accent); }
.filters input::placeholder { color: #b5ada5; }
/* Grid */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
padding: 1.5rem;
max-width: 1400px;
margin: 0 auto;
}
/* Card */
.card {
background: var(--card);
border-radius: 16px;
overflow: hidden;
cursor: pointer;
transition: transform .2s, box-shadow .2s;
position: relative;
border: 1px solid var(--border);
box-shadow: 0 2px 12px var(--shadow);
}
.card:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(120, 100, 70, .18); }
.card img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
}
.card-body { padding: 1rem 1.2rem 1.2rem; }
.card-body h3 { font-size: 1.1rem; margin-bottom: .5rem; line-height: 1.3; color: var(--text); }
.card-body .desc { color: var(--muted); font-size: .85rem; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.card-meta {
display: flex;
gap: .8rem;
margin-top: .7rem;
font-size: .8rem;
color: var(--muted);
}
.card-meta span { display: flex; align-items: center; gap: .3rem; }
.badge-quick {
position: absolute;
top: 12px;
right: 12px;
background: var(--quick);
color: #fff;
font-size: .7rem;
font-weight: 700;
padding: .25rem .6rem;
border-radius: 10px;
box-shadow: 0 2px 6px rgba(0,0,0,.15);
}
.tags { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .6rem; }
.tags span {
background: var(--tag-bg);
color: var(--tag-text);
padding: .15rem .55rem;
border-radius: 8px;
font-size: .72rem;
}
/* Modal / Detail */
.overlay {
display: none;
position: fixed;
inset: 0;
background: rgba(45, 42, 38, .55);
backdrop-filter: blur(4px);
z-index: 100;
overflow-y: auto;
padding: 2rem 1rem;
}
.overlay.open { display: flex; justify-content: center; align-items: flex-start; }
.detail {
background: var(--card);
border-radius: 20px;
max-width: 750px;
width: 100%;
overflow: hidden;
position: relative;
animation: slideUp .3s ease;
box-shadow: 0 20px 60px rgba(0,0,0,.2);
}
@keyframes slideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
.detail img { width: 100%; height: 280px; object-fit: cover; }
.detail-body { padding: 1.5rem 2rem 2rem; }
.detail-body h2 { font-size: 1.5rem; margin-bottom: .3rem; color: var(--text); }
.detail-body .desc-full { color: var(--muted); margin-bottom: 1.2rem; line-height: 1.5; }
.detail-body .meta-row {
display: flex;
gap: 1.5rem;
margin-bottom: 1.2rem;
flex-wrap: wrap;
}
.meta-row .chip {
background: var(--accent2);
color: var(--text);
padding: .4rem .9rem;
border-radius: 12px;
font-size: .85rem;
border: 1px solid var(--border);
}
.detail-body h4 { color: var(--accent); margin: 1rem 0 .5rem; font-size: 1rem; text-transform: uppercase; letter-spacing: 1px; }
.detail-body ul { padding-left: 1.2rem; margin-bottom: .5rem; }
.detail-body li { margin-bottom: .3rem; line-height: 1.5; font-size: .92rem; }
.detail-body ol { padding-left: 1.2rem; }
.detail-body ol li { margin-bottom: .6rem; line-height: 1.5; font-size: .92rem; }
.detail .close-btn {
position: absolute;
top: 14px;
right: 14px;
background: rgba(45, 42, 38, .7);
color: #fff;
border: none;
width: 38px;
height: 38px;
border-radius: 50%;
font-size: 1.3rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background .2s;
}
.detail .close-btn:hover { background: var(--accent); }
.source-link {
display: inline-block;
margin-top: 1rem;
color: var(--accent);
font-size: .85rem;
text-decoration: none;
}
.source-link:hover { text-decoration: underline; }
.fresh-list { background: #fef6e9; border-left: 3px solid var(--quick); padding: .6rem 1rem; border-radius: 8px; margin-top: .5rem; font-size: .85rem; }
.fresh-list strong { color: #b47409; }
.no-results { text-align: center; padding: 3rem; color: var(--muted); font-size: 1.1rem; grid-column: 1 / -1; }
@media (max-width: 600px) {
header h1 { font-size: 1.8rem; }
.grid { padding: 1rem; gap: 1rem; grid-template-columns: 1fr; }
.detail-body { padding: 1.2rem; }
.filters input { width: 100%; }
}
</style>
</head>
<body>
<header>
<h1>🍳 Koch<span>stube</span></h1>
<p>Rezepte von Tausi & Nocki</p>
</header>
<div class="filters">
<input type="text" id="search" placeholder="🔍 Rezept suchen...">
<button class="active" data-filter="alle">Alle</button>
<button data-filter="unter-15-min">⚡ Unter 15 Min</button>
<button data-filter="vegan">🌱 Vegan</button>
<button data-filter="vegetarisch">🧀 Vegetarisch</button>
<button data-filter="fleisch">🥩 Fleisch</button>
<button data-filter="fisch">🐟 Fisch</button>
<button data-filter="ofen">🔥 Ofen</button>
<button data-filter="low-cal">💪 Low-Cal / Protein</button>
</div>
<div class="grid" id="grid"></div>
<div class="overlay" id="overlay">
<div class="detail" id="detail"></div>
</div>
<script>
const rezepte = [{"id": "15-minuten-pfanne", "titel": "15-Minuten-Pfanne mit Hackfleisch und Paprika", "bild": "https://ais.kochbar.de/kbrezept/74349_33645/1200x1200/15-minuten-pfanne-rezept.jpg", "beschreibung": "Hackfleisch, Paprika, Frühlingszwiebeln und Nudeln in einer schnellen Pfanne. Alltagstauglich und sättigend.", "kategorie": "Hauptspeise", "tags": ["pfanne", "hackfleisch", "schnell", "nudeln", "unter-15-min"], "schwierigkeit": "einfach", "zubereitungszeit_min": 15, "portionen": 4, "frisch_besorgen": ["Hackfleisch (300 g)", "Frühlingszwiebeln (1 Bund)"], "zutaten": [{"menge": "1 Bund", "zutat": "Frühlingszwiebeln"}, {"menge": "1", "zutat": "Paprikaschote"}, {"menge": "300 g", "zutat": "Hackfleisch"}, {"menge": "300 ml", "zutat": "Wasser"}, {"menge": "1 TL", "zutat": "Gemüsebrühe (Pulver)"}, {"menge": "200 g", "zutat": "kurze Nudeln (Gabelspaghetti o.ä.)"}, {"menge": "nach Wunsch", "zutat": "frische Champignons (optional)"}, {"menge": "etwas", "zutat": "Salz, Pfeffer"}, {"menge": "optional", "zutat": "Curry"}], "zubereitung": ["Hackfleisch scharf anbraten.", "Frühlingszwiebeln in Ringe schneiden, Paprika schneiden und dazugeben. Wer mag, gibt Champignons dazu. Kurz durchbraten.", "Gemüsebrühe in Wasser auflösen und dazugeben.", "Nudeln separat kochen, abgießen und zur Pfanne geben.", "Evtl. mit etwas Stärke andicken. Mit Salz, Pfeffer und optional Curry abschmecken."], "notizen": "Hackfleisch und Frühlingszwiebeln frisch besorgen. Paprika und Nudeln aus dem Vorrat.", "quelle": {"name": "kochbar.de", "url": "https://www.kochbar.de/rezept/74349/15-Minuten-Pfanne.html", "autor": "loewe_bs"}, "erstellt": "2025-02-16"}, {"id": "baked-feta-pasta", "titel": "Baked Feta Pasta", "beschreibung": "Cremige Ofenpasta mit gerösteten Kirschtomaten, Knoblauch und Feta - der TikTok-Klassiker. In unter 30 Minuten fertig.", "bild": "https://www.eat-this.org/wp-content/uploads/2021/02/eat_this_vegane_baked_feta_pasta-005.jpg", "kategorie": "Hauptspeise", "tags": ["pasta", "ofen", "schnell", "vegetarisch"], "schwierigkeit": "einfach", "zubereitungszeit_min": 30, "portionen": 2, "zutaten": [{"menge": "250 g", "zutat": "Cherrytomaten"}, {"menge": "4 Zehen", "zutat": "Knoblauch"}, {"menge": "200 g", "zutat": "Feta"}, {"menge": "1 TL", "zutat": "Oregano (gerebelt)"}, {"menge": "0.5 TL", "zutat": "Chiliflocken"}, {"menge": "0.5 TL", "zutat": "Salz"}, {"menge": "1-2 EL", "zutat": "Olivenöl"}, {"menge": "250 g", "zutat": "Pasta (z.B. Penne, Cellentani)"}, {"menge": "ein paar Blätter", "zutat": "Basilikum"}], "zubereitung": ["Backofen auf 220 °C Ober-/Unterhitze vorheizen.", "Tomaten, geschälte Knoblauchzehen, Feta, Gewürze, Salz, ein paar Blätter Basilikum und Olivenöl in eine Auflaufform geben und 25 Minuten backen.", "Währenddessen Pasta in reichlich Salzwasser al dente kochen und abgießen.", "Tomaten-Feta-Mischung aus dem Ofen nehmen, Tomatenstile entfernen, alles mit einer Gabel zerdrücken und zu einer cremigen Sauce verrühren.", "Heiße Pasta untermischen und mit frischen Basilikumblättern servieren."], "notizen": "Statt veganem Feta einfach normalen Feta nehmen. Cherrytomaten müssen evtl. besorgt werden.", "quelle": {"name": "eat-this.org", "url": "https://www.eat-this.org/vegane-baked-feta-pasta/", "autor": "Nadine"}, "erstellt": "2025-02-16"}, {"id": "erdnussbutter-nudeln-sambal", "titel": "Schnelle Erdnussbutter-Nudeln mit Sambal", "bild": "https://www.eat-this.org/wp-content/uploads/2021/10/eat_this_schnelle_erdnussbutternudeln_mit_sambal-003.jpg", "beschreibung": "Ultracremige Erdnussbutter-Nudeln in 15 Minuten aus Vorratszutaten. Sojasauce, Essig und Sambal sorgen für den perfekten Kick.", "kategorie": "Hauptspeise", "tags": ["pasta", "erdnuss", "schnell", "vegan", "unter-15-min"], "schwierigkeit": "einfach", "zubereitungszeit_min": 15, "portionen": 2, "zutaten": [{"menge": "200 g", "zutat": "Nudeln (Spaghetti, Mie-Nudeln o.ä.)"}, {"menge": "80 g", "zutat": "Erdnussbutter"}, {"menge": "3 EL", "zutat": "Sojasauce"}, {"menge": "1 EL", "zutat": "Reisessig (oder heller Essig)"}, {"menge": "1 TL", "zutat": "Sesamöl (optional)"}, {"menge": "2 TL", "zutat": "Sambal Oelek (oder Chili)"}, {"menge": "2 Zehen", "zutat": "Knoblauch"}, {"menge": "80 ml", "zutat": "heißes Wasser"}, {"menge": "100 g", "zutat": "Gurke (in Scheiben)"}, {"menge": "70 g", "zutat": "Radieschen (optional)"}, {"menge": "2 EL", "zutat": "geröstete Erdnüsse"}], "zubereitung": ["Knoblauch reiben. Erdnussbutter, Sojasauce, Essig, Sesamöl, Sambal und heißes Wasser zu einer cremigen Sauce verrühren.", "Gurke (und Radieschen) in dünne Scheiben schneiden.", "Nudeln nach Packungsanweisung kochen, 1-2 Minuten früher abgießen. Dabei 100-200 ml Nudelwasser auffangen.", "Nudeln sofort mit der Erdnusssauce und dem Gemüse vermischen. Nach und nach Nudelwasser unterrühren bis cremige Konsistenz entsteht.", "Mit gerösteten Erdnüssen und extra Sambal servieren."], "notizen": "Erdnussbutter steht auf der Einkaufsliste! SojasauLine truncated
const hasFleisch = r => (r.frisch_besorgen||[]).some(z => /hack|huhn|hähn|pute|schwein|rind|schinken/i.test(z)) || r.tags.some(t => /hackfleisch|hähnchen|pute|geschnetzeltes/i.test(t));
const hasFisch = r => r.tags.some(t => /lachs|fisch|garnelen/i.test(t)) || (r.frisch_besorgen||[]).some(z => /lachs|fisch|garnelen/i.test(z));
function matches(r, filter) {
if (filter === 'alle') return true;
if (filter === 'unter-15-min') return r.zubereitungszeit_min <= 15;
if (filter === 'vegan') return r.tags.includes('vegan');
if (filter === 'vegetarisch') return r.tags.includes('vegetarisch') || r.tags.includes('vegan');
if (filter === 'fleisch') return hasFleisch(r);
if (filter === 'fisch') return hasFisch(r);
if (filter === 'ofen') return r.tags.includes('ofen') || r.tags.includes('auflauf') || r.tags.includes('gratin');
if (filter === 'low-cal') return r.tags.includes('low-cal') || r.tags.includes('protein');
return true;
}
let activeFilter = 'alle';
function render() {
const q = document.getElementById('search').value.toLowerCase();
const grid = document.getElementById('grid');
const filtered = rezepte.filter(r => {
if (!matches(r, activeFilter)) return false;
if (q) {
const hay = [r.titel, r.beschreibung, ...r.tags, ...(r.zutaten||[]).map(z=>z.zutat)].join(' ').toLowerCase();
return hay.includes(q);
}
return true;
});
if (!filtered.length) {
grid.innerHTML = '<div class="no-results">Keine Rezepte gefunden 🤷</div>';
return;
}
grid.innerHTML = filtered.map(r => `
<div class="card" onclick="openDetail('${r.id}')">
${r.zubereitungszeit_min <= 15 ? '<div class="badge-quick">⚡ ' + r.zubereitungszeit_min + ' Min</div>' : ''}
<img src="${r.bild}" alt="${r.titel}" loading="lazy" onerror="this.style.background='#f0e6d6';this.style.height='120px'">
<div class="card-body">
<h3>${r.titel}</h3>
<div class="desc">${r.beschreibung}</div>
<div class="card-meta">
<span>⏱ ${r.zubereitungszeit_min} Min</span>
<span>👥 ${r.portionen} Port.</span>
<span>📊 ${r.schwierigkeit}</span>
</div>
<div class="tags">${r.tags.map(t => `<span>${t}</span>`).join('')}</div>
</div>
</div>
`).join('');
}
function openDetail(id) {
const r = rezepte.find(x => x.id === id);
if (!r) return;
const freshHtml = (r.frisch_besorgen && r.frisch_besorgen.length)
? `<div class="fresh-list"><strong>🛒 Frisch besorgen:</strong> ${r.frisch_besorgen.join(', ')}</div>` : '';
document.getElementById('detail').innerHTML = `
<button class="close-btn" onclick="closeDetail()">✕</button>
<img src="${r.bild}" alt="${r.titel}">
<div class="detail-body">
<h2>${r.titel}</h2>
<div class="desc-full">${r.beschreibung}</div>
<div class="meta-row">
<span class="chip">⏱ ${r.zubereitungszeit_min} Min</span>
<span class="chip">👥 ${r.portionen} Portionen</span>
<span class="chip">📊 ${r.schwierigkeit}</span>
</div>
${freshHtml}
<h4>Zutaten</h4>
<ul>${r.zutaten.map(z => `<li><strong>${z.menge}</strong> ${z.zutat}</li>`).join('')}</ul>
${r.currypaste_zutaten ? `<h4>Currypaste</h4><ul>${r.currypaste_zutaten.map(z => `<li>${z}</li>`).join('')}</ul>` : ''}
${r.topping ? `<h4>Topping</h4><ul>${r.topping.map(t => `<li>${t}</li>`).join('')}</ul>` : ''}
<h4>Zubereitung</h4>
<ol>${r.zubereitung.map(s => `<li>${s}</li>`).join('')}</ol>
${r.notizen ? `<h4>Notizen</h4><p style="color:var(--muted);font-size:.9rem;line-height:1.5">${r.notizen}</p>` : ''}
<a class="source-link" href="${r.quelle.url}" target="_blank" rel="noopener">📎 Quelle: ${r.quelle.name} (${r.quelle.autor || ''})</a>
</div>
`;
document.getElementById('overlay').classList.add('open');
document.body.style.overflow = 'hidden';
}
function closeDetail() {
document.getElementById('overlay').classList.remove('open');
document.body.style.overflow = '';
}
document.getElementById('overlay').addEventListener('click', e => {
if (e.target === e.currentTarget) closeDetail();
});
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeDetail(); });
document.querySelectorAll('.filters button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.filters button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
activeFilter = btn.dataset.filter;
render();
});
});
document.getElementById('search').addEventListener('input', render);
render();
</script>
</body>
</html>