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 #134

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3a4305b
Merge remote-tracking branch 'yargalot/master'
mfairchild365 Apr 29, 2014
247b864
Merge remote-tracking branch 'yargalot/master'
mfairchild365 Jun 26, 2014
772f653
Merge branch 'alpha-index'
mfairchild365 Jun 26, 2014
afc99ba
Add methods to convert an rgba background color to rgb
mfairchild365 Jun 27, 2014
b3d28fc
Convert rgba to a solid color before checking contrast
mfairchild365 Jun 27, 2014
b8d7684
Don't warn about rgba anymore
mfairchild365 Jun 27, 2014
eaa1309
Convert to a colour string
mfairchild365 Jun 27, 2014
e2159c5
Return rgb channels as %
mfairchild365 Jun 27, 2014
e730b35
Allow checking against any rgba color, not just the background
mfairchild365 Jun 27, 2014
36c217f
Also convert the foreground (text) rgba color
mfairchild365 Jun 27, 2014
b8d4fa5
Merge remote-tracking branch 'squizlabs/master' into alpha-checking
mfairchild365 Jun 27, 2014
7ce911b
Correctly calculate layered transparencies
mfairchild365 Jun 27, 2014
834d0b7
Allow implicit labels to provide a name for input
kabel Jul 2, 2014
e662366
Merge remote-tracking branch 'kabel/feature-implicit-labels'
mfairchild365 Jul 8, 2014
203031f
Assume a white background if no solid colors were found
mfairchild365 Jul 9, 2014
338cd1d
Include an alpha
mfairchild365 Jul 9, 2014
48ad4e7
Merge branch 'alpha-checking'
mfairchild365 Jul 9, 2014
20fa290
Make absolutely positioned contrast errors warnings
mfairchild365 Aug 11, 2014
bab8109
Remove trailing comma
mfairchild365 Aug 11, 2014
7fe9b5c
Merge pull request #1 from mfairchild365/contrast-abs
kabel Aug 11, 2014
5c1ad9f
Stop processing after sending the failure
mfairchild365 Sep 3, 2014
0a8d34f
Merge remote-tracking branch 'squizlabs/master'
mfairchild365 Nov 7, 2014
a9e6962
`msg` is already defined
mfairchild365 Nov 7, 2014
b6f2758
Fix false positive
mfairchild365 Nov 7, 2014
f92716d
Merge pull request #2 from mfairchild365/master
kabel Nov 7, 2014
bf48fb6
Don't try to check SVG objects
mfairchild365 Dec 5, 2014
77c9a05
Merge pull request #3 from mfairchild365/svg-objects
kabel Dec 5, 2014
9de66d4
Merge pull request #4 from squizlabs/master
kabel Dec 5, 2014
ebb3e0b
Merge branch 'master' of https://github.com/squizlabs/HTML_CodeSniffer
mfairchild365 Mar 3, 2015
883a855
Merge pull request #5 from mfairchild365/master
kabel Mar 3, 2015
935eb35
Merge remote-tracking branch 'origin' into 2.0
mfairchild365 Feb 15, 2016
4f6aff2
Check for the will-change: 'transform' background image performance hack
mfairchild365 May 31, 2016
a11e79c
Merge pull request #6 from mfairchild365/background-will-change-trans…
kabel May 31, 2016
8af0f5e
Merge branch 'master' of https://github.com/squizlabs/HTML_CodeSniffer
mfairchild365 May 31, 2016
90710cd
Fix merge with upstream
mfairchild365 May 31, 2016
d2a6021
Merge pull request #7 from mfairchild365/master
kabel May 31, 2016
f200370
form controls don't need labels if they are display:none
mfairchild365 Aug 30, 2016
9bc316c
Merge pull request #8 from mfairchild365/hidden-inputs
kabel Aug 30, 2016
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
90 changes: 86 additions & 4 deletions HTMLCS.Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,15 @@ 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;
Expand Down Expand Up @@ -357,6 +358,82 @@ HTMLCS.util = function() {

var lum = ((transformed.red * 0.2126) + (transformed.green * 0.7152) + (transformed.blue * 0.0722));
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
}
}

/**
Expand All @@ -379,7 +456,11 @@ 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.
Expand All @@ -394,7 +475,8 @@ 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)
blue: (parseInt(colour.substr(4, 2), 16) / 255),
alpha: 1
};
}

Expand Down
5 changes: 5 additions & 0 deletions Standards/WCAG2AAA/Sniffs/Principle1/Guideline1_3/1_3_1.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ var 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 @@ -203,6 +204,10 @@ var 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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ var 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];
}
Expand Down Expand Up @@ -52,8 +57,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,22 +91,48 @@ 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;
}
if (parentStyle.position == 'absolute') {
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,
Expand All @@ -117,19 +148,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
7 changes: 7 additions & 0 deletions Standards/WCAG2AAA/Sniffs/Principle4/Guideline4_1/4_1_2.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,13 @@ var 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