diff --git a/CHANGES.md b/CHANGES.md index 262bc2100a..fc60632dd1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,11 @@ -## Version 11.3.2 (most likely) +## Version 11.4 (most likely) + +Themes: + +- `Default` is now much closer WCAG AA (contrast) (#3402) [Josh Goebel] +- `Dark` now meets WCAG AA (contrast) (#3402) [Josh Goebel] + +These changes should be for the better and should not be super noticeable but if you're super picky about your colors you may want to intervene here or copy over the older themes from 11.3 or prior. Grammars: diff --git a/package-lock.json b/package-lock.json index 877b12bf68..55b90dec9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "colors": "^1.1.2", "commander": "8.2", "css": "^3.0.0", + "css-color-names": "^1.0.1", "deep-freeze-es6": "^1.4.1", "del": "^6.0.0", "dependency-resolver": "^2.0.1", @@ -38,7 +39,8 @@ "should": "^13.2.3", "terser": "^5.7.0", "tiny-worker": "^2.3.0", - "typescript": "^4.4.4" + "typescript": "^4.4.4", + "wcag-contrast": "^3.0.0" }, "engines": { "node": ">=12.0.0" @@ -1042,6 +1044,15 @@ "source-map-resolve": "^0.6.0" } }, + "node_modules/css-color-names": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", + "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -3358,6 +3369,15 @@ "node": ">=8" } }, + "node_modules/relative-luminance": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/relative-luminance/-/relative-luminance-2.0.1.tgz", + "integrity": "sha512-wFuITNthJilFPwkK7gNJcULxXBcfFZvZORsvdvxeOdO44wCeZnuQkf3nFFzOR/dpJNxYsdRZJLsepWbyKhnMww==", + "dev": true, + "dependencies": { + "esm": "^3.0.84" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4075,6 +4095,15 @@ "node": ">=12" } }, + "node_modules/wcag-contrast": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wcag-contrast/-/wcag-contrast-3.0.0.tgz", + "integrity": "sha512-RWbpg/S7FOXDCwqC2oFhN/vh8dHzj0OS6dpyOSDHyQFSmqmR+lAUStV/ziTT1GzDqL9wol+nZQB4vCi5yEak+w==", + "dev": true, + "dependencies": { + "relative-luminance": "^2.0.0" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -5111,6 +5140,12 @@ "source-map-resolve": "^0.6.0" } }, + "css-color-names": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", + "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==", + "dev": true + }, "cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", @@ -6839,6 +6874,15 @@ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, + "relative-luminance": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/relative-luminance/-/relative-luminance-2.0.1.tgz", + "integrity": "sha512-wFuITNthJilFPwkK7gNJcULxXBcfFZvZORsvdvxeOdO44wCeZnuQkf3nFFzOR/dpJNxYsdRZJLsepWbyKhnMww==", + "dev": true, + "requires": { + "esm": "^3.0.84" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7399,6 +7443,15 @@ "xml-name-validator": "^4.0.0" } }, + "wcag-contrast": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wcag-contrast/-/wcag-contrast-3.0.0.tgz", + "integrity": "sha512-RWbpg/S7FOXDCwqC2oFhN/vh8dHzj0OS6dpyOSDHyQFSmqmR+lAUStV/ziTT1GzDqL9wol+nZQB4vCi5yEak+w==", + "dev": true, + "requires": { + "relative-luminance": "^2.0.0" + } + }, "webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index a6376bd740..985b47fdd6 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "colors": "^1.1.2", "commander": "8.2", "css": "^3.0.0", + "css-color-names": "^1.0.1", "deep-freeze-es6": "^1.4.1", "del": "^6.0.0", "dependency-resolver": "^2.0.1", @@ -84,6 +85,7 @@ "should": "^13.2.3", "terser": "^5.7.0", "tiny-worker": "^2.3.0", - "typescript": "^4.4.4" + "typescript": "^4.4.4", + "wcag-contrast": "^3.0.0" } } diff --git a/src/styles/dark.css b/src/styles/dark.css index 34708897f3..052dbc94f1 100644 --- a/src/styles/dark.css +++ b/src/styles/dark.css @@ -6,7 +6,7 @@ Dark style from softwaremaniacs.org (c) Ivan Sagalaev Math.round(x*100)/100; + +class CSSRule { + constructor(rule, body) { + this.rule = rule; + if (rule.declarations) { + this.bg = rule.declarations.find(x => x.property =="background")?.value; + this.fg = rule.declarations.find(x => x.property =="color")?.value; + + if (this.bg) { + this.bg = csscolors[this.bg] || this.bg; + } + if (this.fg) { + this.fg = csscolors[this.fg] || this.fg; + } + + // inherit from body if we're missing fg or bg + if (this.hasColor) { + if (!this.bg) this.bg = body.background; + if (!this.fg) this.fg = body.foreground; + } + } + } + get background() { + return this.bg + } + + get foreground() { + return this.fg + } + get hasColor() { + if (!this.rule.declarations) return false; + return this.fg || this.bg; + } + toString() { + return ` ${this.foreground} on ${this.background}` + } + + contrastRatio() { + if (!this.foreground) return "unknown (no fg)" + if (!this.background) return "unknown (no bg)" + return round2(wcagContrast.hex(this.foreground, this.background)); + } +} + +function contrast_report(rules) { + console.log("Accessibility Report".yellow); + + var hljs = rules.find (x => x.selectors && x.selectors.includes(".hljs")); + var body = new CSSRule(hljs); + const table = new Table({ + chars: {'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''}, + head: ['ratio', 'selector', 'fg', 'bg'], + colWidths: [7, 40, 10, 10], + style: { + head: ['grey'] + } + }); + + rules.forEach(rule => { + var color = new CSSRule(rule, body); + if (!color.hasColor) return; + table.push([ + color.contrastRatio(), + rule.selectors, + color.foreground, + color.background + ]) + // console.log(r.selectors[0], color.contrastRatio(), color.toString()); + }) + console.log(table.toString()) +} + function validate(data) { const rules = data.stylesheet.rules; @@ -195,6 +271,8 @@ function validate(data) { check_group(CODE, rules); check_group(OTHER, rules); check_group(HIGH_FIDELITY, rules); + + contrast_report(rules); } process.argv.shift();