'use strict'; // --------------------------------------------------------------------------- // Contrast scheme // --------------------------------------------------------------------------- // Accent palette — ordered by preference. Algorithm picks the one with the // highest WCAG contrast ratio against the detected background luminance. const ACCENT_PALETTE = [ { base: '#e63946', hover: '#ff4d58' }, // red { base: '#ff9500', hover: '#ffaa33' }, // orange { base: '#f1c40f', hover: '#f9d439' }, // yellow { base: '#2ecc71', hover: '#4ee88a' }, // green { base: '#00b4d8', hover: '#33c7e5' }, // cyan { base: '#4361ee', hover: '#6d84f4' }, // blue { base: '#c77dff', hover: '#d89fff' }, // purple { base: '#ff6b9d', hover: '#ff8fb5' }, // pink { base: '#ffffff', hover: '#cccccc' }, // white (last resort) ]; function _linearise(c) { c /= 255; return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); } function _luminance(r, g, b) { return 0.2126 * _linearise(r) + 0.7152 * _linearise(g) + 0.0722 * _linearise(b); } function _contrast(l1, l2) { return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05); } function _hexRgb(hex) { return [parseInt(hex.slice(1,3),16), parseInt(hex.slice(3,5),16), parseInt(hex.slice(5,7),16)]; } function analyzeBackground(url) { return new Promise(resolve => { const img = new Image(); img.crossOrigin = 'anonymous'; img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = 64; canvas.height = 64; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, 64, 64); const data = ctx.getImageData(0, 0, 64, 64).data; let tR = 0, tG = 0, tB = 0, tBt601 = 0; const n = data.length / 4; for (let i = 0; i < data.length; i += 4) { tR += data[i]; tG += data[i+1]; tB += data[i+2]; tBt601 += 0.299*data[i] + 0.587*data[i+1] + 0.114*data[i+2]; } resolve({ bright: (tBt601 / n) > 127, bgLuminance: _luminance(tR/n, tG/n, tB/n), }); }; img.onerror = () => resolve({ bright: false, bgLuminance: 0 }); img.src = url; }); } function pickBestAccent(bgLuminance) { let best = ACCENT_PALETTE[0], bestRatio = 0; for (const entry of ACCENT_PALETTE) { const [r, g, b] = _hexRgb(entry.base); const ratio = _contrast(bgLuminance, _luminance(r, g, b)); if (ratio > bestRatio) { bestRatio = ratio; best = entry; } } return best; } function applyAccent(entry) { const root = document.documentElement; root.style.setProperty('--accent', entry.base); root.style.setProperty('--accent-hover', entry.hover); } // Light and dark: the switch is in base.html (every page has it). The reader's // "Wie die App" follows it. window.addEventListener('diora-theme', () => { if (currentBookId) applyReaderSettings(!!currentPdfDoc); }); function setScheme(bright) { document.body.classList.toggle('bright-bg', bright); const btn = document.getElementById('contrast-toggle'); if (btn) btn.style.opacity = bright ? '1' : '0.5'; } function toggleContrast() { setScheme(!document.body.classList.contains('bright-bg')); }