From 0e4645541dc561614a1714abbd3dafacb9a0ea2a Mon Sep 17 00:00:00 2001 From: AliceDarkest Date: Mon, 29 Jun 2026 19:39:33 +0200 Subject: [PATCH] Responsive: mobile Off-Canvas-Navigation + Layout-Anpassungen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sidebar wird auf <=820px nicht mehr ausgeblendet (kein Nav-Verlust), sondern zu einem Off-Canvas-Drawer mit Hamburger-Button + Backdrop (theme.js injiziert ihn auf allen .mf-shell-Seiten; Styles in theme.css). - Topbar stapelt/umbricht auf Mobil (dekorativer Puls + Suche ausgeblendet, kleinere Begrüßung), HUD-Karten-Grid 3->2->1, breite mf-Tabellen horizontal scrollbar. Schließen per Backdrop, Nav-Link oder Escape. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 1 + src/main/resources/static/css/theme.css | 31 ++++++++++++++++++++++++- src/main/resources/static/js/theme.js | 26 +++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a37a519..39bed26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Konto-Löschung sowie ein app-weiter Farb-Relaunch auf das - **App-weiter Farb-Relaunch auf „Back to the Future"-Palette:** Primär **Feuer-Amber** (`#f0a81a`) + Gelb-Glow, **Flux-Blau** (`#2d9cce`) als Zweitakzent (u. a. Links); warmer Bernstein-Glow statt Matrix-Grün. Dark/Light bleiben (sehr dunkler/sehr heller Grund). Zentral über `theme.css`-Tokens; Legacy-Cyan in `style.css` auf Theme-Variablen umgestellt. - **Spiele-Feedback ohne Grün:** richtig = **Flux-Blau + ✓**, falsch = **Rot + ✗** in FinalFlux, MindMatch und TechTacular (löst den fehlenden Kontrast von „grün auf grün"); neue Tokens `--success`, `--danger`, `--secondary`. - Registrierungs-Duplikat leitet auf `/?registerError` (kombinierte Seite öffnet automatisch das Signup-Panel). +- **Vollständig responsive / mobiltauglich:** Auf schmalen Screens wird die App-Sidebar nicht mehr ausgeblendet (vorher fehlte die Navigation komplett), sondern zu einer Off-Canvas-Schublade mit Hamburger-Button + Backdrop (global via `theme.js` injiziert, Styles in `theme.css`). Topbar stapelt/umbricht (dekorativer Puls + Suche ausgeblendet), Spielkarten-Grid 3→2→1, breite Tabellen horizontal scrollbar. Spiele, FinalFlux und Login/Registrierung waren bereits responsiv. ### Behoben - **Konto-Löschung repariert:** „Profil löschen" warf einen 500er (fehlendes `@Transactional` bei einer abgeleiteten Delete-Query) und löschte nichts. Jetzt werden Konto **plus** Spielstände, Achievements und Profilbild-Datei in einer Transaktion entfernt — erfüllt die in der Datenschutzerklärung zugesicherte Löschung (Art. 17 DSGVO). diff --git a/src/main/resources/static/css/theme.css b/src/main/resources/static/css/theme.css index 2728384..5b80248 100644 --- a/src/main/resources/static/css/theme.css +++ b/src/main/resources/static/css/theme.css @@ -171,6 +171,35 @@ a { color:var(--secondary); } .mf-footer a:hover { color:var(--accent); } .mf-footer a + a::before { content:"·"; position:absolute; left:-.6rem; color:var(--border); } +/* ===== Mobile-Nav (Off-Canvas-Drawer; Button/Backdrop per theme.js injiziert) ===== */ +.mf-burger { display:none; position:fixed; top:.7rem; left:.7rem; z-index:1300; width:42px; height:42px; border-radius:10px; border:1px solid var(--border-accent); background:var(--surface); color:var(--accent); align-items:center; justify-content:center; cursor:pointer; box-shadow:0 0 14px var(--glow-w); } +.mf-burger svg { width:20px; height:20px; } +.mf-nav-backdrop { position:fixed; inset:0; background:rgba(0,0,0,.5); opacity:0; pointer-events:none; transition:opacity .28s ease; z-index:1100; } +body.mf-nav-open .mf-nav-backdrop { opacity:1; pointer-events:auto; } + @media (max-width:1180px){ .mf-shell{grid-template-columns:240px 1fr;} .mf-rightbar{display:none;} .mf-topbar{grid-column:2;} .mf-main{grid-column:2;} } -@media (max-width:820px){ .mf-shell{grid-template-columns:1fr;} .mf-sidebar{display:none;} .mf-topbar,.mf-main{grid-column:1;} .mf-grid{grid-template-columns:repeat(2,1fr);} } + +@media (max-width:820px){ + /* Shell stapelt; Sidebar wird zum Off-Canvas-Drawer */ + .mf-shell, .mf-shell.no-right { display:block; } + .mf-topbar { padding:1.05rem 1rem 0 3.7rem; flex-wrap:wrap; gap:.7rem; } + .mf-main { padding:1.1rem 1rem 1.4rem; } + .mf-rightbar { padding:0 1rem 1.4rem; } + .mf-grid { grid-template-columns:repeat(2,1fr); } + .mf-pulse, .mf-search { display:none; } + .mf-hello h1 { font-size:1.4rem; } + .mf-tools { margin-left:auto; } + + .mf-sidebar { + position:fixed; top:0; left:0; height:100dvh; width:min(284px,82vw); z-index:1200; + transform:translateX(-100%); transition:transform .28s ease; overflow-y:auto; + box-shadow:0 18px 50px rgba(0,0,0,.55); + } + body.mf-nav-open .mf-sidebar { transform:translateX(0); } + .mf-burger { display:inline-flex; } + + /* breite Tabellen horizontal scrollbar statt überlaufend */ + .mf-table { display:block; overflow-x:auto; white-space:nowrap; } +} + @media (max-width:560px){ .mf-grid{grid-template-columns:1fr;} } diff --git a/src/main/resources/static/js/theme.js b/src/main/resources/static/js/theme.js index df7c41b..31398eb 100644 --- a/src/main/resources/static/js/theme.js +++ b/src/main/resources/static/js/theme.js @@ -23,6 +23,31 @@ } window.MindfluxTheme = { toggle: toggle, apply: apply }; + // Mobile-Nav: Off-Canvas-Drawer für Seiten mit App-Sidebar (.mf-shell) + function initMobileNav() { + var sidebar = document.querySelector('.mf-sidebar'); + if (!sidebar || document.querySelector('.mf-burger')) return; + + var burger = document.createElement('button'); + burger.className = 'mf-burger'; + burger.setAttribute('aria-label', 'Menü öffnen'); + burger.innerHTML = ''; + + var backdrop = document.createElement('div'); + backdrop.className = 'mf-nav-backdrop'; + + function close() { document.body.classList.remove('mf-nav-open'); } + function toggleNav() { document.body.classList.toggle('mf-nav-open'); } + + burger.addEventListener('click', toggleNav); + backdrop.addEventListener('click', close); + sidebar.querySelectorAll('a').forEach(function (a) { a.addEventListener('click', close); }); + window.addEventListener('keydown', function (e) { if (e.key === 'Escape') close(); }); + + document.body.appendChild(backdrop); + document.body.appendChild(burger); + } + document.addEventListener('DOMContentLoaded', function () { document.querySelectorAll('.mf-theme').forEach(function (b) { b.setAttribute('role', 'button'); @@ -30,5 +55,6 @@ b.addEventListener('click', toggle); }); paint(); + initMobileNav(); }); })();