From ced41148ef17f97377bad2dc2a75f5fc8e2305f8 Mon Sep 17 00:00:00 2001 From: Ludwig Mey Date: Mon, 16 Feb 2026 22:25:51 +1300 Subject: [PATCH] Add dynamic text color adjustment based on background brightness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatically adjusts text color for optimal readability: - Bright backgrounds: Text switches to dark color (#1a1a1a) - Dark backgrounds: Text remains white (#ffffff) - Uses luminance formula (0.299×R + 0.587×G + 0.114×B) for accurate brightness detection - Analyzes images using canvas to sample 100x100 pixels - Smooth color transitions (0.5s) between background changes - Adjusts text shadows for better contrast in each mode - Event time color adapts (#0066cc for light, #4a9eff for dark) Technical implementation: - Added calculateImageBrightness() function in app.js - Modified updateBackground() to analyze and apply appropriate CSS class - Added .light-bg and .dark-bg CSS classes for text color themes - Brightness threshold set at 140 (out of 255) Co-Authored-By: Claude Sonnet 4.5 --- static/css/style.css | 42 +++++++++++++++++++++++++++ static/js/app.js | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/static/css/style.css b/static/css/style.css index f9f1af1..dbfc730 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -11,6 +11,48 @@ body { width: 100vw; height: 100vh; color: #ffffff; + transition: color 0.5s ease-in-out; +} + +/* Dark text for bright backgrounds */ +body.light-bg { + color: #1a1a1a; +} + +body.light-bg .time, +body.light-bg .date, +body.light-bg .current-temp, +body.light-bg .weather-label, +body.light-bg .weather-icon, +body.light-bg .forecast-icon, +body.light-bg section h2, +body.light-bg .day-name, +body.light-bg .day-date, +body.light-bg .event-title, +body.light-bg .event-location, +body.light-bg .joke { + text-shadow: 1px 1px 3px rgba(255, 255, 255, 0.8); +} + +body.light-bg .event-time { + color: #0066cc; +} + +body.light-bg .event { + border-left-color: #0066cc; +} + +/* White text for dark backgrounds (default) */ +body.dark-bg { + color: #ffffff; +} + +body.dark-bg .event-time { + color: #4a9eff; +} + +body.dark-bg .event { + border-left-color: #4a9eff; } /* Background */ diff --git a/static/js/app.js b/static/js/app.js index 7299985..85b2bf1 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -212,6 +212,56 @@ async function updateCalendar() { } } +// Calculate brightness of an image +function calculateImageBrightness(imageSrc, callback) { + const img = new Image(); + img.crossOrigin = 'Anonymous'; + + img.onload = () => { + // Create a canvas to analyze the image + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + // Use a smaller size for faster processing + canvas.width = 100; + canvas.height = 100; + + // Draw the image scaled down + ctx.drawImage(img, 0, 0, 100, 100); + + // Get image data + const imageData = ctx.getImageData(0, 0, 100, 100); + const data = imageData.data; + + // Calculate average brightness + let totalBrightness = 0; + const pixelCount = data.length / 4; + + for (let i = 0; i < data.length; i += 4) { + const r = data[i]; + const g = data[i + 1]; + const b = data[i + 2]; + + // Calculate perceived brightness (using luminance formula) + const brightness = (0.299 * r + 0.587 * g + 0.114 * b); + totalBrightness += brightness; + } + + const avgBrightness = totalBrightness / pixelCount; + + // Return brightness value (0-255) + callback(avgBrightness); + }; + + img.onerror = () => { + console.error('Error loading image for brightness analysis'); + // Default to dark background if error + callback(100); + }; + + img.src = imageSrc; +} + // Update background image async function updateBackground() { try { @@ -226,11 +276,28 @@ async function updateBackground() { const img = new Image(); img.onload = () => { backgroundElement.style.backgroundImage = `url('${data.image}')`; + + // Analyze brightness and adjust text color + calculateImageBrightness(data.image, (brightness) => { + // Threshold: 128 is middle brightness + // If brightness > 140, use dark text (light background) + // If brightness <= 140, use white text (dark background) + if (brightness > 140) { + document.body.classList.remove('dark-bg'); + document.body.classList.add('light-bg'); + } else { + document.body.classList.remove('light-bg'); + document.body.classList.add('dark-bg'); + } + }); }; img.src = data.image; } else if (data.color) { backgroundElement.style.backgroundColor = data.color; backgroundElement.style.backgroundImage = 'none'; + // Assume solid colors are dark + document.body.classList.remove('light-bg'); + document.body.classList.add('dark-bg'); } } catch (error) {