It started at the intersection of two websites I was obsessed with:
A streamlined website seemed like the perfect vehicle to test drive that color change feature in context. The idea sort of died with my total lack of javascript know-how.
I used Chat GPT to make up for my “javascript gap,” and got to a working proof-of-concept relatively quickly.
I opted for SVG icons (shoutout to Phospher Icons) to populate the site since their color could be manipulated via the button.
The code itself is running through a few steps:
*At first I was using AA (4:1) but that lead to some seriously rough color combos. AAA contrast tended to yield more visually appealing pairs more often than not.
Here's the code that's making all this work. I'm sure there's ways to clean this up and optimize it, but for now, this is the best I've got.
<script>
function getRandomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function calculateContrastRatio(foregroundColor, backgroundColor) {
const getLuminance = (color) => {
const hex = color.slice(1);
const r = parseInt(hex.slice(0, 2, 16), 16) / 255;
const g = parseInt(hex.slice(2, 4, 16), 16) / 255;
const b = parseInt(hex.slice(4, 6, 16), 16) / 255;
const sRGB = [r, g, b].map((channel) =>
channel <= 0.03928 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4)
);
return sRGB[0] * 0.2126 + sRGB[1] * 0.7152 + sRGB[2] * 0.0722;
};
const calculateContrast = (foreground, background) => {
const L1 = getLuminance(foreground);
const L2 = getLuminance(background);
return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05);
};
return calculateContrast(foregroundColor, backgroundColor);
}
function generateADACompliantColors() {
let bgColor, elementColor;
do {
bgColor = getRandomColor();
elementColor = getRandomColor();
} while (
calculateContrastRatio(elementColor, bgColor) < 7 || // AAA for normal text
calculateContrastRatio(elementColor, bgColor) < 4.5 || // AA for large text
calculateContrastRatio(bgColor, elementColor) < 7 // Ensure AAA-level contrast for both combinations
);
// Update the background color
document.body.style.backgroundColor = bgColor;
// Update all on-page elements
const elements = document.querySelectorAll("body *");
for (let i = 0; i < elements.length; i++) {
elements[i].style.color = elementColor;
elements[i].style.backgroundColor = bgColor;
elements[i].style.borderColor = elementColor;
// Update the SVG fill color
const svgElement = document.querySelector(".svg-element");
if (svgElement) {
svgElement.style.setProperty('--svgFill', elementColor);
}
}
}
function setTextColorSameAsBackground() {
const elements = document.querySelectorAll(".text-same-color");
const bgColor = getComputedStyle(document.body).backgroundColor; // Get the background color
elements.forEach((element) => {
element.style.color = bgColor;
});
}
function changeFont() {
const fonts = ['Arial', 'Helvetica', 'Times New Roman', 'Courier New', 'Verdana', 'Georgia'];
const randomFont = fonts[Math.floor(Math.random() * fonts.length)];
document.body.style.fontFamily = randomFont;
}
//
document.addEventListener("DOMContentLoaded", function () {
const button = document.getElementById("generateColorsButton");
button.addEventListener("click", function () {
generateADACompliantColors();
setTextColorSameAsBackground(); // Call the function to set text color
});
});
document.addEventListener("DOMContentLoaded", function () {
const button = document.getElementById("generateColorsButtonNav");
button.addEventListener("click", function () {
generateADACompliantColors();
setTextColorSameAsBackground(); // Call the function to set text color
});
});
document.addEventListener("DOMContentLoaded", function () {
const button = document.getElementById("generateColorsButton2");
button.addEventListener("click", function () {
generateADACompliantColors();
setTextColorSameAsBackground(); // Call the function to set text color
});
});
document.addEventListener("DOMContentLoaded", function () {
const button = document.getElementById("generateColorsButtonNavMobile");
button.addEventListener("click", function () {
generateADACompliantColors();
setTextColorSameAsBackground(); // Call the function to set text color
});
});
document.addEventListener("DOMContentLoaded", function () {
const button = document.getElementById("changeButton");
button.addEventListener("click", function () {
generateADACompliantColors();
setTextColorSameAsBackground(); // Call the function to set text color
changeFont();
});
});
</script>