diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | doc/_templates/indexsidebar.html | 5 | ||||
-rw-r--r-- | doc/_themes/pygments14/layout.html | 2 | ||||
-rw-r--r-- | doc/_themes/pygments14/localtoc.html | 17 | ||||
-rw-r--r-- | doc/_themes/pygments14/relations.html | 25 | ||||
-rw-r--r-- | doc/_themes/pygments14/static/pygments14.css_t | 44 | ||||
-rw-r--r-- | doc/_themes/pygments14/theme.conf | 2 | ||||
-rw-r--r-- | pygments/styles/paraiso_dark.py | 3 | ||||
-rw-r--r-- | pygments/styles/paraiso_light.py | 3 | ||||
-rw-r--r-- | requirements.txt | 1 | ||||
-rwxr-xr-x | scripts/update_contrasts.py | 21 | ||||
-rw-r--r-- | tests/contrast/min_contrasts.json | 43 | ||||
-rw-r--r-- | tests/contrast/test_contrasts.py | 97 | ||||
-rw-r--r-- | tox.ini | 1 |
14 files changed, 247 insertions, 18 deletions
@@ -247,5 +247,6 @@ Other contributors, listed alphabetically, are: * Thomas Duboucher -- CDDL lexer * Philipp Imhof -- Pango Markup formatter * Thomas Voss -- Sed lexer +* Martin Fischer -- WCAG contrast testing Many thanks for all contributions! diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html index 96d359d5..0c595ecf 100644 --- a/doc/_templates/indexsidebar.html +++ b/doc/_templates/indexsidebar.html @@ -1,3 +1,4 @@ +<section> <h3>Download</h3> {% if version.endswith('(hg)') %} <p>This documentation is for version <b>{{ version }}</b>, which is @@ -12,13 +13,15 @@ Index</a>, or install it with:</p> <pre>pip install Pygments</pre> {% endif %} - +</section> +<section> <h3>Questions? Suggestions?</h3> <p><img src="{{ pathto("_static/github.png", 1) }}" width="24" /> Clone at <a href="https://github.com/pygments/pygments">GitHub</a>.</p> <p>You can also open an issue at the <a href="https://github.com/pygments/pygments/issues">tracker</a>.</p> +</section> <p class="logo">A <a href="https://www.pocoo.org/"> <img src="{{ pathto("_static/pocoo.png", 1) }}" /></a> project</a></p> diff --git a/doc/_themes/pygments14/layout.html b/doc/_themes/pygments14/layout.html index 7523e918..d25cb443 100644 --- a/doc/_themes/pygments14/layout.html +++ b/doc/_themes/pygments14/layout.html @@ -78,9 +78,11 @@ </a> </div> </div> +<div class="flexwrapper"> {% endblock %} {% block footer %} + </div> {# closes "flexwrapper" div #} <div class="footer" role="contentinfo"> © Copyright 2006-2021, Georg Brandl and Pygments contributors. Created using <a href="https://sphinx-doc.org/">Sphinx</a> {{ diff --git a/doc/_themes/pygments14/localtoc.html b/doc/_themes/pygments14/localtoc.html new file mode 100644 index 00000000..c0e2de0c --- /dev/null +++ b/doc/_themes/pygments14/localtoc.html @@ -0,0 +1,17 @@ +{# + basic/localtoc.html + ~~~~~~~~~~~~~~~~~~~ + + Sphinx sidebar template: local table of contents. + + This file can be removed once https://github.com/sphinx-doc/sphinx/pull/9815 has landed. + + :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- if display_toc %} + <div> + <h3><a href="{{ pathto(root_doc)|e }}">{{ _('Table of Contents') }}</a></h3> + {{ toc }} + </div> +{%- endif %} diff --git a/doc/_themes/pygments14/relations.html b/doc/_themes/pygments14/relations.html new file mode 100644 index 00000000..372894db --- /dev/null +++ b/doc/_themes/pygments14/relations.html @@ -0,0 +1,25 @@ +{# + basic/relations.html + ~~~~~~~~~~~~~~~~~~~~ + + Sphinx sidebar template: relation links. + + This file can be removed once https://github.com/sphinx-doc/sphinx/pull/9815 has landed. + + :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- if prev %} +<div> + <h4>{{ _('Previous topic') }}</h4> + <p class="topless"><a href="{{ prev.link|e }}" + title="{{ _('previous chapter') }}">{{ prev.title }}</a></p> +</div> +{%- endif %} +{%- if next %} +<div> + <h4>{{ _('Next topic') }}</h4> + <p class="topless"><a href="{{ next.link|e }}" + title="{{ _('next chapter') }}">{{ next.title }}</a></p> +</div> +{%- endif %} diff --git a/doc/_themes/pygments14/static/pygments14.css_t b/doc/_themes/pygments14/static/pygments14.css_t index d95f8c68..11cfae7b 100644 --- a/doc/_themes/pygments14/static/pygments14.css_t +++ b/doc/_themes/pygments14/static/pygments14.css_t @@ -28,7 +28,6 @@ body { */ margin: 0 auto; - min-width: 780px; max-width: 1080px; } @@ -70,19 +69,36 @@ body { } div.document { + width: 700px; + flex-grow: 100; text-align: left; /*border-left: 1em solid {{ theme_lightyellow }};*/ + min-width: 500px; +} + +@media screen and (max-width: 550px) { + div.document { + min-width: inherit; + } } div.bodywrapper { - margin: 0 12px 0 240px; background-color: white; /* border-right: 1px solid {{ theme_border }}; */ } +.flexwrapper { + display: flex; + gap: 15px; + flex-wrap: wrap; + padding-right: 12px; +} + div.body { margin: 0; padding: 0.5em 20px 20px 20px; + width: 100%; + box-sizing: border-box; } div.related { @@ -125,19 +141,19 @@ div.related ul li a:hover { text-shadow: 0px 0px 1px rgba(255, 255, 255, 0.5); } -div.sphinxsidebarwrapper { - position: relative; - top: 0px; - padding: 0; -} - div.sphinxsidebar { margin: 0; padding: 0 0px 15px 15px; width: 210px; - float: left; + float: none; font-size: 1em; text-align: left; + flex-grow: 1; +} + +.sphinxsidebarwrapper > * { + flex: 1 1 0px; + min-width: 200px; } div.sphinxsidebar .logo { @@ -161,7 +177,7 @@ div.sphinxsidebar input { div.sphinxsidebar h3 { font-size: 1.5em; /* border-top: 1px solid {{ theme_border }}; */ - margin-top: 1em; + margin-top: 0; margin-bottom: 0.5em; padding-top: 0.5em; } @@ -172,7 +188,6 @@ div.sphinxsidebar h4 { } div.sphinxsidebar h3, div.sphinxsidebar h4 { - margin-right: -15px; margin-left: -15px; padding-right: 14px; padding-left: 14px; @@ -181,6 +196,13 @@ div.sphinxsidebar h3, div.sphinxsidebar h4 { /*text-shadow: 0px 0px 0.5px rgba(0, 0, 0, 0.4);*/ } +div.sphinxsidebarwrapper { + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 15px; +} + div.sphinxsidebarwrapper > h3:first-child { margin-top: 0.5em; border: none; diff --git a/doc/_themes/pygments14/theme.conf b/doc/_themes/pygments14/theme.conf index fffe66d6..8d2988fa 100644 --- a/doc/_themes/pygments14/theme.conf +++ b/doc/_themes/pygments14/theme.conf @@ -4,6 +4,8 @@ stylesheet = pygments14.css pygments_style = friendly [options] +body_min_width = inherit +body_max_width = inherit green = #66b55e darkgreen = #36852e darkgray = #666666 diff --git a/pygments/styles/paraiso_dark.py b/pygments/styles/paraiso_dark.py index 25561395..0c98f005 100644 --- a/pygments/styles/paraiso_dark.py +++ b/pygments/styles/paraiso_dark.py @@ -38,9 +38,6 @@ class ParaisoDarkStyle(Style): background_color = BACKGROUND highlight_color = SELECTION - background_color = BACKGROUND - highlight_color = SELECTION - styles = { # No corresponding class for the following: Text: FOREGROUND, # class: '' diff --git a/pygments/styles/paraiso_light.py b/pygments/styles/paraiso_light.py index 608d1734..4c9f1392 100644 --- a/pygments/styles/paraiso_light.py +++ b/pygments/styles/paraiso_light.py @@ -38,9 +38,6 @@ class ParaisoLightStyle(Style): background_color = BACKGROUND highlight_color = SELECTION - background_color = BACKGROUND - highlight_color = SELECTION - styles = { # No corresponding class for the following: Text: FOREGROUND, # class: '' diff --git a/requirements.txt b/requirements.txt index 49a627f2..7240ed5b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ pytest>=6.0 pyflakes pylint tox +wcag-contrast-ratio diff --git a/scripts/update_contrasts.py b/scripts/update_contrasts.py new file mode 100755 index 00000000..156bc5cb --- /dev/null +++ b/scripts/update_contrasts.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +""" + Updates tests/contrast/min_contrasts.json + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Whenever you have improved the minimum contrast of a style you should run + this script, so that the test_contrasts.py test prevents future degredations. +""" + +import os +import sys + +# always prefer Pygments from source if exists +srcpath = os.path.join(os.path.dirname(__file__), "..") +if os.path.isdir(os.path.join(srcpath, "pygments")): + sys.path.insert(0, srcpath) + +import tests.contrast.test_contrasts + +tests.contrast.test_contrasts.test_contrasts(fail_if_improved=False) +tests.contrast.test_contrasts.update_json() diff --git a/tests/contrast/min_contrasts.json b/tests/contrast/min_contrasts.json new file mode 100644 index 00000000..9867a3a1 --- /dev/null +++ b/tests/contrast/min_contrasts.json @@ -0,0 +1,43 @@ +{ + "default": 2.6, + "emacs": 2.4, + "friendly": 2.2, + "colorful": 2.2, + "autumn": 2.2, + "murphy": 1.4, + "manni": 1.4, + "material": 1.6, + "monokai": 1.4, + "perldoc": 2.7, + "pastie": 2.5, + "borland": 2.3, + "trac": 2.3, + "native": 3.0, + "fruity": 1.6, + "bw": 21.0, + "vim": 1.3, + "vs": 3.6, + "tango": 2.4, + "rrt": 1.0, + "xcode": 5.1, + "igor": 3.6, + "paraiso-light": 1.3, + "paraiso-dark": 1.3, + "lovelace": 3.5, + "algol": 3.5, + "algol_nu": 3.5, + "arduino": 2.6, + "rainbow_dash": 2.1, + "abap": 2.5, + "solarized-dark": 1.5, + "solarized-light": 1.0, + "sas": 4.2, + "stata": 2.4, + "stata-light": 2.4, + "stata-dark": 1.4, + "inkpot": 1.0, + "zenburn": 1.6, + "gruvbox-dark": 1.4, + "gruvbox-light": 3.2, + "dracula": 1.4 +}
\ No newline at end of file diff --git a/tests/contrast/test_contrasts.py b/tests/contrast/test_contrasts.py new file mode 100644 index 00000000..82694bc5 --- /dev/null +++ b/tests/contrast/test_contrasts.py @@ -0,0 +1,97 @@ +""" + Contrast Tests + ~~~~~~~~~~ + + Pygments styles should be accessible to people with suboptimal vision. + This test ensures that the minimum contrast of styles does not degrade, + and that every rule of a new style fulfills the WCAG AA standard.[1] + + [1]: https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html +""" + +import json +import os +import sys + +import pygments.styles +import pygments.token +import wcag_contrast_ratio + +JSON_FILENAME = os.path.join(os.path.dirname(__file__), "min_contrasts.json") +WCAG_AA_CONTRAST = 4.5 + + +def hex2rgb(hexstr): + hexstr = hexstr.lstrip("#") + r = int(hexstr[:2], 16) / 255 + g = int(hexstr[2:4], 16) / 255 + b = int(hexstr[4:], 16) / 255 + return (r, g, b) + + +def get_style_contrasts(style_cls): + return [ + ( + round( + wcag_contrast_ratio.rgb( + hex2rgb(style["bgcolor"] or style_cls.background_color), + hex2rgb(style["color"] or "#000000") + # we default to black because browsers also do + ), + 1, + ), + ttype, + ) + for ttype, style in style_cls.list_styles() + if ttype != pygments.token.Whitespace + ] + + +def builtin_styles(): + for style_name in pygments.styles.STYLE_MAP: + yield (style_name, pygments.styles.get_style_by_name(style_name)) + + +def update_json(): + with open(JSON_FILENAME, "w") as f: + json.dump( + { + name: min(x[0] for x in get_style_contrasts(style)) + for name, style in builtin_styles() + }, + f, + indent=2, + ) + + +def test_contrasts(fail_if_improved=True): + with open(JSON_FILENAME) as f: + previous_contrasts = json.load(f) + + for style_name in pygments.styles.STYLE_MAP: + style = pygments.styles.get_style_by_name(style_name) + contrasts = get_style_contrasts(style) + min_contrast = min([x[0] for x in contrasts]) + + bar = previous_contrasts.get(style_name, WCAG_AA_CONTRAST) + + assert not min_contrast < bar, ( + "contrast degradation for style '{}'\n" + "The following rules have a contrast lower than the required {}:\n\n" + "{}\n" + ).format( + style_name, + bar, + "\n".join( + [ + "* {:.2f} {}".format(contrast, ttype) + for contrast, ttype in contrasts + if contrast < bar + ] + ), + ) + + if fail_if_improved: + assert ( + not min_contrast > bar + ), "congrats, you improved a contrast! please run ./scripts/update_contrasts.py" @@ -5,6 +5,7 @@ envlist = py35, py36, py37, py38, py39, py310, pypy3, lint deps = pytest pytest-cov + wcag-contrast-ratio commands = pytest {posargs} |