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

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