Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / toolkit / modules / Color.sys.mjs
blobd00902ef6ba00245f2572632a10bb3fbba0848cb
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 /**
6  * A list of minimum contrast ratio's per WCAG 2.0 conformance level.
7  * Please refer to section 1.4.3 of the WCAG 2.0 spec at http://www.w3.org/TR/WCAG20/.
8  * Simply put:
9  * A = Large text only.
10  * AA = Regular sized text or large text in enhanced contrast mode.
11  * AAA = Regular sized text in enhanced contrast mode.
12  *
13  * @type {Object}
14  */
15 const CONTRAST_RATIO_LEVELS = {
16   A: 3,
17   AA: 4.5,
18   AAA: 7,
21 /**
22  * For text legibility on any background color, you need to determine which text
23  * color - black or white - will yield the highest contrast ratio.
24  * Since you're always comparing `contrastRatio(bgcolor, black) >
25  * contrastRatio(bgcolor, white) ? <use black> : <use white>`, we can greatly
26  * simplify the calculation to the following constant.
27  *
28  * @type {Number}
29  */
30 const CONTRAST_BRIGHTTEXT_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05;
32 /**
33  * Color class, which describes a color.
34  * In the future, this object may be extended to allow for conversions between
35  * different color formats and notations, support transparency.
36  *
37  * @param {Number} r Red color component
38  * @param {Number} g Green color component
39  * @param {Number} b Blue color component
40  */
41 export class Color {
42   constructor(r, g, b) {
43     this.r = r;
44     this.g = g;
45     this.b = b;
46   }
48   /**
49    * Formula from W3C's WCAG 2.0 spec's relative luminance, section 1.4.1,
50    * http://www.w3.org/TR/WCAG20/.
51    *
52    * @return {Number} Relative luminance, represented as number between 0 and 1.
53    */
54   get relativeLuminance() {
55     let colorArr = [this.r, this.g, this.b].map(color => {
56       color = parseInt(color, 10);
57       if (color <= 10) {
58         return color / 255 / 12.92;
59       }
60       return Math.pow((color / 255 + 0.055) / 1.055, 2.4);
61     });
62     return colorArr[0] * 0.2126 + colorArr[1] * 0.7152 + colorArr[2] * 0.0722;
63   }
65   /**
66    * @return {Boolean} TRUE if you need to use a bright color (e.g. 'white'), when
67    *                   this color is set as the background.
68    */
69   get useBrightText() {
70     return this.relativeLuminance <= CONTRAST_BRIGHTTEXT_THRESHOLD;
71   }
73   /**
74    * Get the contrast ratio between the current color and a second other color.
75    * A common use case is to express the difference between a foreground and a
76    * background color in numbers.
77    * Formula from W3C's WCAG 2.0 spec's contrast ratio, section 1.4.1,
78    * http://www.w3.org/TR/WCAG20/.
79    *
80    * @param  {Color}  otherColor Color instance to calculate the contrast with
81    * @return {Number} Contrast ratios can range from 1 to 21, commonly written
82    *                  as 1:1 to 21:1.
83    */
84   contrastRatio(otherColor) {
85     if (!(otherColor instanceof Color)) {
86       throw new TypeError("The first argument should be an instance of Color");
87     }
89     let luminance = this.relativeLuminance;
90     let otherLuminance = otherColor.relativeLuminance;
91     return (
92       (Math.max(luminance, otherLuminance) + 0.05) /
93       (Math.min(luminance, otherLuminance) + 0.05)
94     );
95   }
97   /**
98    * Method to check if the contrast ratio between two colors is high enough to
99    * be discernable.
100    *
101    * @param  {Color}  otherColor Color instance to calculate the contrast with
102    * @param  {String} [level]    WCAG conformance level that maps to the minimum
103    *                             required contrast ratio. Defaults to 'AA'
104    * @return {Boolean}
105    */
106   isContrastRatioAcceptable(otherColor, level = "AA") {
107     return this.contrastRatio(otherColor) > CONTRAST_RATIO_LEVELS[level];
108   }