Reputation: 3806
I'm trying to find the correct matching contrast color for a given background color that will pass WCAG AA guidelines (text is smaller 14px).
The algorithm i have seen is..
function getContrastYIQ(hexcolor){
var r = parseInt(hexcolor.substr(1,2),16);
var g = parseInt(hexcolor.substr(3,2),16);
var b = parseInt(hexcolor.substr(5,2),16);
var yiq = ((r*299)+(g*587)+(b*114))/1000;
return (yiq >= 128) ? 'dark-color' : 'light-color'; }
Ref: http://optional.is/required/2011/01/12/maximum-color-contrast/
However, for these color some colors like #e8540a and #ff4249 it returns light color when this does not pass color contrast guidelines.
Is there another algorithm/function in JS i could use that would return a valid contrast color that would pass these guidelines instead?
http://squizlabs.github.io/HTML_CodeSniffer/ seems to have the right algorithm in it... But couldnt seem to find the relevant code to convert to JS. Anyone have any suggestions?
Upvotes: 4
Views: 4777
Reputation: 11
The "a lot more complicated" form on w3.org is for converting from sRGB
colorspace into standard RGB
The formula for RGB
is simple.
for each color, find its "relative brightness"
L = 0.2126 * R + 0.7152 * G + 0.0722 * B
calculate the ratio:
(higherRelBrightness + 0.05) / (lowerRelBrightness + 0.05)
which will be from 1.0 (identical brightness) to 21.0 (#000 to #fff)
Upvotes: 0
Reputation: 397
There are a few concepts that need to be clarified to answer your question.
#000080
) background you can put white (#000000
) text (contrast ratio 16.0), but it also works with yellow (#ffff00
, contrast ratio 14.9), pink (#ffc0cb
, contrast ratio 10.4), etc. See these examples in action:<p style="background:navy; color:white">white on navy</p>
<p style="background:navy; color:yellow">yellow on navy</p>
<p style="background:navy; color:pink">pink on navy</p>
The algorithm you refer to does not match WCAG 2.0 normative definitions for relative luminance and contrast ratio. You should stick to the latter algorithms. Others may provide approximations that sometimes work and sometimes don't (or they may even provide wrong results no matter what). That said, the WCAG 2.0 recommended algorithm is neither specially difficult to be implemented nor computationally intensive, thus it is not worth to try and find an alternative, which may even underperform.
WCAG 2.0 contrast ratio algorithm solves a decision problem, i.e. is the contrast between these two colours enough (for most users to distinguish between each other)? However, you seem more interested in solving a (constrained) optimization problem, i.e. which (of some preset text colours) has most contrast with this background colour? You should have in mind these are two related but different problems -and the difference is not to be dismissed. We will come back to this later.
Regarding squizlabs HTML code sniffer, you stated that you couldn't find the relevant code. Well, their implementation of the WCAG 2.0 contrast ratio algorithm can be easily found searching for 'contrast' on their github repo: see the contrastRatio
, relativeLum
and colourStrToRGB
functions. Interestingly, they also provide a recommendation algorithm that provides a suggestion to shift either the foreground or the background colour (or both) so as to abide by a specified contrast ratio, while keeping the changes to the minimum necessary.
Ok, that said, once we have the scope of our problem more clear, let's try to answer the question(s):
Q: How do I know whether black or white text is more appropriate on a specific background in terms of contrast?
A: You can simply compute the colour contrast between your background colour and black, on the one hand; and then between your background colour and white, on the other; and then keep the one which achieves the best contrast ratio. In pseudo-js:
function(yourColor) {
var black = "#000000";
var white = "#ffffff";
return contrastRatio(yourColor, black) > contrastRatio(yourColor, white) ? 'black' : 'white';
}
You can use the implementation of the contrast ratio provided by squizlabs or your own one (it's not that hard if you follow WCAG 2.0 description).
Note that, in any case, the contrastRatio
function always provides a value greater than or equal to 1.0, because it always provides the ratio of the brighter colour to the darker one. In consequence, the function is commutative (you do not need to pay attention to the order of the arguments).
Note also that squizlabs recommendColour
function is of no use to you, as they provide the closest recommended colour to match the contrast rule, which, as a general rule, needs not be either black or white.
Q: Why sometimes this or that algorithm recommends using a black (or a white) text despite that colour does not meet the contrast ratio rule?
A: Because sometimes, for some background colours and contrast levels, there is no text colour at all that can meet the contrast ratio rule.
Do you remember the difference between the optimization and the decision? One thing is to say "black is the text colour that provides the maximum contrast with your background colour", and another, different thing is to say "black text provides a contrast greater than 7 to 1 with your background colour".
Sometimes, even the maximum contrast achievable will not be sufficient to be contrasting enough to achieve the desired level. That means that you would need to (a) choose a different background colour, or (b) resort to other styles that allow less strict contrast ratios (i.e. large text), or (c) renouce to comply with WCAG 2.0 at the level you had chosen.
Let's illustrate that with a few examples:
+-----------------------+-----------+---------------+---------+--------+ | Case | Target C | Text Lum | white? | black? | +-----------------------+-----------+---------------+---------+--------+ | AA large text | 3:1 | 0.000 - 0.100 | OK | NO | | AA large text | 3:1 | 0.100 - 0.300 | OK | OK | | AA large text | 3:1 | 0.300 - 1.000 | NO | OK | | AA small, AAA large | 4.5:1 | 0.000 - 0.175 | OK | NO | | AA small, AAA large | 4.5:1 | 0.175 - 0.183 | OK | OK | | AA small, AAA large | 4.5:1 | 0.183 - 1.000 | NO | OK | | AAA small text | 7:1 | 0.000 - 0.100 | OK | NO | | AAA small text | 7:1 | 0.100 - 0.300 | NO | NO | | AAA small text | 7:1 | 0.300 - 1.000 | OK | OK | +-----------------------+-----------+---------------+---------+--------+
As you may note, there is a range of colours, whose luminance is between 0.1 and 0.3, which have a contrast ratio between 3:1 and 7:1 with both black and white. This means that these background colours have sufficient contrast with both black and white to use them with either large white or large black text and comply with WCAG SC 1.4.3 (AA level) (although only one of them will be the best choice in each case: white for background luminance between 0.100 and 0.179, and black between 0.179 and 0.300). But, on the other hand, these same colours do not have sufficient contrast to be used with any small text, no matter its colour, while still complying with WCAG SC 1.4.6 (AAA level). Finally, regarding small text and WCAG SC 1.4.3 (AA level), there is always one colour (either black or white) that provides sufficient contrast to be used (there is even a small range where both apply).
The initial algorithm by optional.is you refer to is inspired by an informative draft suggestion by WCAG 1.0. However, note this is unofficial (as it never advanced from the draft status), obsolete (as it has long been superseded by WCAG 2.0), and the implementation is potentially incorrect (as the whole algorithm needs to take into account two variables: lightness difference and colour difference, while the implementation you refer to only considers the former).
I have included an inaccessible table, this is due to SO markdown limitations, which does not allow arbitrary HTML, apologies for that; anyway I do not think it is necessary to understand the answer.
Upvotes: 7
Reputation: 18807
Your algorithm does not match the official WCAG formula.
You can see the WCAG formula to get the contrast ratio on the following page: http://www.w3.org/TR/WCAG20-TECHS/G17.html which is a lot more complicated.
I don't know where they found this...
Upvotes: 1