Files
mindflux/src/main/resources/static/js/theme.js
T
AliceDarkest d5d4add293
mindflux-ci / build (push) Successful in 23s
Eliminate platform theme flash
2026-07-14 15:22:26 +02:00

116 lines
5.3 KiB
JavaScript
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.
/* Mindflux Theme – Dark/Light-Umschaltung, zentral.
Im <head> einbinden (ohne defer), dann setzt es das Theme vor dem Rendern (kein Flash).
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); }
apply(read()); // früh -> kein Aufblitzen
// Die Plattformklasse liegt vor dem ersten Rendern auf html. So ist die
// Neural-Hülle bereits aktiv, wenn die statisch importierten Styles zeichnen.
// Die eigenständigen Spielrouten bleiben bewusst ausgenommen.
var path = window.location.pathname;
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');
// Globale, rein clientseitige Komfortfunktionen. FluxTimer bleibt auf öffentlichen
// Seiten inaktiv, läuft bei aktivierter Einstellung aber über Seitennavigationen weiter.
if (!document.querySelector('link[data-fluxtimer]')) {
var timerCss = document.createElement('link');
timerCss.rel = 'stylesheet';
timerCss.href = '/css/fluxtimer.css?v=4';
timerCss.setAttribute('data-fluxtimer', '');
document.head.appendChild(timerCss);
}
if (!document.querySelector('script[data-fluxtimer]')) {
var timerScript = document.createElement('script');
timerScript.src = '/js/fluxtimer.js?v=2';
timerScript.defer = true;
timerScript.setAttribute('data-fluxtimer', '');
document.head.appendChild(timerScript);
}
if (!document.querySelector('link[data-accessibility]')) {
var accessibilityCss = document.createElement('link');
accessibilityCss.rel = 'stylesheet';
accessibilityCss.href = '/css/accessibility.css?v=4';
accessibilityCss.setAttribute('data-accessibility', '');
document.head.appendChild(accessibilityCss);
var accessibilityPlacementCss = document.createElement('link');
accessibilityPlacementCss.rel = 'stylesheet';
accessibilityPlacementCss.href = '/css/accessibility-placement.css?v=9';
accessibilityPlacementCss.setAttribute('data-accessibility', '');
document.head.appendChild(accessibilityPlacementCss);
}
if (!document.querySelector('script[data-accessibility]')) {
var accessibilityScript = document.createElement('script');
accessibilityScript.src = '/js/accessibility.js?v=8';
accessibilityScript.defer = true;
accessibilityScript.setAttribute('data-accessibility', '');
document.head.appendChild(accessibilityScript);
}
var SUN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>';
var MOON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>';
function paint() {
var dark = document.documentElement.getAttribute('data-theme') === 'dark';
document.querySelectorAll('.mf-theme').forEach(function (b) { b.innerHTML = dark ? SUN : MOON; });
}
function toggle() {
var next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
apply(next);
try { localStorage.setItem(KEY, next); } catch (e) {}
paint();
}
function set(mode) {
var next = mode === 'light' ? 'light' : 'dark';
apply(next);
try { localStorage.setItem(KEY, next); } catch (e) {}
paint();
}
window.MindfluxTheme = {
toggle: toggle,
apply: apply,
set: set,
current: function () { return document.documentElement.getAttribute('data-theme'); }
};
// 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 = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M3 12h18M3 18h18"/></svg>';
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');
b.setAttribute('aria-label', 'Theme umschalten');
b.addEventListener('click', toggle);
});
paint();
initMobileNav();
});
})();