From 12dfb6f70f047238d025e86de867c0d6cefbfb2e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 17 May 2022 20:41:09 +0100 Subject: [PATCH 1/4] Page sections --- webapp_timeline.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/webapp_timeline.py b/webapp_timeline.py index acd00fbbb..c7918016f 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -380,7 +380,7 @@ def _html_timeline_end(base_dir: str, nickname: str, domain_full: str, tl_str = ' \n' # end of column-center - tl_str += ' \n' + tl_str += ' \n \n' # right column right_column_str = \ @@ -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' + \ + right_column_str + \ + '
\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,10 +792,15 @@ def html_timeline(css_cache: {}, default_timeline: str, True, False, theme, access_keys, shared_items_federated_domains) tl_str += ' \n' + 'id="links" tabindex="-1">\n' + \ + '
\n' + \ + left_column_str + \ + '
\n' + \ + ' \n' + # center column containing posts - tl_str += ' \n' + \ ' \n' @@ -793,9 +793,9 @@ def html_timeline(css_cache: {}, default_timeline: str, shared_items_federated_domains) tl_str += ' \n' # center column containing posts From 64b401da3d40213cca7a8867aa7967d51c0bfb37 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 17 May 2022 21:10:48 +0100 Subject: [PATCH 3/4] Don't need middle section --- webapp_timeline.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/webapp_timeline.py b/webapp_timeline.py index 073307996..030848548 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -380,7 +380,7 @@ def _html_timeline_end(base_dir: str, nickname: str, domain_full: str, tl_str = ' \n' # end of column-center - tl_str += ' \n \n' + tl_str += ' \n' # right column right_column_str = \ @@ -799,8 +799,7 @@ def html_timeline(css_cache: {}, default_timeline: str, ' \n' # center column containing posts - tl_str += '
' + \ - left_column_str + ' \n' + tl_str += ' \n' + \ + '
\n' if not full_width_tl_button_header: tl_str += \ From 4754daed8e52df98ed9f0cde712444e15e168ec4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 17 May 2022 21:06:13 +0100 Subject: [PATCH 2/4] Make left and right columns into asides --- webapp_timeline.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/webapp_timeline.py b/webapp_timeline.py index c7918016f..073307996 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -396,9 +396,9 @@ def _html_timeline_end(base_dir: str, nickname: str, domain_full: str, default_timeline, access_keys) tl_str += '
\n' + \ - '
\n' + \ + '
\n' + \ + ' \n' + \ '
\n' + \ - '
\n' + \ + '
\n' + \ + ' \n' + \ '
\n' + \ - '
\n' + tl_str += '
\n' if not full_width_tl_button_header: tl_str += \ From 27ea5e560cc5f52e82447d86a140f1a28081a12e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Wed, 18 May 2022 17:06:26 +0100 Subject: [PATCH 4/4] Test all themes for color contrast --- tests.py | 41 +++++++++++++++++++++++++++++++++ webapp_theme_designer.py | 49 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) 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