From 5f45d3273e6f2f353c1489fb0368d686bfc81f67 Mon Sep 17 00:00:00 2001 From: Michael Fairchild Date: Fri, 27 Jun 2014 11:44:07 -0500 Subject: [PATCH 1/2] Add methods to convert an rgba background color to rgb --- HTMLCS.Util.js | 98 +++++++++++++++++-- .../Sniffs/Principle1/Guideline1_3/1_3_1.js | 5 + .../Principle1/Guideline1_4/1_4_3_Contrast.js | 46 +++++++-- .../Sniffs/Principle4/Guideline4_1/4_1_2.js | 7 ++ 4 files changed, 141 insertions(+), 15 deletions(-) diff --git a/HTMLCS.Util.js b/HTMLCS.Util.js index ab10fdde..2dc81802 100644 --- a/HTMLCS.Util.js +++ b/HTMLCS.Util.js @@ -191,14 +191,15 @@ _global.HTMLCS.util = function() { * * @returns {Object} */ - self.style = function(element) { + self.style = function(element, pseudo) { var computedStyle = null; var window = self.getElementWindow(element); + var pseudo = pseudo || null; if (element.currentStyle) { computedStyle = element.currentStyle; } else if (window.getComputedStyle) { - computedStyle = window.getComputedStyle(element, null); + computedStyle = window.getComputedStyle(element, pseudo); } return computedStyle; @@ -240,7 +241,7 @@ _global.HTMLCS.util = function() { * Returns true if the element is deliberately hidden from Accessibility APIs using ARIA hidden. * * Not: This is separate to isAccessibilityHidden() due to a need to check specifically for aria hidden. - * + * * @param {Node} element The element to check. * * @return {Boolean} @@ -397,7 +398,7 @@ _global.HTMLCS.util = function() { * Returns all elements that are visible to the accessibility API. * * @param {Node} element The parent element to search. - * @param {String} selector Optional selector to pass to + * @param {String} selector Optional selector to pass to * * @return {Array} */ @@ -532,6 +533,82 @@ _global.HTMLCS.util = 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} + */ + 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. * @@ -552,7 +629,11 @@ _global.HTMLCS.util = function() { colour = { red: (matches[1] / 255), green: (matches[2] / 255), - blue: (matches[3] / 255) + blue: (matches[3] / 255), + alpha: 1.0 + }; + if (matches[4]) { + colour.alpha = parseFloat(/^,\s*(.*)$/.exec(matches[4])[1]); } } else { // Hex digit format. @@ -566,8 +647,9 @@ _global.HTMLCS.util = 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) + gsreen: (parseInt(colour.substr(2, 2), 16) / 255), + blue: (parseInt(colour.substr(4, 2), 16) / 255), + alpha: 1 }; } @@ -1290,4 +1372,4 @@ _global.HTMLCS.util = function() { }; return self; -}(); \ No newline at end of file +}(); 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 d368f954..04165761 100755 --- a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js +++ b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js @@ -202,6 +202,7 @@ _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) { @@ -230,6 +231,10 @@ _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 77380f2a..e562bd23 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 @@ -20,7 +20,12 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3_Contrast = { var failures = []; if (!top.ownerDocument) { - var toProcess = [top.getElementsByTagName('body')[0]]; + var toProcess = []; + var body = top.getElementsByTagName('body'); + if (body.length) { + //SVG objects will not have a body element. Don't check them. + var toProcess = [body[0]]; + } } else { var toProcess = [top]; } @@ -52,11 +57,11 @@ _global.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; } - + if (style.position == 'absolute') { isAbsolute = true; } @@ -86,6 +91,7 @@ _global.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; } @@ -93,15 +99,40 @@ _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 + var beforeStyle = HTMLCS.util.style(parent, ':before'); + if ( + beforeStyle + && beforeStyle.position == 'fixed' + && beforeStyle.willChange == 'transform' + //Make sure it is trying to cover the entire content area + && beforeStyle.width == parentStyle.width + && parseInt(beforeStyle.height, 10) <= parseInt(parentStyle.height, 10) + //And finally it needs a background image + && beforeStyle.backgroundImage !== 'none' + ) { + hasBgImg = true; + break; + } + 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, @@ -125,9 +156,10 @@ _global.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, 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 43486904..8619b15b 100755 --- a/Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js +++ b/Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js @@ -185,6 +185,13 @@ _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. From 5086fe6a30de7d9db9b7e5a3a222a5a4251d24af Mon Sep 17 00:00:00 2001 From: Phillip Baker Date: Tue, 3 Jul 2018 21:00:16 -0400 Subject: [PATCH 2/2] 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 - .../Sniffs/Principle1/Guideline1_4/1_4_3.js | 8 +- .../Principle1/Guideline1_4/1_4_3_Contrast.js | 40 +++++--- .../Sniffs/Principle4/Guideline4_1/4_1_2.js | 7 -- Tests/WCAG2/1_4_3_Contrast.html | 2 + Translations/en.js | 1 + 7 files changed, 50 insertions(+), 107 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.js b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js index 97677e07..da1f8093 100644 --- a/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js +++ b/Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js @@ -39,7 +39,7 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3 = { for (var i = 0; i < failures.length; i++) { var element = failures[i].element; - + var decimals = 2; var value = (Math.round(failures[i].value * Math.pow(10, decimals)) / Math.pow(10, decimals)); var required = failures[i].required; @@ -47,13 +47,14 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3 = { var hasBgImg = failures[i].hasBgImage || false; var bgColour = failures[i].bgColour || false; var isAbsolute = failures[i].isAbsolute || false; + var hasAlpha = failures[i].hasAlpha || false; // If the values would look identical, add decimals to the value. while (required === value) { decimals++; value = (Math.round(failures[i].value * Math.pow(10, decimals)) / Math.pow(10, decimals)); } - + if (required === 4.5) { var code = 'G18'; } else if (required === 3.0) { @@ -80,6 +81,9 @@ _global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_4_1_4_3 = { } else if (hasBgImg === true) { code += '.BgImage'; HTMLCS.addMessage(HTMLCS.WARNING, element, _global.HTMLCS.getTranslation("1_4_3_G18_or_G145.BgImage").replace(/\{\{required\}\}/g, required), code); + } else if (hasAlpha === true) { + code += '.Alpha'; + HTMLCS.addMessage(HTMLCS.WARNING, element, _global.HTMLCS.getTranslation("1_4_3_G18_or_G145.Alpha").replace(/\{\{required\}\}/g, required), code); } else { code += '.Fail'; HTMLCS.addMessage(HTMLCS.ERROR, element, _global.HTMLCS.getTranslation("1_4_3_G18_or_G145.Fail").replace(/\{\{required\}\}/g, required).replace(/\{\{value\}\}/g, value) + recommendText, 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 e562bd23..33149c3e 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 @@ -119,15 +119,31 @@ _global.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 (bgColour && HTMLCS.util.colourStrToRGB(bgColour).alpha < 1.0) { + // If we have a rgba background colour, skip the contrast ratio checks, + // and push a warning instead. + failures.push({ + element: node, + colour: foreColour, + bgColour: bgColour, + value: undefined, + required: reqRatio, + hasAlpha: true, + }); + continue; + } else if (foreColour && HTMLCS.util.colourStrToRGB(foreColour).alpha < 1.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, + hasAlpha: true + }); + 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

+ diff --git a/Translations/en.js b/Translations/en.js index 3d483325..7665513c 100644 --- a/Translations/en.js +++ b/Translations/en.js @@ -177,6 +177,7 @@ _global.translation['en'] = { //1_4_3.js ,"1_4_3_G18_or_G145.Abs" : '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.' ,"1_4_3_G18_or_G145.BgImage" : '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.' + ,"1_4_3_G18_or_G145.Alpha" : 'This element\'s text or background contains transparency. Ensure the contrast ratio between the text and background are at least {{required}}:1.' ,"1_4_3_G18_or_G145.Fail" : 'This element has insufficient contrast at this conformance level. Expected a contrast ratio of at least {{required}}:1, but text in this element has a contrast ratio of {{value}}:1.' ,"1_4_3_G18_or_G145.Fail.Recomendation" : 'Recommendation: change' ,"1_4_3_G18_or_G145.Fail.Recomendation.Text" : 'text colour to'