diff --git a/CHANGES.md b/CHANGES.md index b1413c29a5..cf04f4562d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ Themes: - added "vs Dark " theme [Twineee1][] +- added `GitHub Adaptive (Light & Dark)` theme [makl11][] New Grammars: @@ -27,6 +28,10 @@ Core Grammars: - fix(css) `unicode-range` parsing, issue #4253 [Kerry Shetline][] - fix(csharp) Support digit separators [te-ing][] +Developer Tools: + +- add support for checking a11y for themes with `light-dark(...)` colors to `tools/checkTheme.js` + Documentation: - alphabetized languages + upper-cased BASIC in SUPPORTED_LANGUAGES.md [Adam Lui][] @@ -55,6 +60,7 @@ CONTRIBUTORS [te-ing]: https://github.com/te-ing [Anthony Martin]: https://github.com/anthony-c-martin [NriotHrreion]: https://github.com/NriotHrreion +[makl11]: https://github.com/makl11 ## Version 11.11.1 diff --git a/src/styles/github-adaptive.css b/src/styles/github-adaptive.css new file mode 100644 index 0000000000..16fa594efe --- /dev/null +++ b/src/styles/github-adaptive.css @@ -0,0 +1,118 @@ +/*! + Theme: GitHub Adaptive (Light & Dark) + Description: A combination of @Hirse's GitHub Light and Dark themes that switches based on the user's system preference. + Authors: github.com, @Hirse +*/ + + +:root { + color-scheme: light dark; +} + +.hljs.light { + color-scheme: light; +} +.hljs.dark { + color-scheme: dark; +} + +.hljs { + color: light-dark(#24292e, #c9d1d9); + background: light-dark(#ffffff, #0d1117); +} + +.hljs-doctag, +.hljs-keyword, +.hljs-meta .hljs-keyword, +.hljs-template-tag, +.hljs-template-variable, +.hljs-type, +.hljs-variable.language_ { + color: light-dark(#d73a49, #ff7b72); +} + +.hljs-title, +.hljs-title.class_, +.hljs-title.class_.inherited__, +.hljs-title.function_ { + color: light-dark(#6f42c1, #d2a8ff); +} + +.hljs-attr, +.hljs-attribute, +.hljs-literal, +.hljs-meta, +.hljs-number, +.hljs-operator, +.hljs-variable, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-selector-id { + color: light-dark(#005cc5, #79c0ff); +} + +.hljs-regexp, +.hljs-string, +.hljs-meta .hljs-string { + color: light-dark(#032f62, #a5d6ff); +} + +.hljs-built_in, +.hljs-symbol { + color: light-dark(#e36209, #ffa657); +} + +.hljs-comment, +.hljs-code, +.hljs-formula { + color: light-dark(#6a737d, #8b949e); +} + +.hljs-name, +.hljs-quote, +.hljs-selector-tag, +.hljs-selector-pseudo { + color: light-dark(#22863a, #7ee787); +} + +.hljs-subst { + color: light-dark(#24292e, #c9d1d9); +} + +.hljs-section { + color: light-dark(#005cc5, #1f6feb); + font-weight: bold; +} + +.hljs-bullet { + color: light-dark(#735c0f, #f2cc60); +} + +.hljs-emphasis { + color: light-dark(#24292e, #c9d1d9); + font-style: italic; +} + +.hljs-strong { + color: light-dark(#24292e, #c9d1d9); + font-weight: bold; +} + +.hljs-addition { + color: light-dark(#22863a, #aff5b4); + background-color: light-dark(#f0fff4, #033a16); +} + +.hljs-deletion { + color: light-dark(#b31d28, #ffdcd7); + background-color: light-dark(#ffeef0, #67060c); +} + +.hljs-char.escape_, +.hljs-link, +.hljs-params, +.hljs-property, +.hljs-punctuation, +.hljs-tag { + /* purposely ignored */ +} diff --git a/tools/checkTheme.js b/tools/checkTheme.js index c5e49bfd02..819d27fad5 100755 --- a/tools/checkTheme.js +++ b/tools/checkTheme.js @@ -187,6 +187,19 @@ function check_group(group, rules) { } } +function is_light_dark(value) { + return typeof value === "string" && value.startsWith("light-dark"); +} + +function get_colors_from_light_dark(light_dark_value) { + light_dark_value = light_dark_value.trim(); + light_dark_value = light_dark_value.substring(11, light_dark_value.length - 1); + let [light, dark] = light_dark_value.split(","); + light = light.trim(); + dark = dark.trim(); + return [light, dark]; +} + const round2 = (x) => Math.round(x*100)/100; class CSSRule { @@ -199,20 +212,38 @@ class CSSRule { } this.fg = rule.declarations.find(x => x.property =="color")?.value; + if (is_light_dark(this.bg)) { + [this.bg, this.bg_dark] = get_colors_from_light_dark(this.bg); + } + if (is_light_dark(this.fg)) { + [this.fg, this.fg_dark] = get_colors_from_light_dark(this.fg); + } + if (this.bg) { this.bg = csscolors[this.bg] || this.bg; } if (this.fg) { this.fg = csscolors[this.fg] || this.fg; } + if (this.bg_dark) { + this.bg_dark = csscolors[this.bg_dark] || this.bg_dark; + } + if (this.fg_dark) { + this.fg_dark = csscolors[this.fg_dark] || this.fg_dark; + } // 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; + if (body?.hasLightDark) { + if (!this.bg_dark) this.bg_dark = body.backgroundDark; + if (!this.fg_dark) this.fg_dark = body.foregroundDark; + } } } } + get background() { return this.bg } @@ -220,17 +251,46 @@ class CSSRule { get foreground() { return this.fg } + + get backgroundDark() { + return this.bg_dark; + } + + get foregroundDark() { + return this.fg_dark; + } + get hasColor() { if (!this.rule.declarations) return false; return this.fg || this.bg; } + + get hasLightDark() { + if (!this.rule.declarations) return false; + return this.fg_dark || this.bg_dark; + } + toString() { + if (this.hasLightDark) { + return ` light: ${this.foreground} on ${this.background}, dark: ${this.foregroundDark} on ${this.backgroundDark}`; + } return ` ${this.foreground} on ${this.background}` } contrastRatio() { if (!this.foreground) return "unknown (no fg)" if (!this.background) return "unknown (no bg)" + if (this.hasLightDark) { + const lightRatio = round2(wcagContrast.hex(this.foreground, this.background)); + + let darkRatio = "unknown"; + if (!this.foregroundDark) darkRatio = "unknown (no fg)"; + else if (!this.backgroundDark) darkRatio = "unknown (no bg)"; + else darkRatio = round2(wcagContrast.hex(this.foregroundDark, this.backgroundDark)); + + return [lightRatio, darkRatio]; + } + return round2(wcagContrast.hex(this.foreground, this.background)); } } @@ -240,10 +300,17 @@ function contrast_report(rules) { var hljs = rules.find (x => x.selectors && x.selectors.includes(".hljs")); var body = new CSSRule(hljs); + const head = body.hasLightDark + ? ['ratio light', 'ratio dark', 'selector', 'fg light', 'bg light', 'fg dark', 'bg dark'] + : ['ratio', 'selector', 'fg', 'bg']; + const colWidths = body.hasLightDark + ? [13, 12, 40, 10, 10, 10, 10] + : [7, 40, 10, 10]; + const table = new Table({ chars: {'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''}, - head: ['ratio', 'selector', 'fg', 'bg'], - colWidths: [7, 40, 10, 10], + head, + colWidths, style: { head: ['grey'] } @@ -252,12 +319,23 @@ function contrast_report(rules) { rules.forEach(rule => { var color = new CSSRule(rule, body); if (!color.hasColor) return; - table.push([ - color.contrastRatio(), - rule.selectors, - color.foreground, - color.background - ]) + if (color.hasLightDark) { + table.push([ + ...color.contrastRatio(), + rule.selectors, + color.foreground, + color.background, + color.foregroundDark, + color.backgroundDark + ]); + } else { + table.push([ + color.contrastRatio(), + rule.selectors, + color.foreground, + color.background + ]); + } // console.log(r.selectors[0], color.contrastRatio(), color.toString()); }) console.log(table.toString()) diff --git a/tools/developer.html b/tools/developer.html index 1086f01847..47ef00ef48 100644 --- a/tools/developer.html +++ b/tools/developer.html @@ -268,6 +268,7 @@

Code

+