Add dynamic text color adjustment based on background brightness

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 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 22:25:51 +13:00
parent 11adc10c34
commit ced41148ef
2 changed files with 109 additions and 0 deletions

View File

@@ -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 */

View File

@@ -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) {