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

fixrule(text_contrast_sufficient): Support CJK large scale text in color contrast calculation #1888

Merged
merged 10 commits into from
Apr 12, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -17,40 +17,41 @@ import { ColorUtil } from "../../v2/dom/ColorUtil";
import { Rule, RuleResult, RuleFail, RuleContext, RulePotential, RulePass, RuleContextHierarchy } from "../api/IRule";
import { eRulePolicy, eToolkitLevel } from "../api/IRule";
//import { setCache } from "../util/CacheUtil";
import { getWeightNumber, getFontInPixels } from "../util/CSSUtil";
import { getWeightNumber, getFontInPixels} from "../util/CSSUtil";
import { containsCKJ } from "../util/CommonUtil";

export let text_contrast_sufficient: Rule = {
id: "text_contrast_sufficient",
context: "dom:*",
refactor: {
"IBMA_Color_Contrast_WCAG2AA": {
"Pass_0": "Pass_0",
"Fail_1": "Fail_1",
"Potential_1": "Potential_same_color"
"Pass_0": "pass",
"Fail_1": "fail_contrast",
"Potential_1": "potential_same_color"
},
"IBMA_Color_Contrast_WCAG2AA_PV": {
"Pass_0": "Pass_0",
"Potential_1": "Potential_graphic_background"
"pass_0": "pass",
"potential_1": "potential_graphic_background"
}
},
help: {
"en-US": {
"group": `text_contrast_sufficient.html`,
"Pass_0": `text_contrast_sufficient.html`,
"Fail_1": `text_contrast_sufficient.html`,
"Potential_same_color": `text_contrast_sufficient.html`,
"Potential_graphic_background": `text_contrast_sufficient.html`,
"Potential_text_shadow": `text_contrast_sufficient.html`
"pass": `text_contrast_sufficient.html`,
"fail_contrast": `text_contrast_sufficient.html`,
"potential_same_color": `text_contrast_sufficient.html`,
"potential_graphic_background": `text_contrast_sufficient.html`,
"potential_text_shadow": `text_contrast_sufficient.html`
}
},
messages: {
"en-US": {
"group": "The contrast ratio of text with its background must meet WCAG AA requirements",
"Pass_0": "Rule Passed",
"Fail_1": "Text contrast of {0} with its background is less than the WCAG AA minimum requirements for text of size {1}px and weight of {2}",
"Potential_same_color": "The foreground text and its background color are both detected as {3}. Verify the text meets the WCAG AA requirements for minimum contrast",
"Potential_graphic_background": "Verify the contrast ratio of the text against the lightest and the darkest colors of the background meets the WCAG AA minimum requirements for text of size {1}px and weight of {2}",
"Potential_text_shadow": "Verify the contrast ratio of the text with shadow meets the WCAG AA minimum requirements for text of size {1}px and weight of {2}"
"pass": "The contrast ratio of text with its background meets WCAG AA requirements",
"fail_contrast": "Text contrast of {0} with its background is less than the WCAG AA minimum requirements for text of size {1}px and weight of {2}",
"potential_same_color": "The foreground text and its background color are both detected as {3}. Verify the text meets the WCAG AA requirements for minimum contrast",
"potential_graphic_background": "Verify the contrast ratio of the text against the lightest and the darkest colors of the background meets the WCAG AA minimum requirements for text of size {1}px and weight of {2}",
"potential_text_shadow": "Verify the contrast ratio of the text with shadow meets the WCAG AA minimum requirements for text of size {1}px and weight of {2}"
}
},
rulesets: [{
Expand Down Expand Up @@ -104,9 +105,10 @@ export let text_contrast_sufficient: Rule = {
* see https://stackoverflow.com/questions/3770117/what-is-the-range-of-unicode-printable-characters
* (3) for now not consider unicode special characters that are different in different languages
*/
let regex = /[^(a-zA-Z\d\s)\u0000-\u0008\u000B-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F\u205F-\u206F\u3000\uFEFF]+/g;
const removed = childStr.trim().replace(regex, '');
if (removed.trim().length === 0)
//let regex = /[^(a-zA-Z\d\s)\u0000-\u0008\u000B-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F\u205F-\u206F\u3000\uFEFF]+/g;
let regex = /[^(a-zA-Z\d\s)\^(\u4e00-\u9fff\u3400-\u4dbf)\u0000-\u0008\u000B-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F\u205F-\u206F\u3000\uFEFF]+/g;
childStr = childStr.trim().replace(regex, '');
if (childStr.trim().length === 0)
return null;
}

Expand Down Expand Up @@ -252,6 +254,12 @@ export let text_contrast_sufficient: Rule = {
let weight = getWeightNumber(style.fontWeight);
let size = getFontInPixels(style.fontSize, elem);
let isLargeScale = size >= 24 || size >= 18.6 && weight >= 700;

if (containsCKJ(childStr)) {
// https://github.com/act-rules/act-rules.github.io/pull/2121/files
// for CJK, 22 pt or 18 pt with font-weight >= 700, 1pt = 1.333 px
isLargeScale = size >= 29.3 || size >= 24 && weight >= 700;
}
let passed = ratio >= 4.5 || (ratio >= 3 && isLargeScale);
let hasBackground = colorCombo.hasBGImage || colorCombo.hasGradient;
let textShadow = colorCombo.textShadow;
Expand Down Expand Up @@ -287,19 +295,19 @@ export let text_contrast_sufficient: Rule = {
if (!passed) {
if (hasBackground) {
// fire potential since a text on an image or gradient may be still viewable, depending on the text location on the gradient or image
return RulePotential("Potential_graphic_background", [ratio.toFixed(2), size, weight]);;
return RulePotential("potential_graphic_background", [ratio.toFixed(2), size, weight]);;
} else if (textShadow) {
// fire potential since a text with shadow may be still viewable, depending on the shadow efffects
return RulePotential("Potential_text_shadow", [ratio.toFixed(2), size, weight]);;
return RulePotential("potential_text_shadow", [ratio.toFixed(2), size, weight]);;
} else {
if (fg.toHex() === bg.toHex()) {
return RulePotential("Potential_same_color", [ratio.toFixed(2), size, weight, fg.toHex(), bg.toHex(), colorCombo.hasBGImage, colorCombo.hasGradient]);
return RulePotential("potential_same_color", [ratio.toFixed(2), size, weight, fg.toHex(), bg.toHex(), colorCombo.hasBGImage, colorCombo.hasGradient]);
} else {
return RuleFail("Fail_1", [ratio.toFixed(2), size, weight, fg.toHex(), bg.toHex(), colorCombo.hasBGImage, colorCombo.hasGradient]);
return RuleFail("fail_contrast", [ratio.toFixed(2), size, weight, fg.toHex(), bg.toHex(), colorCombo.hasBGImage, colorCombo.hasGradient]);
}
}
} else {
return RulePass("Pass_0", [ratio.toFixed(2), size, weight, fg.toHex(), bg.toHex(), colorCombo.hasBGImage, colorCombo.hasGradient]);
return RulePass("pass", [ratio.toFixed(2), size, weight, fg.toHex(), bg.toHex(), colorCombo.hasBGImage, colorCombo.hasGradient]);
}
}
}
17 changes: 17 additions & 0 deletions accessibility-checker-engine/src/v4/util/CommonUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,20 @@ export function getDeprecatedAriaAttributes(element: Element) {
}
return ret;
}

/*
* string contains CJK (chinese, japaneses, or korea)
* return: boolean
*/
export function containsCKJ(text: string) {
if (!text) return false;

// https://en.wikipedia.org/wiki/CJK_Unified_Ideographs https://ayaka.shn.hk/hanregex/
let regex =/(?:[\u4e00-\u9fff\u3400-\u4dbf])+/g;

const replaced = text.trim().replace(regex, '');
if (replaced.length === text.trim().length)
return false;

return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ <h1>Test page</h1>
"dom": "/html[1]/body[1]/main[1]/h1[1]",
"aria": "/document[1]/main[1]/heading[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"21.00",
32,
Expand All @@ -130,7 +130,7 @@ <h1>Test page</h1>
"dom": "/html[1]/body[1]/main[1]/div[1]/div[1]",
"aria": "/document[1]/main[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.85 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.85",
Expand All @@ -154,7 +154,7 @@ <h1>Test page</h1>
"dom": "/html[1]/body[1]/main[1]/div[1]/div[3]",
"aria": "/document[1]/main[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.85 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.85",
Expand All @@ -178,7 +178,7 @@ <h1>Test page</h1>
"dom": "/html[1]/body[1]/main[1]/div[1]/div[6]",
"aria": "/document[1]/main[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.85 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.85",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6775,8 +6775,8 @@
"dom": "/html[1]/body[1]/div[1]/div[1]/div[1]/section[1]/div[1]/div[1]/article[1]/div[1]/div[1]/div[1]",
"aria": "/document[1]/article[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"5.33",
12,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ <h3>Color using Class Tests</h3>
"dom": "/html[1]/body[1]/h3[1]",
"aria": "/document[1]/heading[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"21.00",
18.72,
Expand All @@ -113,8 +113,8 @@ <h3>Color using Class Tests</h3>
"dom": "/html[1]/body[1]/div[1]/div[1]",
"aria": "/document[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"8.59",
16,
Expand All @@ -137,7 +137,7 @@ <h3>Color using Class Tests</h3>
"dom": "/html[1]/body[1]/div[2]/div[1]",
"aria": "/document[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 1.13 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"1.13",
Expand All @@ -161,7 +161,7 @@ <h3>Color using Class Tests</h3>
"dom": "/html[1]/body[1]/div[3]/div[1]",
"aria": "/document[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.49 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.49",
Expand All @@ -185,7 +185,7 @@ <h3>Color using Class Tests</h3>
"dom": "/html[1]/body[1]/div[4]/div[1]/div[1]",
"aria": "/document[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 1.13 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"1.13",
Expand All @@ -209,7 +209,7 @@ <h3>Color using Class Tests</h3>
"dom": "/html[1]/body[1]/div[5]/div[1]",
"aria": "/document[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.49 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.49",
Expand All @@ -233,8 +233,8 @@ <h3>Color using Class Tests</h3>
"dom": "/html[1]/body[1]/div[6]/div[1]/div[1]",
"aria": "/document[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"5.25",
16,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"dom": "/html[1]/body[1]/main[1]",
"aria": "/document[1]/main[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"21.00",
16,
Expand All @@ -60,8 +60,8 @@
"dom": "/html[1]/body[1]/main[1]/div[1]/div[1]/#document-fragment[1]/div[1]",
"aria": "/document[1]/main[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"15.61",
16,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dom": "/html[1]/body[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.32 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.32",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"dom": "/html[1]/body[1]/div[1]",
"aria": "/document[1]/button[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 3.86 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"3.86",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dom": "/html[1]/body[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Potential_graphic_background",
"reasonId": "potential_graphic_background",
"message": "Verify the contrast ratio of the text against the lightest and the darkest colors of the background meets the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"1.00",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"dom": "/html[1]/body[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Potential_graphic_background",
"reasonId": "potential_graphic_background",
"message": "Verify the contrast ratio of the text against the lightest and the darkest colors of the background meets the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.82",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"dom": "/html[1]/body[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.11 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.11",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"dom": "/html[1]/body[1]/div[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.11 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.11",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"dom": "/html[1]/body[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 2.32 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"2.32",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"dom": "/html[1]/body[1]/span[1]",
"aria": "/document[1]"
},
"reasonId": "Potential_graphic_background",
"reasonId": "potential_graphic_background",
"message": "Verify the contrast ratio of the text against the lightest and the darkest colors of the background meets the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"1.00",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
"dom": "/html[1]/body[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"12.63",
16,
Expand All @@ -52,7 +52,7 @@
"dom": "/html[1]/body[1]/p[2]",
"aria": "/document[1]/paragraph[2]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 3.86 with its background is less than the WCAG AA minimum requirements for text of size 16px and weight of 400",
"messageArgs": [
"3.86",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"dom": "/html[1]/body[1]/button[1]",
"aria": "/document[1]/button[1]"
},
"reasonId": "Fail_1",
"reasonId": "fail_contrast",
"message": "Text contrast of 3.86 with its background is less than the WCAG AA minimum requirements for text of size 13.3333px and weight of 400",
"messageArgs": [
"3.86",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"dom": "/html[1]/body[1]/p[1]",
"aria": "/document[1]/paragraph[1]"
},
"reasonId": "Potential_same_color",
"reasonId": "potential_same_color",
"message": "The foreground text and its background color are both detected as #ffffff. Verify the text meets the WCAG AA requirements for minimum contrast",
"messageArgs": [
"1.00",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"dom": "/html[1]/body[1]/label[1]",
"aria": "/document[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"reasonId": "pass",
"message": "The contrast ratio of text with its background meets WCAG AA requirements",
"messageArgs": [
"3.54",
16,
Expand Down
Loading
Loading