UNPKG

contra-color

Version:

For a given color, find a contrasting color (not optimal).

107 lines (66 loc) 5.23 kB
# contra-color For a given color, find a contrasting color (not optimal). [Try it NOW!](https://canbax.github.io/contra-color/) <p align="center"> <img src="docs/demo.gif" alt="Sample Screen Shot"> </p> ## Usage ### Use [npm](https://www.npmjs.com/) Run command `npm i contra-color` ### import ESM module `import { getContrastingColor, getContrast } from 'contra-color';` ### import CommonJS module ``` const contra = require("contra-color"); console.log(contra.getContrastingColor("#0f0f0f")); ``` ### import UMD module Add `<script src="https://unpkg.com/contra-color/dist/contra-color.umd.js"></script>` to your HTML file. Then you can call API functions like `contraColor.getContrastingColor('#f0f0f0')` ## API ### `function getContrastingColor(c: string, isLinearLuminance?: boolean, contrastDiff?: number): IContraColor` - get a contrasting color for a given color - If `isLinearLuminance` is true, it will use the linear luminance formula. Otherwise, it will use power curve formula. - Set `contrastDiff` as 0 to find color with maximum contrast. If you pass a number in range (0,20], it will find a color with a the smallest contrast that is greater then `contrastDiff` ### `function getContrast(c1: string, c2: string, isLinearLuminance?: boolean): number` - Find contrast of two colors. - If you pass isLinearLuminance, it will calculate luminance with power curve formula. ## Problem What is the best font color for a certain background color? AFAIK, this question doesn't have a scientifically proven answer for now (29 October 2021). ## Background [World Wide Web Consortium (W3C)](https://en.wikipedia.org/wiki/World_Wide_Web_Consortium) publishes some guidelines like [Web Content Accessibility Guidelines (WCAG)](https://developer.mozilla.org/en-US/docs/Glossary/WCAG). According to these, [contrast ratio](https://www.w3.org/TR/WCAG20/#contrast-ratiodef) is defined as $$(L_1 + 0.05) / (L_2 + 0.05)$$ where $L_1$ is the relative luminance of the lighter of the colors, and $L_2$ is the relative luminance of the darker of the colors. [ Mozilla Developer Network (MDN)](https://developer.mozilla.org/en-US/) provides explanations about [calculating relative luminance](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance#calculating_relative_luminance). In the below codes, `luminance` function defines the relative luminance. If you give RBG values which are in range [0,255], the function will return a `luminance` value. ``` function luminance(R: number, G: number, B: number) { const r = sRGBtoLin(R); const g = sRGBtoLin(G); const b = sRGBtoLin(B); return r * 0.2126 + g * 0.7152 + b * 0.0722; } function sRGBtoLin(colorChannel: number) { colorChannel = colorChannel / 255; if (colorChannel <= 0.04045) { return colorChannel / 12.92; } else { return Math.pow((colorChannel + 0.055) / 1.055, 2.4); } } ``` According to the above definitions of _contrast ratio_ and _relative luminance_, the _contrast ratio_ can be 1 at minimum, 21 at maximum. ### Human Perception of Luminance According to [MDN](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance#perceptual_lightness), human perception is roughly a power curve. For this reason, we also let the user use non-linear luminance values. ### Too High Contrast Ratio According to [some reseearch](https://www.anec.eu/images/Publications/technical-studies/ANEC-final-report-1503-1700-Lenoir-et-al.pdf) too high contrast difference is not that good. > Among the contrast revisions, provide guidance on excessive contrast — too high of a contrast causes reading problems and eye strain as well. source: https://github.com/w3c/wcag/issues/695#issuecomment-483077339 ## Solution ### Brute Force Brute force search for finding the best contrasting color needs three `for` loops for each color channel. This means 256*256*256 = 16777216 lines of execution. This takes time!. ### Greedy Search We make a greedy search. We iterate over each channel just once. We start from the most important channel to the least important channel. The most important channel is **green** since its coefficient is the largest _0.7152_ in the equation, the least important channel is **blue** since its coefficient is the smallest _0.0722_ in the equation. Since greedy search does not give an optimal solution, we also start from different colors like black, white and, a random color. ### Limiting Contrast Difference To limit the contrast ratio, we start from the current color and then we diverge. As mentioned before too high contrast might not be ideal for readability. ## Comments and Discussion - The algorithm might not give always the color with maximum contrast. - If you specify a `contrastDiff` again it might not give a color with a high contrast. - There are some discussions regarding [Contrast Ratio Math](https://github.com/w3c/wcag/issues/695). So even the formulas accepted by W3C are questionable and can be changed in the future. - For me, it is very interesting that this is an active research question.