diff --git a/tests.py b/tests.py
index 7f6ba9a22..1dad076c2 100644
--- a/tests.py
+++ b/tests.py
@@ -148,6 +148,7 @@ from content import remove_long_words
from content import replace_content_duplicates
from content import remove_text_formatting
from content import remove_html_tag
+from theme import get_themes_list
from theme import update_default_themes_list
from theme import set_css_param
from theme import scan_themes_for_scripts
@@ -182,6 +183,7 @@ from blocking import load_cw_lists
from blocking import add_cw_from_lists
from happening import dav_month_via_server
from happening import dav_day_via_server
+from webapp_theme_designer import color_contrast
TEST_SERVER_GROUP_RUNNING = False
@@ -7092,6 +7094,44 @@ def _test_diff_content() -> None:
assert html_str == expected
+def _test_color_contrast_value(base_dir: str) -> None:
+ print('test_color_contrast_value')
+ minimum_color_contrast = 4.5
+ background = 'black'
+ foreground = 'white'
+ contrast = color_contrast(background, foreground)
+ assert contrast
+ assert contrast > 20
+ assert contrast < 22
+ foreground = 'grey'
+ contrast = color_contrast(background, foreground)
+ assert contrast
+ assert contrast > 5
+ assert contrast < 6
+ themes = get_themes_list(base_dir)
+ for theme_name in themes:
+ theme_filename = base_dir + '/theme/' + theme_name + '/theme.json'
+ if not os.path.isfile(theme_filename):
+ continue
+ theme_json = load_json(theme_filename)
+ if not theme_json:
+ continue
+ if not theme_json.get('main-fg-color'):
+ continue
+ if not theme_json.get('main-bg-color'):
+ continue
+ foreground = theme_json['main-fg-color']
+ background = theme_json['main-bg-color']
+ contrast = color_contrast(background, foreground)
+ if contrast is None:
+ continue
+ if contrast < minimum_color_contrast:
+ print('Theme ' + theme_name + ' has not enough color contrast ' +
+ str(contrast) + ' < ' + str(minimum_color_contrast))
+ assert contrast >= minimum_color_contrast
+ print('Color contrast is ok for all themes')
+
+
def run_all_tests():
base_dir = os.getcwd()
print('Running tests...')
@@ -7109,6 +7149,7 @@ def run_all_tests():
_test_checkbox_names()
_test_thread_functions()
_test_functions()
+ _test_color_contrast_value(base_dir)
_test_diff_content()
_test_bold_reading()
_test_published_to_local_timezone()
diff --git a/webapp_theme_designer.py b/webapp_theme_designer.py
index 7fb18299e..b88752b20 100644
--- a/webapp_theme_designer.py
+++ b/webapp_theme_designer.py
@@ -331,3 +331,52 @@ def html_theme_designer(css_cache: {}, base_dir: str,
theme_form += '\n'
theme_form += html_footer()
return theme_form
+
+
+def _relative_luminance(color: str) -> float:
+ """ Returns the relative luminance for the given color
+ """
+ color = color.lstrip('#')
+ rgb = list(int(color[i:i+2], 16) for i in (0, 2, 4))
+ srgb = (
+ rgb[0] / 255.0,
+ rgb[1] / 255.0,
+ rgb[2] / 255.0
+ )
+ if srgb[0] <= 0.03928:
+ rgb[0] = srgb[0] / 12.92
+ else:
+ rgb[0] = pow(((srgb[0] + 0.055) / 1.055), 2.4)
+ if srgb[1] <= 0.03928:
+ rgb[1] = srgb[1] / 12.92
+ else:
+ rgb[1] = pow(((srgb[1] + 0.055) / 1.055), 2.4)
+ if srgb[2] <= 0.03928:
+ rgb[2] = srgb[2] / 12.92
+ else:
+ rgb[2] = pow(((srgb[2] + 0.055) / 1.055), 2.4)
+
+ return \
+ 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]
+
+
+def color_contrast(background: str, foreground: str) -> float:
+ """returns the color contrast
+ """
+ if not background.startswith('#'):
+ if color_to_hex.get(background):
+ background = color_to_hex[background]
+ else:
+ return None
+ if not foreground.startswith('#'):
+ if color_to_hex.get(foreground):
+ foreground = color_to_hex[foreground]
+ else:
+ return None
+ background_luminance = _relative_luminance(background)
+ foreground_luminance = _relative_luminance(foreground)
+ if background_luminance > foreground_luminance:
+ return (0.05 + background_luminance) / (0.05 + foreground_luminance)
+ else:
+ return (0.05 + foreground_luminance) / (0.05 + background_luminance)
+ return None
diff --git a/webapp_timeline.py b/webapp_timeline.py
index acd00fbbb..030848548 100644
--- a/webapp_timeline.py
+++ b/webapp_timeline.py
@@ -395,14 +395,18 @@ def _html_timeline_end(base_dir: str, nickname: str, domain_full: str,
authorized, True, theme,
default_timeline, access_keys)
tl_str += '
' + \
- right_column_str + ' | \n'
- tl_str += ' \n'
+ 'id="newswire" tabindex="-1">\n' + \
+ ' \n' + \
+ ' \n' + \
+ ' \n'
_log_timeline_timing(enable_timing_log, timeline_start_time, box_name, '9')
tl_str += ' \n'
tl_str += '\n'
+ tl_str += '\n'
return tl_str
@@ -768,6 +772,7 @@ def html_timeline(css_cache: {}, default_timeline: str,
# start the timeline
tl_str += \
+ '\n' + \
'\n' + \
' \n' + \
' \n' + \
@@ -787,8 +792,12 @@ def html_timeline(css_cache: {}, default_timeline: str,
True, False, theme, access_keys,
shared_items_federated_domains)
tl_str += ' ' + \
- left_column_str + ' | \n'
+ 'id="links" tabindex="-1">\n' + \
+ ' \n' + \
+ ' \n'
+
# center column containing posts
tl_str += ' \n'
|