summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--doc/_templates/indexsidebar.html5
-rw-r--r--doc/_themes/pygments14/layout.html2
-rw-r--r--doc/_themes/pygments14/localtoc.html17
-rw-r--r--doc/_themes/pygments14/relations.html25
-rw-r--r--doc/_themes/pygments14/static/pygments14.css_t44
-rw-r--r--doc/_themes/pygments14/theme.conf2
-rw-r--r--pygments/styles/paraiso_dark.py3
-rw-r--r--pygments/styles/paraiso_light.py3
-rw-r--r--requirements.txt1
-rwxr-xr-xscripts/update_contrasts.py21
-rw-r--r--tests/contrast/min_contrasts.json43
-rw-r--r--tests/contrast/test_contrasts.py97
-rw-r--r--tox.ini1
14 files changed, 247 insertions, 18 deletions
diff --git a/AUTHORS b/AUTHORS
index d9eba8ad..f2f65848 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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">
&copy; 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"
diff --git a/tox.ini b/tox.ini
index aa26979c..dea9ee41 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,6 +5,7 @@ envlist = py35, py36, py37, py38, py39, py310, pypy3, lint
deps =
pytest
pytest-cov
+ wcag-contrast-ratio
commands = pytest {posargs}