diff --git a/HTMLCS.js b/HTMLCS.js index 708229e7..00574742 100755 --- a/HTMLCS.js +++ b/HTMLCS.js @@ -851,6 +851,82 @@ var HTMLCS = new function() return lum; } + /** + * Convert an rgba colour to rgb, by traversing the dom and mixing colors as needed. + * + * @param element - the element to compare the rgba color against. + * @param colour - the starting rgba color to check. + * @returns {Colour Object} + */ + this.rgbaBackgroundToRgb = function(colour, element) { + var parent = element.parentNode; + var original = this.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 = this.style(parent); + var parentColourStr = parentStyle.backgroundColor; + var parentColour = this.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 = this.mixColours(solidColour, backgrounds.pop()); + } + + return this.mixColours(solidColour, original); + } + + this.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. * @@ -871,7 +947,11 @@ var HTMLCS = new function() colour = { red: (matches[1] / 255), green: (matches[2] / 255), - blue: (matches[3] / 255) + blue: (matches[3] / 255), + alpha: 1 + } + if (matches[4]) { + colour.alpha = parseFloat(/^,\s*(.*)$/.exec(matches[4])[1]); } } else { // Hex digit format. @@ -886,7 +966,8 @@ var HTMLCS = new function() colour = { red: (parseInt(colour.substr(0, 2), 16) / 255), green: (parseInt(colour.substr(2, 2), 16) / 255), - blue: (parseInt(colour.substr(4, 2), 16) / 255) + blue: (parseInt(colour.substr(4, 2), 16) / 255), + alpha: 1 }; } diff --git a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js index 98685e92..6420e313 100644 --- a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js +++ b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js @@ -77,6 +77,9 @@ var HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3 = { if (isAbsolute === true) { code += '.Abs'; HTMLCS.addMessage(HTMLCS.WARNING, element, 'This element is absolutely positioned and the background color can not be determined. Ensure the contrast ratio between the text and all covered parts of the background are at least ' + required + ':1.', code); + } else if (bgColour && bgColour.indexOf('rgba') === 0) { + code += '.Alpha'; + HTMLCS.addMessage(HTMLCS.WARNING, element, 'This element\'s text is placed on a background that has an alpha transparency. Ensure the contrast ratio between the text and background color are at least ' + required + ':1.', code); } else if (hasBgImg === true) { code += '.BgImage'; HTMLCS.addMessage(HTMLCS.WARNING, element, 'This element\'s text is placed on a background image. Ensure the contrast ratio between the text and all covered parts of the image are at least ' + required + ':1.', code); diff --git a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3_Contrast.js b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3_Contrast.js index 51e71054..35f3dbd0 100644 --- a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3_Contrast.js +++ b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3_Contrast.js @@ -52,8 +52,8 @@ var HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = { var bgElement = node; var hasBgImg = false; var isAbsolute = false; - - if (style.backgroundImage !== 'none') { + + if (style.backgroundImage !== 'none') { hasBgImg = true; } @@ -86,6 +86,7 @@ var HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = { var parentStyle = HTMLCS.util.style(parent); var bgColour = parentStyle.backgroundColor; + var bgElement = parent; if (parentStyle.backgroundImage !== 'none') { hasBgImg = true; } @@ -96,12 +97,20 @@ var HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = { parent = parent.parentNode; }//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 background image, skip the contrast ratio checks, // and push a warning instead. failures.push({ element: node, - colour: style.color, + colour: foreColour, bgColour: undefined, value: undefined, required: reqRatio, @@ -117,6 +126,7 @@ var HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = { required: reqRatio, isAbsolute: true }); + continue; } else if ((bgColour === 'transparent') || (bgColour === 'rgba(0, 0, 0, 0)')) { // If the background colour is still transparent, this is probably // a fragment with which we cannot reliably make a statement about @@ -124,12 +134,12 @@ var HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = { continue; } - var contrastRatio = HTMLCS.util.contrastRatio(bgColour, style.color); + var contrastRatio = HTMLCS.util.contrastRatio(bgColour, foreColour); if (contrastRatio < reqRatio) { - var recommendation = this.recommendColour(bgColour, style.color, reqRatio); + var recommendation = this.recommendColour(bgColour, foreColour, reqRatio); failures.push({ element: node,