Skip to content

Instantly share code, notes, and snippets.

@darcher-
Last active June 6, 2024 19:35
Show Gist options
  • Save darcher-/9011ff00b6fa499a36ec924b5eaa62c3 to your computer and use it in GitHub Desktop.
Save darcher-/9011ff00b6fa499a36ec924b5eaa62c3 to your computer and use it in GitHub Desktop.
Accessibility contrast rating utility.
/**
* Defines a color scheme with a text color and a backdrop color.
* @type {Object}
* @property {string} textColor - The hexadecimal color value for the text.
* @property {string} backDropColor - The hexadecimal color value for the backdrop.
*/
const COLOR_SCHEME = {
textColor: "#000",
backDropColor: "#fff",
};
/**
* An object that contains information about the contrast ratio between a text color and a backdrop color, including whether the contrast meets WCAG AA and AAA standards.
* @type {Object}
* @property {string} textColor - The hexadecimal color value for the text.
* @property {string} backDropColor - The hexadecimal color value for the backdrop.
* @property {boolean} AA - Whether the contrast ratio meets the WCAG AA standard.
* @property {boolean} AAA - Whether the contrast ratio meets the WCAG AAA standard.
* @property {number} A11Y_WCAG_RATIO - The calculated contrast ratio between the text color and backdrop color.
*/
const RATIO_DATAUM = Object.freeze({
...COLOR_SCHEME,
AA: meetsWCAGContrastLevel(
COLOR_SCHEME.backDropColor,
COLOR_SCHEME.textColor,
'AA',
),
AAA: meetsWCAGContrastLevel(
COLOR_SCHEME.backDropColor,
COLOR_SCHEME.textColor,
'AAA'
),
A11Y_WCAG_RATIO: calculateContrastRatio(
COLOR_SCHEME.backDropColor,
COLOR_SCHEME.textColor,
),
});
console.log(RATIO_DATAUM);
//! Utilities
/**
* Converts a hexadecimal color code to an RGB color object.
* @param {string} hexadecimal - The hexadecimal color code to convert.
* @returns {Object} - An object with `r`, `g`, and `b` properties representing the RGB color values.
*/
function hexToRgb(hexadecimal) {
const formattedHexCode = hexadecimal.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,
(m, r, g, b) => r + r + g + g + b + b
);
const rgbChannelColors = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
formattedHexCode
);
return {
r: parseInt(rgbChannelColors[1], 16),
g: parseInt(rgbChannelColors[2], 16),
b: parseInt(rgbChannelColors[3], 16),
};
}
/**
* Calculates the relative luminance of a color channel value.
* @param {number} channel - The color channel value (0-255).
* @returns {number} - The relative luminance of the color channel.
*/
function calculateRelativeChannelLuminance(channel) {
return (channel /= 255) <= 0.03928 ? channel / 12.92 : Math.pow(
(channel + 0.055) / 1.055, 2.4
);
}
/**
* Calculates the relative luminance of an RGB color.
* @param {number} r - The red color channel value (0-255).
* @param {number} g - The green color channel value (0-255).
* @param {number} b - The blue color channel value (0-255).
* @returns {number} - The relative luminance of the RGB color.
*/
function relativeRgbLumLevel(r, g, b) {
const [red, green, blue] = [r, g, b].map(
calculateRelativeChannelLuminance
);
const G = 0.7152 * green;
const B = 0.0722 * blue;
const R = 0.2126 * red;
return (R + G + B);
}
/**
* Retrieves the RGB color channels for two hexadecimal color values.
* @param {string} set1 - The first hexadecimal color value.
* @param {string} set2 - The second hexadecimal color value.
* @returns {object} - An object containing the red, green, and blue color channels for both color values.
*/
function getRgbChannels(set1, set2) {
const { r: red1, g: green1, b: blue1 } = hexToRgb(set1);
const { r: red2, g: green2, b: blue2 } = hexToRgb(set2);
return {
green: [green1, green2],
blue: [blue1, blue2],
red: [red1, red2],
}
}
/**
* Calculates the range of vibrance between two sets of RGB color channels.
* @param {object} param - An object containing the red, green, and blue color channels for two color values.
* @param {number[]} param.g - The green color channel values for the two colors.
* @param {number[]} param.b - The blue color channel values for the two colors.
* @param {number[]} param.r - The red color channel values for the two colors.
* @returns {object} - An object containing the brighter and darker vibrance values.
*/
function rangeOfVibrance({ g, b, r }) {
const vibrance1 = relativeRgbLumLevel(r[0], g[0], b[0]);
const vibrance2 = relativeRgbLumLevel(r[1], g[1], b[1]);
return {
brighter: Math.max(vibrance1, vibrance2) + 0.05,
darker: Math.min(vibrance1, vibrance2) + 0.05,
}
}
/**
* Calculates the contrast ratio between two hexadecimal color values.
* @param {string} hex1 - The first hexadecimal color value.
* @param {string} hex2 - The second hexadecimal color value.
* @returns {number} - The contrast ratio between the two colors.
*/
function calculateContrastRatio(hex1, hex2) {
const { red, green, blue } = getRgbChannels(hex1, hex2);
const { brighter, darker } = rangeOfVibrance({
g: [...green],
b: [...blue],
r: [...red],
});
return (brighter / darker);
}
/**
* Checks if the contrast ratio between two hexadecimal color values meets the specified WCAG contrast level.
* @param {string} hex1 - The first hexadecimal color value.
* @param {string} hex2 - The second hexadecimal color value.
* @param {'AA'|'AAA'} level - The WCAG contrast level to check against ('AA' or 'AAA').
* @returns {boolean} - True if the contrast ratio meets the specified WCAG contrast level, false otherwise.
*/
function meetsWCAGContrastLevel(hex1, hex2, level) {
const contrastRatio = calculateContrastRatio(hex1, hex2);
const requiredRatio = level === 'AA' ? 4.5 : 7;
return contrastRatio >= requiredRatio;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment