Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rgba contrast checking by converting rgba to a solid rgb color #97

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 83 additions & 2 deletions HTMLCS.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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.
Expand All @@ -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
};
}

Expand Down
3 changes: 3 additions & 0 deletions Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_4/1_4_3.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand All @@ -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,
Expand All @@ -117,19 +126,20 @@ 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
// contrast ratio. Skip the element.
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,
Expand Down