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:
@@ -11,6 +11,48 @@ body {
|
|||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
color: #ffffff;
|
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 */
|
/* Background */
|
||||||
|
|||||||
@@ -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
|
// Update background image
|
||||||
async function updateBackground() {
|
async function updateBackground() {
|
||||||
try {
|
try {
|
||||||
@@ -226,11 +276,28 @@ async function updateBackground() {
|
|||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
backgroundElement.style.backgroundImage = `url('${data.image}')`;
|
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;
|
img.src = data.image;
|
||||||
} else if (data.color) {
|
} else if (data.color) {
|
||||||
backgroundElement.style.backgroundColor = data.color;
|
backgroundElement.style.backgroundColor = data.color;
|
||||||
backgroundElement.style.backgroundImage = 'none';
|
backgroundElement.style.backgroundImage = 'none';
|
||||||
|
// Assume solid colors are dark
|
||||||
|
document.body.classList.remove('light-bg');
|
||||||
|
document.body.classList.add('dark-bg');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user