diff --git a/docs/ui-ergebnis.png b/docs/ui-ergebnis.png index 34bc5cf..e12298d 100644 Binary files a/docs/ui-ergebnis.png and b/docs/ui-ergebnis.png differ diff --git a/docs/ui-stufen.png b/docs/ui-stufen.png index 492cd1c..355cb89 100644 Binary files a/docs/ui-stufen.png and b/docs/ui-stufen.png differ diff --git a/src/angebote/web_static/index.html b/src/angebote/web_static/index.html index f4582ab..c0e4689 100644 --- a/src/angebote/web_static/index.html +++ b/src/angebote/web_static/index.html @@ -24,7 +24,7 @@ --err:#8a231d; --err-bg:#fbeae8; --err-line:#e6b8b2; --price:#11221b; --line:#e3ded5; --line-2:#eceae2; --ctrl:#8a8474; - --focus:#0e5d42; + --focus:#0e5d42; color-scheme:light; --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s6:24px; --s8:32px; --s12:48px; --radius:16px; --radius-sm:11px; --radius-pill:999px; --appbar-h:60px; @@ -34,8 +34,27 @@ --shadow-lg:0 20px 50px -20px rgba(20,18,12,.30); --ring:0 0 0 3px var(--brand-weak), 0 0 0 1.5px var(--brand); } + /* Dunkel-Tokens — AAA-verifiziert. ACHTUNG: bewusst ZWEIMAL geführt, damit + beides geht: manueller Schalter ([data-theme=dark]) UND System-Automatik + (prefers-color-scheme, außer der Nutzer hat manuell „hell" gewählt). + Beide Blöcke MÜSSEN identische Werte haben. */ + :root[data-theme="dark"] { + --bg:#13120d; --panel:#1d1b15; --panel-2:#23211a; --panel-3:#2a2820; + --ink:#f2efe7; --ink-2:#d6d2c6; --muted:#cbc6b7; + --brand:#3db188; --brand-text:#6cd2a4; --on-brand:#06130c; + --brand-weak:#18352a; --brand-weak-line:#2c5a47; + --warn:#f4cf86; --warn-bg:#3a2f12; --warn-line:#5c4a1e; + --err:#f0a59c; --err-bg:#3a1f1c; --err-line:#5e2e2a; + --price:#eaf7f0; + --line:#2e2c24; --line-2:#272519; --ctrl:#767161; + --focus:#6cd2a4; color-scheme:dark; + --shadow-sm:0 1px 2px rgba(0,0,0,.4); + --shadow-md:0 12px 32px -14px rgba(0,0,0,.6); + --shadow-lg:0 24px 56px -22px rgba(0,0,0,.7); + --ring:0 0 0 3px rgba(108,210,164,.28), 0 0 0 1.5px var(--brand-text); + } @media (prefers-color-scheme: dark) { - :root { + :root:not([data-theme="light"]) { --bg:#13120d; --panel:#1d1b15; --panel-2:#23211a; --panel-3:#2a2820; --ink:#f2efe7; --ink-2:#d6d2c6; --muted:#cbc6b7; --brand:#3db188; --brand-text:#6cd2a4; --on-brand:#06130c; @@ -44,7 +63,7 @@ --err:#f0a59c; --err-bg:#3a1f1c; --err-line:#5e2e2a; --price:#eaf7f0; --line:#2e2c24; --line-2:#272519; --ctrl:#767161; - --focus:#6cd2a4; + --focus:#6cd2a4; color-scheme:dark; --shadow-sm:0 1px 2px rgba(0,0,0,.4); --shadow-md:0 12px 32px -14px rgba(0,0,0,.6); --shadow-lg:0 24px 56px -22px rgba(0,0,0,.7); @@ -306,6 +325,21 @@ border:1px solid var(--warn-line); border-radius:6px; padding:1px 8px; margin-left:8px; font-weight:700; } .leer { padding:var(--s4) var(--s6); color:var(--muted); font-style:italic; font-size:14px; } + /* Filter-Feedback: Treffer-Zähler, sanfte Einblendung, Leer-Zustand */ + .filterstat { font-size:13.5px; color:var(--muted); margin-bottom:var(--s3); font-weight:600; + display:flex; align-items:center; gap:8px; flex-wrap:wrap; } + .filterstat b { color:var(--ink); font-weight:800; } + .linkbtn { background:none; border:none; color:var(--brand-text); font:inherit; font-weight:700; + cursor:pointer; padding:2px 4px; min-height:0; text-decoration:underline; border-radius:6px; } + .linkbtn:hover { background:var(--brand-weak); filter:none; transform:none; box-shadow:none; } + .linkbtn:focus-visible { box-shadow:var(--ring); } + @keyframes grp-in { from { opacity:0; transform:translateY(5px); } to { opacity:1; transform:none; } } + #gruppen.filtern > section.grp { animation:grp-in .2s ease both; } + .keine-treffer { padding:var(--s12) var(--s6); text-align:center; background:var(--panel); + border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow-sm); color:var(--muted); } + .keine-treffer .big { font-size:18px; font-weight:800; color:var(--ink); margin-bottom:6px; } + @media (prefers-reduced-motion: reduce) { #gruppen.filtern > section.grp { animation:none; } } + /* Quelle: sichtbar lassen, nur schöner (kein Roh-String mehr) */ .quelle { display:inline-flex; align-items:center; gap:4px; font-size:11.5px; color:var(--muted); background:var(--panel-3); border:1px solid var(--line); border-radius:6px; padding:1px 7px; font-weight:600; } @@ -339,6 +373,12 @@ .grp-h-btn { padding-left:var(--s4); padding-right:var(--s4); } } +
Zum Inhalt springen @@ -359,6 +399,7 @@ PLZ … + @@ -475,6 +516,7 @@ + @@ -761,17 +803,23 @@ function zeichneGruppen(){ const fH = $("#fhaendler").value; const fT = $("#ftext").value.trim().toLowerCase(); const nurSicher = $("#fsicher").checked; + const filterAktiv = !!(fH || fT || nurSicher); const box = $("#gruppen"); box.innerHTML = ""; + box.classList.toggle("filtern", filterAktiv); // löst die Einblende-Animation aus (Feedback) const navDaten = []; + let treffer = 0; for (const g of d.gruppen){ const items = g.angebote.filter(a => passt(a, fH, fT, nurSicher)); + // Beim Filtern leere Gruppen GANZ ausblenden -> Treffer stehen sofort oben, + // man muss sie nicht zwischen leeren Boxen suchen. Ohne Filter: alle zeigen. + if (!items.length && filterAktiv) continue; + const sec = document.createElement("section"); sec.className = "grp"; sec.dataset.grp = g.name; - const secId = "grp-" + g.name.replace(/[^a-z0-9]+/gi, "-").toLowerCase(); - sec.id = secId; + sec.id = "grp-" + g.name.replace(/[^a-z0-9]+/gi, "-").toLowerCase(); const h = document.createElement("h3"); h.className = "grp-h"; @@ -791,19 +839,73 @@ function zeichneGruppen(){ if (!items.length){ const p = document.createElement("div"); p.className = "leer"; - p.textContent = (fH || fT || nurSicher) ? "keine Treffer im Filter" : "keine Angebote"; + p.textContent = "keine Angebote"; sec.appendChild(p); } else { const ul = document.createElement("ul"); for (const a of items) ul.appendChild(zeile(a, g.name)); sec.appendChild(ul); navDaten.push({ name:g.name, count:items.length, uns:items.filter(a=>a.unsicher).length }); + treffer += items.length; } box.appendChild(sec); } + + // 0 Treffer trotz aktivem Filter -> klarer Leer-Zustand statt leerer Seite + if (filterAktiv && !box.children.length){ + box.innerHTML = + `