From 2f9fd0c35f6fb74ba0d4e8336048217611b3d347 Mon Sep 17 00:00:00 2001 From: Phillip Baker Date: Tue, 3 Jul 2018 21:00:16 -0400 Subject: [PATCH] Issue warnings for alpha in contrast checking. 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 --- HTMLCS.Util.js | 94 +++---------------- .../Sniffs/Principle1/Guideline1_3/1_3_1.js | 5 - .../Principle1/Guideline1_4/1_4_3_Contrast.js | 36 ++++--- .../Sniffs/Principle4/Guideline4_1/4_1_2.js | 7 -- Tests/WCAG2/1_4_3_Contrast.html | 2 + 5 files changed, 40 insertions(+), 104 deletions(-) diff --git a/HTMLCS.Util.js b/HTMLCS.Util.js index 2dc81802..d698614e 100644 --- a/HTMLCS.Util.js +++ b/HTMLCS.Util.js @@ -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. @@ -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, }; } diff --git a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js index 04165761..d368f954 100755 --- a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js +++ b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js @@ -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) { @@ -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 + '"]'); 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 e562bd23..42383674 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 @@ -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 { @@ -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 @@ -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({ diff --git a/Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js b/Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js index 8619b15b..43486904 100755 --- a/Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js +++ b/Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js @@ -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. diff --git a/Tests/WCAG2/1_4_3_Contrast.html b/Tests/WCAG2/1_4_3_Contrast.html index fae3759a..46cf3da4 100644 --- a/Tests/WCAG2/1_4_3_Contrast.html +++ b/Tests/WCAG2/1_4_3_Contrast.html @@ -15,6 +15,8 @@

Sample: I am 14pt bold text and should pass

+

Warning: I am 14pt bold text on a transparent background and should generate a warning

+