From 3bc6c8f75cfed2ff48cafc2b526375ac6a685570 Mon Sep 17 00:00:00 2001 From: AliceDarkest Date: Wed, 22 Jul 2026 13:04:25 +0200 Subject: [PATCH] Refine Light Mode, UX und Mail-Diagnose --- CHANGELOG.md | 23 + STYLEGUIDE.md | 10 +- mindfluxrefine2.md | 305 ++++++++++ .../controller/RegisterController.java | 12 +- .../mindflux/service/AccountMailService.java | 80 ++- .../static/css/accessibility-placement.css | 1 + .../resources/static/css/accessibility.css | 3 + .../resources/static/css/achievements.css | 165 ------ .../resources/static/css/codeescape-guide.css | 1 + src/main/resources/static/css/codeescape.css | 4 + src/main/resources/static/css/finalflux.css | 1 + .../resources/static/css/networker-guide.css | 1 + .../resources/static/css/platform-neural.css | 6 + src/main/resources/static/css/progress.css | 184 ------ src/main/resources/static/css/settings.css | 207 ------- src/main/resources/static/css/soziales.css | 78 --- src/main/resources/static/css/style.css | 555 +----------------- src/main/resources/static/css/syntaxsaga.css | 3 + src/main/resources/static/css/theme.css | 69 ++- src/main/resources/static/js/devlabgame.js | 5 +- .../resources/static/js/finalflux-check.js | 29 - .../resources/static/js/finalflux-test.js | 2 + src/main/resources/static/js/mindmatch.js | 1 + src/main/resources/static/js/syntaxsaga.js | 3 +- src/main/resources/static/js/techtacular.js | 1 + src/main/resources/static/js/theme.js | 31 +- src/main/resources/templates/finalflux.html | 5 +- 27 files changed, 519 insertions(+), 1266 deletions(-) create mode 100644 mindfluxrefine2.md delete mode 100644 src/main/resources/static/css/achievements.css delete mode 100644 src/main/resources/static/css/progress.css delete mode 100644 src/main/resources/static/css/settings.css delete mode 100644 src/main/resources/static/css/soziales.css delete mode 100644 src/main/resources/static/js/finalflux-check.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c976bae..d176a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ Versionierung [semantisch](https://semver.org/lang/de/). ## [Unreleased] – 2026-07-21 · Gesamt-Audit: Light Mode, Barrierefreiheit, AP2-Inhalte, DSGVO ### Light Mode & Design +- **Light-Mode-Fundament:** zentrale UI-Effekte sind jetzt als Theme-Primitives + organisiert. Dark Mode behält Neon-Emissionen; Light Mode nutzt warme Elevation, + entfernt Text-/Icon-Halos und erhält einen sichtbaren Scrollbar-Thumb. Native + Controls folgen über `color-scheme` dem gewählten Theme. +- **Theme-Start & FinalProtocol:** Erstbesucher folgen der OS-Farbpräferenz, die + Browser-Chrome erhält ein passendes `theme-color` und FinalProtocol lädt keinen + ungenutzten Legacy-Style mehr; die statischen Lernseiten erhalten daraus nur noch + eine minimale Reset-Basis. +- **Kritische Light-Mode-Fälle behoben:** Login-Overlay, SyntaxSaga-Bühne, + CodeEscape-Terminal, NetWorker-Guide und die Farbenblind-Palette sind in heller + Darstellung wieder lesbar und kontraststark. A11y-Widget und FinalProtocol + verwenden ebenfalls themefähige Farben. +- **Ansichtswechsel abgesichert:** zentrale SPA-Übergänge starten oben, ohne laufendes + Antwortfeedback zu versetzen. FinalProtocol scrollt Ergebnisse und Neustarts gezielt + ins Sichtfeld; SyntaxSaga und DevLab fokussieren ohne Scrollsprung. - **Echter Light Mode statt Invertierung:** Light-Tokens auf WCAG AA nachgeschärft (Akzent, Muted, Sekundär, Spielakzente), neues `--warn`-Token. Die Spielkarten der Plattform-Hülle erhalten im Light Mode farbton-treu abgedunkelte Akzente statt @@ -48,6 +63,14 @@ Versionierung [semantisch](https://semver.org/lang/de/). - **Demo-Konto „NeuralTest"** wird per Migration aus produktiven Datenbanken entfernt. ### Technik & Qualität +- **E-Mail-Versand diagnostizierbar:** Bestätigungs- und Reset-Mails schreiben jetzt + datensparsame, korrelierbare Ereignislogs für Token-Erstellung, SMTP-Übergabe, + Fehler, erneute Anforderung und Verifikation. Roh-Tokens und Bestätigungslinks + werden nie protokolliert; eine SMTP-Annahme wird nicht als Postfachzustellung + ausgegeben. +- **Legacy aufgeräumt:** die nicht referenzierten Styles für Achievements, Progress, + Settings und Soziales sowie `finalflux-check.js` entfernt; der Styleguide dokumentiert + die neuen Effekt-Primitives. - **Chart.js lokal ausgeliefert** statt vom CDN – das einzige externe Asset entfällt, wodurch die Datenschutzerklärung wieder zur Realität passt. - **Security-Header** ergänzt: Content-Security-Policy (self-hosted), Referrer-Policy; diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 31760b8..e179cd9 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -162,16 +162,18 @@ Feedback-Konvention (Blau/Rot) einhalten. ## Do / Don't **Do** -- Farben/Schatten/Glow ausschließlich über Tokens. +- Farben, Schatten und Effekte ausschließlich über Tokens. Für Effekte die + Primitives `--g-text`, `--g-icon`, `--g-strong`, `--g-amb`, `--g-card`, + `--g-card-hover`, `--g-overlay` und `--g-focus` verwenden; keine direkten + Glow-Geometrien in Komponenten schreiben. - Auf Akzentflächen Text in `--accent-ink`. - Erfolg = Blau, Fehler = Rot (nie Grün/Orange für „richtig/falsch"). - Neue Komponenten an den `.mf-*`-Bausteinen orientieren. **Don't** - Keine hart kodierten Hex-/RGBA-Werte in Templates oder Spiele-CSS. -- Nicht die **Legacy-Dateien** als Stil-Referenz nehmen: `achievements.css`, `progress.css`, - `settings.css`, `soziales.css` (ungenutzt) sowie alte Schrift-Deklarationen - (`'Segoe UI'`/`'Poppins'`) entsprechen **nicht** dem aktuellen System. Maßgeblich ist `theme.css`. +- Keine direkten `0 0 … var(--glow-*)`-, `text-shadow`- oder `drop-shadow`-Werte + in Komponenten. Maßgeblich ist `theme.css`. --- diff --git a/mindfluxrefine2.md b/mindfluxrefine2.md new file mode 100644 index 0000000..ddd5ccf --- /dev/null +++ b/mindfluxrefine2.md @@ -0,0 +1,305 @@ +# Mindflux — Refine 2: Light-Mode-Konzept, Scroll-Verhalten, Restpunkte + +## Kontext + +Folgeplan zu `mindfluxrefine.md`. Die Code-Verifikation (2026-07-21) bestätigt: **alle „vollständig"-Claims aus Refine 1 halten der Prüfung stand**; offen sind AP 1e (Glow im Light Mode), der AP-7-Rest sowie einige Zusatzfunde (s. AP R5). Dieser Plan liefert das, was Refine 1 offen ließ: ein **echtes Light-Mode-Konzept**, das alle Probleme an der Wurzel behebt (statt Glow-Alphas weiter zu dimmen), plus das neue Thema **Scroll-Verhalten** (Ergebnisanzeigen u. ä. müssen oben starten, nicht unten). + +**Audit-Kurzbefunde (2026-07-21, drei unabhängige Durchgänge):** + +- **Light-Mode-Strukturfehler:** Der Light-Block in `theme.css` tauscht nur *Farbwerte/Alphas* der Glow-Tokens. Die *Geometrie* der Effekte (`box-shadow:0 0 20px …`, `text-shadow:0 0 12px …`, `drop-shadow(0 0 8px …)`) steht hart in ~70 Komponentenregeln quer durch `theme.css`, alle Spiele-CSS und Template-` - -.admin-actions { - display: flex; - flex-direction: column; - gap: 1rem; - margin-top: 1rem; -} - -.admin-card { - display: block; - background-color: var(--glow-w); - padding: 1rem 1.5rem; - border-radius: 12px; - color: var(--accent); - text-decoration: none; - font-weight: bold; - transition: all 0.3s ease; - box-shadow: 0 4px 12px var(--glow-w); -} - -.admin-card:hover { - background-color: var(--glow-s); - transform: translateY(-2px); - box-shadow: 0 6px 18px var(--glow-w); -} - -.admin-card.disabled { - opacity: 0.4; - pointer-events: none; - cursor: default; -} -.admin-actions { - text-align: left; - margin: 1.5rem 2rem; -} - -.btn-create { - display: inline-block; - padding: 0.6rem 1.2rem; - font-size: 1rem; - background-color: var(--accent); - color: var(--accent-ink); - border: none; - border-radius: 10px; - font-weight: bold; - text-decoration: none; - box-shadow: 0 4px 10px var(--glow-s); - transition: background-color 0.3s, transform 0.2s; -} - -.btn-create:hover { - background-color: var(--accent); - transform: translateY(-2px); -} +/* Legacy entry point retained for static FinalProtocol lessons. + Layout and components live in theme.css and finalflux.css. */ +html,body{min-height:100%} +body{margin:0} diff --git a/src/main/resources/static/css/syntaxsaga.css b/src/main/resources/static/css/syntaxsaga.css index f22e349..5a4c1bd 100644 --- a/src/main/resources/static/css/syntaxsaga.css +++ b/src/main/resources/static/css/syntaxsaga.css @@ -249,3 +249,6 @@ body.ss-body { /* Textadventure-Terminal: phosphorige Log-Atmosphäre, Bewertung bleibt blau/rot. */ body.ss-body { --accent:var(--ss-accent); --accent-2:var(--ss-secondary); --glow-s:var(--ss-glow); --glow-w:color-mix(in srgb,var(--ss-accent) 15%,transparent); background:radial-gradient(circle at 45% -8%,color-mix(in srgb,var(--ss-accent) 16%,transparent),transparent 42%),#07100b; } body.ss-body::before { content:""; position:fixed; inset:0; pointer-events:none; opacity:.14; background:repeating-linear-gradient(0deg,transparent 0 3px,rgba(129,214,149,.18) 4px 5px); }.ss-shell { position:relative; z-index:1; }.ss-code { border-color:color-mix(in srgb,var(--ss-accent) 55%,var(--border)); box-shadow:inset 0 0 28px color-mix(in srgb,var(--ss-accent) 10%,transparent); }.ss-mission-no::before { content:"> "; color:var(--ss-secondary); } +/* Daylight keeps the terminal texture without retaining a black stage. */ +[data-theme="light"] body.ss-body{background:radial-gradient(1100px 500px at 50% -10%,rgba(39,125,73,.08),transparent 60%),#eef6f0} +[data-theme="light"] body.ss-body::before{background:repeating-linear-gradient(0deg,rgba(16,64,40,.05) 0 1px,transparent 1px 4px)} diff --git a/src/main/resources/static/css/theme.css b/src/main/resources/static/css/theme.css index 6e8b9d9..5bab42a 100644 --- a/src/main/resources/static/css/theme.css +++ b/src/main/resources/static/css/theme.css @@ -9,6 +9,7 @@ ============================================================ */ :root, [data-theme="dark"] { + color-scheme:dark; --bg:#0a0806; --bg-glow: radial-gradient(900px 420px at 60% -10%, rgba(240,168,26,.10), transparent 60%); --panel:#13100b; --panel-2:#171209; --surface:#120f0a; --surface-2:#1b1610; --border:#2a2117; --line:#221b12; @@ -29,9 +30,20 @@ --fluxtimer-display:#170704; --fluxtimer-segment:#ff765f; --glow-s:rgba(240,168,26,.55); --glow-w:rgba(240,168,26,.16); --g-soft:0 0 18px var(--glow-w); + --g-text:0 0 12px var(--glow-s); + --g-icon:drop-shadow(0 0 8px var(--glow-s)); + --g-strong:0 0 20px var(--glow-s); + --g-amb:0 0 14px var(--glow-w); + --g-card:drop-shadow(0 0 7px var(--glow-w)); + --g-card-hover:drop-shadow(0 0 16px var(--glow-s)); + --g-overlay:0 24px 60px rgba(0,0,0,.5); + --g-focus:0 0 0 3px var(--glow-w); + --g-bar:0 0 10px var(--glow-s); + --scroll-thumb:var(--line); } [data-theme="light"] { - --bg:#fbf6ec; --bg-glow: radial-gradient(900px 420px at 60% -10%, rgba(214,81,22,.10), transparent 60%); + color-scheme:light; + --bg:#fbf6ec; --bg-glow: radial-gradient(900px 420px at 60% -10%, rgba(214,81,22,.05), transparent 60%); --panel:#ffffff; --panel-2:#fdf7ea; --surface:#ffffff; --surface-2:#fbf4e6; --border:#ece1cf; --line:#f1e8d8; --border-accent:rgba(194,65,12,.40); --accent:#c2410c; --accent-2:#b45309; --accent-ink:#ffffff; @@ -51,6 +63,16 @@ --fluxtimer-display:#fff5ec; --fluxtimer-segment:#a93131; --glow-s:rgba(214,81,22,.40); --glow-w:rgba(214,81,22,.12); --g-soft:0 6px 18px var(--glow-w); + --g-text:none; + --g-icon:none; + --g-strong:0 2px 10px rgba(67,48,26,.18),0 1px 3px rgba(67,48,26,.10); + --g-amb:0 1px 5px rgba(67,48,26,.10); + --g-card:drop-shadow(0 2px 5px rgba(67,48,26,.10)); + --g-card-hover:drop-shadow(0 8px 18px rgba(67,48,26,.16)); + --g-overlay:0 18px 44px rgba(67,48,26,.20); + --g-focus:0 0 0 3px color-mix(in srgb,var(--accent) 28%,transparent); + --g-bar:none; + --scroll-thumb:#d9cbb2; } * { box-sizing:border-box; margin:0; padding:0; } @@ -61,7 +83,7 @@ body { min-height:100vh; transition:background-color .35s ease,color .35s ease; } a { color:var(--secondary); } -::-webkit-scrollbar{width:10px;height:10px} ::-webkit-scrollbar-thumb{background:var(--line);border-radius:9px} +::-webkit-scrollbar{width:10px;height:10px} ::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:9px} /* ===== App-Shell (Sidebar | Mitte | rechte Spalte) ===== */ .mf-shell { display:grid; grid-template-columns:264px 1fr 332px; grid-template-rows:auto 1fr; min-height:100vh; } @@ -76,22 +98,22 @@ a { color:var(--secondary); } .mf-logo { display:flex; align-items:center; gap:.6rem; padding:.2rem .3rem .4rem; text-decoration:none; } .mf-logo .mark { width:38px; height:38px; flex:0 0 auto; background:var(--accent); -webkit-mask:url("/img/mindflux-emblem.png") center/contain no-repeat; mask:url("/img/mindflux-emblem.png") center/contain no-repeat; - filter:drop-shadow(0 0 8px var(--glow-s)); } -.mf-logo .txt b { color:var(--accent); font-weight:800; letter-spacing:.13em; font-size:1.02rem; display:block; text-shadow:0 0 12px var(--glow-s); } + filter:var(--g-icon); } +.mf-logo .txt b { color:var(--accent); font-weight:800; letter-spacing:.13em; font-size:1.02rem; display:block; text-shadow:var(--g-text); } .mf-logo .txt span { color:var(--muted); font-size:.56rem; letter-spacing:.22em; } /* ===== Sidebar-Navigation ===== */ .mf-nav { display:flex; flex-direction:column; gap:.25rem; } .mf-nav a { display:flex; align-items:center; gap:.75rem; padding:.7rem .85rem; border-radius:11px; color:var(--muted); text-decoration:none; font-size:.9rem; font-weight:600; border:1px solid transparent; transition:all .18s ease; } .mf-nav a:hover { color:var(--text); background:var(--surface-2); } -.mf-nav a.active { color:var(--accent-ink); background:linear-gradient(100deg,var(--accent),var(--accent-2)); box-shadow:0 0 20px var(--glow-s); } +.mf-nav a.active { color:var(--accent-ink); background:linear-gradient(100deg,var(--accent),var(--accent-2)); box-shadow:var(--g-strong); } .mf-nav a.active svg { color:var(--accent-ink); } .mf-nav svg { width:18px; height:18px; color:var(--accent); flex:0 0 auto; } /* ===== Profil / Level / Streak ===== */ .mf-profile { border:1px solid var(--border); border-radius:13px; padding:.7rem; background:var(--surface); } .mf-profile .row { display:flex; align-items:center; gap:.6rem; } -.mf-avatar { width:38px; height:38px; border-radius:50%; background:linear-gradient(135deg,var(--accent),var(--accent-2)); display:inline-flex; align-items:center; justify-content:center; color:var(--accent-ink); font-weight:800; box-shadow:0 0 14px var(--glow-s); overflow:hidden; } +.mf-avatar { width:38px; height:38px; border-radius:50%; background:linear-gradient(135deg,var(--accent),var(--accent-2)); display:inline-flex; align-items:center; justify-content:center; color:var(--accent-ink); font-weight:800; box-shadow:var(--g-strong); overflow:hidden; } .mf-avatar img { width:100%; height:100%; object-fit:cover; } .mf-profile b { color:var(--heading); font-size:.86rem; display:block; } .mf-profile .role { color:var(--accent); font-size:.72rem; } @@ -99,25 +121,25 @@ a { color:var(--secondary); } .mf-meta .lvl { color:var(--muted); letter-spacing:.04em; } .mf-meta .lvl b { color:var(--heading); } .mf-meta .xp { color:var(--muted); margin:.2rem 0 .35rem; } .mf-streak { display:flex; align-items:center; gap:.55rem; margin-top:.2rem; } -.mf-streak .fire { font-size:1.1rem; filter:drop-shadow(0 0 6px var(--glow-s)); } +.mf-streak .fire { font-size:1.1rem; filter:var(--g-icon); } .mf-streak b { color:var(--heading); font-size:.82rem; display:block; } .mf-streak small { color:var(--muted); } -.mf-brain { margin-top:auto; opacity:.55; display:flex; justify-content:center; filter:drop-shadow(0 0 10px var(--glow-s)); } +.mf-brain { margin-top:auto; opacity:.55; display:flex; justify-content:center; filter:var(--g-icon); } .mf-brain svg { width:150px; height:auto; } /* ===== Topbar ===== */ .mf-hello h1 { color:var(--heading); font-size:1.85rem; font-weight:800; letter-spacing:-.01em; } -.mf-hello h1 .nm { color:var(--accent); text-shadow:0 0 16px var(--glow-s); } +.mf-hello h1 .nm { color:var(--accent); text-shadow:var(--g-text); } .mf-hello p { color:var(--muted); margin-top:.35rem; font-size:.95rem; } .mf-tools { display:flex; align-items:center; gap:.6rem; margin-left:auto; } .mf-iconbtn { width:42px; height:42px; border-radius:10px; border:1px solid var(--border); background:var(--surface); color:var(--muted); display:inline-flex; align-items:center; justify-content:center; cursor:pointer; text-decoration:none; transition:all .18s; } -.mf-iconbtn:hover { color:var(--accent); border-color:var(--border-accent); box-shadow:0 0 14px var(--glow-w); } +.mf-iconbtn:hover { color:var(--accent); border-color:var(--border-accent); box-shadow:var(--g-amb); } .mf-iconbtn svg { width:20px; height:20px; } .mf-search { display:flex; align-items:center; gap:.5rem; min-height:42px; background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:.5rem .8rem; color:var(--muted); } .mf-search input { background:none; border:0; outline:none; color:var(--text); font-size:.86rem; width:130px; } .mf-search svg { width:15px; height:15px; } /* ===== Theme-Umschalter ===== */ -.mf-theme { width:42px; height:42px; border-radius:10px; border:1px solid var(--border-accent); background:var(--surface); cursor:pointer; display:inline-flex; align-items:center; justify-content:center; color:var(--accent); box-shadow:0 0 14px var(--glow-w); } +.mf-theme { width:42px; height:42px; border-radius:10px; border:1px solid var(--border-accent); background:var(--surface); cursor:pointer; display:inline-flex; align-items:center; justify-content:center; color:var(--accent); box-shadow:var(--g-amb); } .mf-theme svg { width:20px; height:20px; } /* ===== HUD-Spielkarten ===== */ @@ -125,15 +147,15 @@ a { color:var(--secondary); } .mf-card { position:relative; display:flex; flex-direction:column; padding:1.15rem 1.2rem 1.1rem; background:var(--surface); border:1px solid var(--border-accent); --n:18px; clip-path:polygon(0 0, calc(100% - var(--n)) 0, 100% var(--n), 100% 100%, 0 100%); - text-decoration:none; color:inherit; filter:drop-shadow(0 0 7px var(--glow-w)); transition:transform .16s ease, filter .25s ease, background-color .2s; } + text-decoration:none; color:inherit; filter:var(--g-card); transition:transform .16s ease, filter .25s ease, background-color .2s; } .mf-card::before { content:""; position:absolute; left:10px; top:10px; width:14px; height:14px; border-left:2px solid var(--accent); border-top:2px solid var(--accent); opacity:.7; } -.mf-card:hover { transform:translateY(-4px); background:var(--surface-2); filter:drop-shadow(0 0 16px var(--glow-s)); } -.mf-card .ico { width:58px; height:58px; color:var(--accent); margin-bottom:.7rem; filter:drop-shadow(0 0 6px var(--glow-s)); } +.mf-card:hover { transform:translateY(-4px); background:var(--surface-2); filter:var(--g-card-hover); } +.mf-card .ico { width:58px; height:58px; color:var(--accent); margin-bottom:.7rem; filter:var(--g-icon); } .mf-card .ico svg { width:100%; height:100%; } .mf-card h3 { color:var(--heading); font-size:1.06rem; font-weight:700; margin-bottom:.4rem; } .mf-card p { color:var(--muted); font-size:.83rem; line-height:1.5; flex:1; } .mf-play { margin-top:.9rem; align-self:flex-start; display:inline-flex; align-items:center; gap:.45rem; font-size:.74rem; font-weight:800; letter-spacing:.08em; color:var(--accent); border:1px solid var(--border-accent); border-radius:8px; padding:.5rem .85rem; text-decoration:none; transition:all .18s; } -.mf-card:hover .mf-play { background:var(--accent); color:var(--accent-ink); box-shadow:0 0 16px var(--glow-s); } +.mf-card:hover .mf-play { background:var(--accent); color:var(--accent-ink); box-shadow:var(--g-strong); } .mf-play svg { width:14px; height:14px; } .mf-card-codeescape { --accent:var(--esc-accent); --glow-s:var(--esc-glow); --glow-w:color-mix(in srgb,var(--esc-accent) 16%,transparent); background:repeating-linear-gradient(0deg,rgba(255,255,255,.015) 0 1px,transparent 1px 4px),var(--surface); } .mf-card-codeescape::after { content:"ACCESS"; position:absolute; right:12px; top:9px; color:var(--esc-accent); font:700 .52rem/1 "Courier New",monospace; letter-spacing:.12em; opacity:.6; } @@ -143,15 +165,15 @@ a { color:var(--secondary); } /* ===== Panels / Widgets ===== */ .mf-panel { background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:1.1rem; box-shadow:var(--g-soft); } -.mf-panel.accent { border-color:var(--border-accent); box-shadow:0 0 20px var(--glow-w); } +.mf-panel.accent { border-color:var(--border-accent); box-shadow:var(--g-amb); } .mf-panel h4 { display:flex; align-items:center; gap:.5rem; color:var(--accent); font-size:.74rem; letter-spacing:.12em; text-transform:uppercase; margin-bottom:.9rem; } .mf-panel h4 svg { width:15px; height:15px; } .mf-eyebrow { color:var(--accent); font-weight:700; text-transform:uppercase; letter-spacing:.08em; font-size:.8rem; } .mf-bar { height:7px; border-radius:99px; background:var(--surface-2); border:1px solid var(--border); overflow:hidden; } -.mf-bar > i { display:block; height:100%; background:linear-gradient(90deg,var(--accent),var(--accent-2)); box-shadow:0 0 10px var(--glow-s); } +.mf-bar > i { display:block; height:100%; background:linear-gradient(90deg,var(--accent),var(--accent-2)); box-shadow:var(--g-bar); } /* ===== Buttons ===== */ -.mf-btn { display:inline-flex; align-items:center; justify-content:center; gap:.45rem; font-weight:800; font-size:.82rem; letter-spacing:.04em; border:0; border-radius:9px; padding:.6rem 1.1rem; cursor:pointer; text-decoration:none; background:var(--accent); color:var(--accent-ink); box-shadow:0 0 18px var(--glow-s); transition:all .18s; } +.mf-btn { display:inline-flex; align-items:center; justify-content:center; gap:.45rem; font-weight:800; font-size:.82rem; letter-spacing:.04em; border:0; border-radius:9px; padding:.6rem 1.1rem; cursor:pointer; text-decoration:none; background:var(--accent); color:var(--accent-ink); box-shadow:var(--g-strong); transition:all .18s; } .mf-btn:hover { filter:brightness(1.05); transform:translateY(-1px); } .mf-btn svg { width:15px; height:15px; } .mf-btn.outline { background:transparent; color:var(--accent); border:1px solid var(--border-accent); box-shadow:none; } @@ -165,7 +187,7 @@ a { color:var(--secondary); } .mf-input, .mf-select, input.mf-input, select.mf-select, textarea.mf-input { width:100%; padding:.65rem .8rem; border-radius:10px; border:1px solid var(--border); background:var(--surface-2); color:var(--text); font-size:.95rem; font-family:inherit; } -.mf-input:focus, .mf-select:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--glow-w); } +.mf-input:focus, .mf-select:focus { outline:none; border-color:var(--accent); box-shadow:var(--g-focus); } .mf-input::placeholder { color:var(--muted); } /* ===== Tabellen ===== */ @@ -179,7 +201,8 @@ a { color:var(--secondary); } /* ===== Standalone-Karte (Login/Auth, einzelne Inhaltsseiten) ===== */ .mf-center { display:flex; align-items:center; justify-content:center; min-height:100vh; padding:1.5rem; } -.mf-cardbox { width:100%; max-width:420px; background:var(--surface); border:1px solid var(--border-accent); border-radius:18px; padding:2rem; box-shadow:0 0 30px var(--glow-w); } +.mf-cardbox { width:100%; max-width:420px; background:var(--surface); border:1px solid var(--border-accent); border-radius:18px; padding:2rem; box-shadow:var(--g-strong); } +[data-theme="dark"] .mf-cardbox { box-shadow:0 0 30px var(--glow-w); } .mf-container { max-width:1000px; margin:0 auto; padding:1.5rem; } .mf-page-head { display:flex; align-items:center; gap:1rem; margin-bottom:1.4rem; } .mf-back { color:var(--accent); text-decoration:none; font-weight:700; font-size:.88rem; } @@ -199,7 +222,7 @@ a { color:var(--secondary); } .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 { 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:var(--g-amb); } .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; } @@ -220,7 +243,7 @@ body.mf-nav-open .mf-nav-backdrop { opacity:1; pointer-events: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); + box-shadow:var(--g-overlay); } body.mf-nav-open .mf-sidebar { transform:translateX(0); } .mf-burger { display:inline-flex; } @@ -235,4 +258,4 @@ body.mf-nav-open .mf-nav-backdrop { opacity:1; pointer-events:auto; } .mf-tab-nav { display:flex; flex-wrap:wrap; gap:.4rem; margin-bottom:1.4rem; } .mf-tab { padding:.55rem .95rem; border-radius:10px; border:1px solid var(--border); color:var(--muted); text-decoration:none; font-size:.85rem; font-weight:700; transition:all .18s ease; } .mf-tab:hover { color:var(--text); background:var(--surface-2); } -.mf-tab.active { color:var(--accent-ink); background:linear-gradient(100deg,var(--accent),var(--accent-2)); box-shadow:0 0 18px var(--glow-s); border-color:transparent; } +.mf-tab.active { color:var(--accent-ink); background:linear-gradient(100deg,var(--accent),var(--accent-2)); box-shadow:var(--g-strong); border-color:transparent; } diff --git a/src/main/resources/static/js/devlabgame.js b/src/main/resources/static/js/devlabgame.js index 6002732..3d6603d 100644 --- a/src/main/resources/static/js/devlabgame.js +++ b/src/main/resources/static/js/devlabgame.js @@ -80,7 +80,7 @@ if (last < tpl.length) code.appendChild(document.createTextNode(tpl.slice(last))); pre.appendChild(code); r.playfield.appendChild(pre); - const first = Object.values(inputs)[0]; if (first) first.focus(); + const first = Object.values(inputs)[0]; if (first) first.focus({ preventScroll: true }); return { collect: () => ({ gaps: Object.entries(inputs).map(([pos, i]) => ({ position: Number(pos), text: i.value })) }), lock: () => Object.values(inputs).forEach((i) => { i.disabled = true; }), @@ -230,7 +230,7 @@ input.addEventListener('input', preview); input.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); doCheck(false); } }); const wrap = document.createElement('div'); wrap.className = 'dlg-regex'; - wrap.append(input, list); r.playfield.appendChild(wrap); preview(); input.focus(); + wrap.append(input, list); r.playfield.appendChild(wrap); preview(); input.focus({ preventScroll: true }); return { collect: () => ({ pattern: input.value }), lock: () => { input.disabled = true; }, @@ -241,6 +241,7 @@ // ---------- Ablauf ---------- function renderTask() { + window.scrollTo(0, 0); const task = session.tasks[index]; cur = { task, attempts: 0, solved: false, revealed: false }; r.kind.textContent = `Aufgabe ${index + 1}`; diff --git a/src/main/resources/static/js/finalflux-check.js b/src/main/resources/static/js/finalflux-check.js deleted file mode 100644 index 750f8be..0000000 --- a/src/main/resources/static/js/finalflux-check.js +++ /dev/null @@ -1,29 +0,0 @@ -document.addEventListener('DOMContentLoaded', () => { - document.querySelectorAll('[data-fluxcheck]').forEach((check) => { - const button = check.querySelector('button'); - const result = check.querySelector('.fluxcheck-result'); - - if (!button || !result) { - return; - } - - button.addEventListener('click', () => { - const selected = check.querySelector('input[type="radio"]:checked'); - if (!selected) { - result.textContent = 'Wähle zuerst eine Antwort aus.'; - result.className = 'fluxcheck-result wrong'; - return; - } - - const expected = check.dataset.answer; - const explanation = check.dataset.explanation || ''; - const isCorrect = selected.value === expected; - - result.textContent = isCorrect - ? 'Richtig. ' + explanation - : 'Noch nicht. ' + explanation; - result.className = 'fluxcheck-result ' + (isCorrect ? 'correct' : 'wrong'); - }); - }); -}); - diff --git a/src/main/resources/static/js/finalflux-test.js b/src/main/resources/static/js/finalflux-test.js index ed76543..b67fbef 100644 --- a/src/main/resources/static/js/finalflux-test.js +++ b/src/main/resources/static/js/finalflux-test.js @@ -100,6 +100,7 @@ document.addEventListener('DOMContentLoaded', async () => { testRoot.hidden = true; elements.testResult.hidden = false; + elements.testResult.scrollIntoView({ block: 'start' }); elements.resultSummary.textContent = `Du hast ${score} von ${questions.length} Fragen richtig beantwortet.`; elements.categorySummary.innerHTML = ''; Array.from(categoryStats.entries()).forEach(([category, stats]) => { @@ -154,6 +155,7 @@ document.addEventListener('DOMContentLoaded', async () => { categoryStats.clear(); testRoot.hidden = false; elements.testResult.hidden = true; + testRoot.scrollIntoView({ block: 'start' }); renderQuestion(); }); diff --git a/src/main/resources/static/js/mindmatch.js b/src/main/resources/static/js/mindmatch.js index 05917b3..cf5ca38 100644 --- a/src/main/resources/static/js/mindmatch.js +++ b/src/main/resources/static/js/mindmatch.js @@ -330,6 +330,7 @@ } function newGame() { + window.scrollTo(0, 0); const slug = deckSelect.value; const deckPairs = decksBySlug.get(slug); diff --git a/src/main/resources/static/js/syntaxsaga.js b/src/main/resources/static/js/syntaxsaga.js index fd26d94..ae6c0e5 100644 --- a/src/main/resources/static/js/syntaxsaga.js +++ b/src/main/resources/static/js/syntaxsaga.js @@ -111,6 +111,7 @@ // ---------- Mission rendern ---------- function renderMission() { + window.scrollTo(0, 0); const mission = story.missions[missionIndex]; cur = { mission, attempts: {}, revealed: {}, solved: {}, scored: {} }; curGaps = {}; @@ -207,7 +208,7 @@ storyEl.appendChild(scene); updateHud(); const first = Object.values(curInputs)[0]; - if (first) first.focus(); + if (first) first.focus({ preventScroll: true }); } // ---------- Prüfen / Auflösen ---------- diff --git a/src/main/resources/static/js/techtacular.js b/src/main/resources/static/js/techtacular.js index 8e83bd1..0208386 100644 --- a/src/main/resources/static/js/techtacular.js +++ b/src/main/resources/static/js/techtacular.js @@ -263,6 +263,7 @@ } async function newGame() { + window.scrollTo(0, 0); stopTimer(); seconds = 0; score = 0; answeredCount = 0; current = null; jokerUsed = false; submitted.length = 0; diff --git a/src/main/resources/static/js/theme.js b/src/main/resources/static/js/theme.js index fa20a3d..771818b 100644 --- a/src/main/resources/static/js/theme.js +++ b/src/main/resources/static/js/theme.js @@ -3,8 +3,20 @@ Jeder Button mit Klasse .mf-theme wird automatisch zum Umschalter. */ (function () { var KEY = 'mf-theme'; - function read() { try { return localStorage.getItem(KEY) || 'dark'; } catch (e) { return 'dark'; } } - function apply(mode) { document.documentElement.setAttribute('data-theme', mode); } + function preferredTheme() { + return window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'; + } + function read() { try { return localStorage.getItem(KEY) || preferredTheme(); } catch (e) { return preferredTheme(); } } + function apply(mode) { + document.documentElement.setAttribute('data-theme', mode); + var meta = document.querySelector('meta[name="theme-color"]'); + if (!meta) { + meta = document.createElement('meta'); + meta.name = 'theme-color'; + document.head.appendChild(meta); + } + meta.content = mode === 'light' ? '#fbf6ec' : '#0a0806'; + } apply(read()); // früh -> kein Aufblitzen @@ -15,6 +27,10 @@ var isGameRoute = ['/mindmatch', '/techtacular', '/devlab', '/syntaxsaga', '/codeescape', '/bughunter', '/serviceclash', '/networker'] .some(function (route) { return path === route || path.indexOf(route + '/') === 0; }); if (!isGameRoute) document.documentElement.classList.add('sn-platform'); + if (isGameRoute && 'scrollRestoration' in history) { + history.scrollRestoration = 'manual'; + window.scrollTo(0, 0); + } // Globale, rein clientseitige Komfortfunktionen. FluxTimer bleibt auf öffentlichen // Seiten inaktiv, läuft bei aktivierter Einstellung aber über Seitennavigationen weiter. @@ -96,6 +112,10 @@ set: set, current: function () { return document.documentElement.getAttribute('data-theme'); } }; + window.MindfluxUX = { + toTop: function () { window.scrollTo(0, 0); }, + focusQuiet: function (el) { if (el) el.focus({ preventScroll: true }); } + }; // Mobile-Nav: Off-Canvas-Drawer für Seiten mit App-Sidebar (.mf-shell) function initMobileNav() { @@ -130,5 +150,12 @@ }); paint(); initMobileNav(); + // SPA-Spiele ersetzen ihre Ansicht per innerHTML. Nur echte Übergänge werden + // zurück an den Seitenanfang gesetzt; Antwort-/Zeilenfeedback bleibt stabil. + document.addEventListener('click', function (event) { + if (!isGameRoute) return; + var transition = event.target.closest('[data-start], #bhStart, #bhNext, #bhAgain, #again, #nwStart, #nwAgain, #scStart, #scNext, #scAgain'); + if (transition) window.setTimeout(function () { window.scrollTo(0, 0); }, 0); + }); }); })(); diff --git a/src/main/resources/templates/finalflux.html b/src/main/resources/templates/finalflux.html index d0c9112..d218029 100644 --- a/src/main/resources/templates/finalflux.html +++ b/src/main/resources/templates/finalflux.html @@ -4,10 +4,9 @@ FinalProtocol - SysnaptiK Neural - - + - +