Skip to content

Commit

Permalink
Issue warnings for alpha in contrast checking.
Browse files Browse the repository at this point in the history
Three complications with calculating the "flattened" (non-alpha) representation of font color and background color are:
* determining what is actually behind the text - elements may be positioned with css such that it's very hard to determine what element is the background of some or all of the text
* the css opacity element may introduce further aspects of transparency
* transparent background can reveal images where contrast cannot be calculated
  • Loading branch information
phillbaker committed Jul 4, 2018
1 parent 5f45d32 commit 2f9fd0c
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 104 deletions.
94 changes: 13 additions & 81 deletions HTMLCS.Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,86 +534,9 @@ _global.HTMLCS.util = function() {
}

/**
* Convert an rgba colour to rgb, by traversing the dom and mixing colors as needed.
* Convert a colour string to a structure with red/green/blue/alpha elements.
*
* @param element - the element to compare the rgba color against.
* @param colour - the starting rgba color to check.
* @returns {Colour Object}
*/
self.rgbaBackgroundToRgb = function(colour, element) {
var parent = element.parentNode;
var original = self.colourStrToRGB(colour);
var backgrounds = [];
var solidFound = false;

if (original.alpha == 1) {
//Return early if it is already solid.
return original;
}

//Find all the background with transparancy until we get to a solid colour
while (solidFound == false) {
if ((!parent) || (!parent.ownerDocument)) {
//No parent was found, assume a solid white background.
backgrounds.push({
red: 1,
green: 1,
blue: 1,
alpha: 1
});
break;
}

var parentStyle = self.style(parent);
var parentColourStr = parentStyle.backgroundColor;
var parentColour = self.colourStrToRGB(parentColourStr);

if ((parentColourStr === 'transparent') || (parentColourStr === 'rgba(0, 0, 0, 0)')) {
//Skip totally transparent parents until we find a solid color.
parent = parent.parentNode;
continue;
}

backgrounds.push(parentColour);

if (parentColour.alpha == 1) {
solidFound = true;
}

parent = parent.parentNode;
}

//Now we need to start with the solid color that we found, and work our way up to the original color.
var solidColour = backgrounds.pop();
while (backgrounds.length) {
solidColour = self.mixColours(solidColour, backgrounds.pop());
}

return self.mixColours(solidColour, original);
};

self.mixColours = function(bg, fg) {
//Convert colors to int values for mixing.
bg.red = Math.round(bg.red*255);
bg.green = Math.round(bg.green*255);
bg.blue = Math.round(bg.blue*255);
fg.red = Math.round(fg.red*255);
fg.green = Math.round(fg.green*255);
fg.blue = Math.round(fg.blue*255);

return {
red: Math.round(fg.alpha * fg.red + (1 - fg.alpha) * bg.red) / 255,
green: Math.round(fg.alpha * fg.green + (1 - fg.alpha) * bg.green) / 255,
blue: Math.round(fg.alpha * fg.blue + (1 - fg.alpha) * bg.blue) / 255,
alpha: bg.alpha
}
}

/**
* Convert a colour string to a structure with red/green/blue elements.
*
* Supports rgb() and hex colours (3 or 6 hex digits, optional "#").
* rgba() also supported but the alpha channel is currently ignored.
* Supports rgb() and hex colours (3, 4, 6 or 8 hex digits, optional "#").
* Each red/green/blue element is in the range [0.0, 1.0].
*
* @param {String} colour The colour to convert.
Expand Down Expand Up @@ -645,11 +568,20 @@ _global.HTMLCS.util = function() {
colour = colour.replace(/^(.)(.)(.)$/, '$1$1$2$2$3$3');
}

if (colour.length === 4) {
colour = colour.replace(/^(.)(.)(.)(.)$/, '$1$1$2$2$3$3$4$4');
}

var alpha = 1; // Default if alpha is not specified
if (colour.length === 8) {
alpha = parseInt(colour.substr(6, 2), 16) / 255;
}

colour = {
red: (parseInt(colour.substr(0, 2), 16) / 255),
gsreen: (parseInt(colour.substr(2, 2), 16) / 255),
green: (parseInt(colour.substr(2, 2), 16) / 255),
blue: (parseInt(colour.substr(4, 2), 16) / 255),
alpha: 1
alpha: alpha,
};
}

Expand Down
5 changes: 0 additions & 5 deletions Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,6 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_3_1_3_1 = {
testLabelsOnInputs: function(element, top, muteErrors)
{
var nodeName = element.nodeName.toLowerCase();
var style = HTMLCS.util.style(element);
var inputType = nodeName;
if (inputType === 'input') {
if (element.hasAttribute('type') === true) {
Expand Down Expand Up @@ -231,10 +230,6 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_3_1_3_1 = {
if (element.getAttribute('hidden') !== null) {
needsLabel = false;
}

if ('none' === style.display) {
needsLabel = false;
}

// Find an explicit label.
var explicitLabel = element.ownerDocument.querySelector('label[for="' + element.id + '"]');
Expand Down
36 changes: 25 additions & 11 deletions Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3_Contrast.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = {
var toProcess = [];
var body = top.getElementsByTagName('body');
if (body.length) {
//SVG objects will not have a body element. Don't check them.
// SVG objects will not have a body element. Don't check them.
var toProcess = [body[0]];
}
} else {
Expand Down Expand Up @@ -99,8 +99,8 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = {
isAbsolute = true;
}

//Search for the smooth scrolling willChange: 'transform' background hack
//See http://fourkitchens.com/blog/article/fix-scrolling-performance-css-will-change-property
// Search for the smooth scrolling willChange: 'transform' background hack
// See http://fourkitchens.com/blog/article/fix-scrolling-performance-css-will-change-property
var beforeStyle = HTMLCS.util.style(parent, ':before');
if (
beforeStyle
Expand All @@ -120,14 +120,28 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = {
}//end while

if (bgColour && bgColour.indexOf('rgba') === 0) {
bgColour = HTMLCS.util.RGBtoColourStr(HTMLCS.util.rgbaBackgroundToRgb(bgColour, bgElement));
}

if (foreColour && foreColour.indexOf('rgba') === 0) {
foreColour = HTMLCS.util.RGBtoColourStr(HTMLCS.util.rgbaBackgroundToRgb(foreColour, node));
}

if (hasBgImg === true) {
// If we have a rgba background color, skip the contrast ratio checks,
// and push a warning instead.
failures.push({
element: node,
colour: foreColour,
bgColour: bgColour,
value: undefined,
required: reqRatio
});
continue;
} else if (foreColour && foreColour.indexOf('rgba') === 0) {
// If we have a rgba fore colour, skip the contrast ratio checks,
// and push a warning instead.
failures.push({
element: node,
colour: foreColour,
bgColour: foreColour,
value: undefined,
required: reqRatio
});
continue;
} else if (hasBgImg === true) {
// If we have a background image, skip the contrast ratio checks,
// and push a warning instead.
failures.push({
Expand Down
7 changes: 0 additions & 7 deletions Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,6 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle4_Guideline4_1_4_1_2 = {
var element = elements[el];
var nodeName = element.nodeName.toLowerCase();
var msgSubCode = element.nodeName.substr(0, 1).toUpperCase() + element.nodeName.substr(1).toLowerCase();

var style = HTMLCS.util.style(element);
if ('none' === style.display) {
//Element is hidden, so no name is required
continue;
}

if (nodeName === 'input') {
if (element.hasAttribute('type') === false) {
// If no type attribute, default to text.
Expand Down
2 changes: 2 additions & 0 deletions Tests/WCAG2/1_4_3_Contrast.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

<p>Sample: <span id="issue155" style="color: #7c7cff; background-color: #ffffff; font-size: 14pt; font-weight: bold;">I am 14pt bold text and should pass</span></p>

<p>Warning: <span id="pr134" style="color: #7c7cff; background-color: #0000000C; font-size: 14pt; font-weight: bold;">I am 14pt bold text on a transparent background and should generate a warning</span></p>

</body>
</html>

Expand Down

0 comments on commit 2f9fd0c

Please sign in to comment.