/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * A list of minimum contrast ratio's per WCAG 2.0 conformance level. * Please refer to section 1.4.3 of the WCAG 2.0 spec at http://www.w3.org/TR/WCAG20/. * Simply put: * A = Large text only. * AA = Regular sized text or large text in enhanced contrast mode. * AAA = Regular sized text in enhanced contrast mode. * * @type {Object} */ const CONTRAST_RATIO_LEVELS = { A: 3, AA: 4.5, AAA: 7, }; /** * For text legibility on any background color, you need to determine which text * color - black or white - will yield the highest contrast ratio. * Since you're always comparing `contrastRatio(bgcolor, black) > * contrastRatio(bgcolor, white) ? : `, we can greatly * simplify the calculation to the following constant. * * @type {Number} */ const CONTRAST_BRIGHTTEXT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05; /** * Color class, which describes a color. * In the future, this object may be extended to allow for conversions between * different color formats and notations, support transparency. * * @param {Number} r Red color component * @param {Number} g Green color component * @param {Number} b Blue color component */ export class Color { constructor(r, g, b) { this.r = r; this.g = g; this.b = b; } /** * Formula from W3C's WCAG 2.0 spec's relative luminance, section 1.4.1, * http://www.w3.org/TR/WCAG20/. * * @return {Number} Relative luminance, represented as number between 0 and 1. */ get relativeLuminance() { let colorArr = [this.r, this.g, this.b].map(color => { color = parseInt(color, 10); if (color <= 10) { return color / 255 / 12.92; } return Math.pow((color / 255 + 0.055) / 1.055, 2.4); }); return colorArr[0] * 0.2126 + colorArr[1] * 0.7152 + colorArr[2] * 0.0722; } /** * @return {Boolean} TRUE if you need to use a bright color (e.g. 'white'), when * this color is set as the background. */ get useBrightText() { return this.relativeLuminance <= CONTRAST_BRIGHTTEXT_THRESHOLD; } /** * Get the contrast ratio between the current color and a second other color. * A common use case is to express the difference between a foreground and a * background color in numbers. * Formula from W3C's WCAG 2.0 spec's contrast ratio, section 1.4.1, * http://www.w3.org/TR/WCAG20/. * * @param {Color} otherColor Color instance to calculate the contrast with * @return {Number} Contrast ratios can range from 1 to 21, commonly written * as 1:1 to 21:1. */ contrastRatio(otherColor) { if (!(otherColor instanceof Color)) { throw new TypeError("The first argument should be an instance of Color"); } let luminance = this.relativeLuminance; let otherLuminance = otherColor.relativeLuminance; return ( (Math.max(luminance, otherLuminance) + 0.05) / (Math.min(luminance, otherLuminance) + 0.05) ); } /** * Method to check if the contrast ratio between two colors is high enough to * be discernable. * * @param {Color} otherColor Color instance to calculate the contrast with * @param {String} [level] WCAG conformance level that maps to the minimum * required contrast ratio. Defaults to 'AA' * @return {Boolean} */ isContrastRatioAcceptable(otherColor, level = "AA") { return this.contrastRatio(otherColor) > CONTRAST_RATIO_LEVELS[level]; } }