diff --git a/changedetectionio/blueprint/tags/__init__.py b/changedetectionio/blueprint/tags/__init__.py
index 7c9fffb0..97361550 100644
--- a/changedetectionio/blueprint/tags/__init__.py
+++ b/changedetectionio/blueprint/tags/__init__.py
@@ -29,6 +29,7 @@ def construct_blueprint(datastore: ChangeDetectionStore):
form=add_form,
generate_tag_colors=processors.generate_processor_badge_colors,
tag_count=tag_count,
+ wcag_text_color=processors.wcag_text_color,
)
return output
diff --git a/changedetectionio/blueprint/tags/form.py b/changedetectionio/blueprint/tags/form.py
index e8b3e192..61b8bdb8 100644
--- a/changedetectionio/blueprint/tags/form.py
+++ b/changedetectionio/blueprint/tags/form.py
@@ -12,12 +12,9 @@ class group_restock_settings_form(restock_settings_form):
overrides_watch = BooleanField('Activate for individual watches in this tag/group?', default=False)
url_match_pattern = StringField('Auto-apply to watches with URLs matching',
render_kw={"placeholder": "e.g. *://example.com/* or github.com/myorg"})
+ tag_colour = StringField('Tag colour', default='')
class SingleTag(Form):
name = StringField('Tag name', [validators.InputRequired()], render_kw={"placeholder": "Name"})
save_button = SubmitField('Save', render_kw={"class": "pure-button pure-button-primary"})
-
-
-
-
diff --git a/changedetectionio/blueprint/tags/templates/edit-tag.html b/changedetectionio/blueprint/tags/templates/edit-tag.html
index 03305126..ecdc0bbb 100644
--- a/changedetectionio/blueprint/tags/templates/edit-tag.html
+++ b/changedetectionio/blueprint/tags/templates/edit-tag.html
@@ -57,6 +57,32 @@
{% endif %}
+
+
diff --git a/changedetectionio/blueprint/tags/templates/groups-overview.html b/changedetectionio/blueprint/tags/templates/groups-overview.html
index 2c627889..30bcf5d2 100644
--- a/changedetectionio/blueprint/tags/templates/groups-overview.html
+++ b/changedetectionio/blueprint/tags/templates/groups-overview.html
@@ -7,6 +7,9 @@
{%- for uuid, tag in available_tags -%}
{%- if tag and tag.title -%}
{%- set class_name = tag.title|sanitize_tag_class -%}
+{%- if tag.get('tag_colour') -%}
+.watch-tag-list.tag-{{ class_name }} { background-color: {{ tag.tag_colour }}; color: {{ wcag_text_color(tag.tag_colour) }}; }
+{%- else -%}
{%- set colors = generate_tag_colors(tag.title) -%}
.watch-tag-list.tag-{{ class_name }} {
background-color: {{ colors['light']['bg'] }};
@@ -17,6 +20,7 @@ html[data-darkmode="true"] .watch-tag-list.tag-{{ class_name }} {
color: {{ colors['dark']['color'] }};
}
{%- endif -%}
+{%- endif -%}
{%- endfor -%}
diff --git a/changedetectionio/blueprint/watchlist/__init__.py b/changedetectionio/blueprint/watchlist/__init__.py
index ebdfa627..ea879ac6 100644
--- a/changedetectionio/blueprint/watchlist/__init__.py
+++ b/changedetectionio/blueprint/watchlist/__init__.py
@@ -92,6 +92,7 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe
extra_classes='has-queue' if not update_q.empty() else '',
form=form,
generate_tag_colors=processors.generate_processor_badge_colors,
+ wcag_text_color=processors.wcag_text_color,
guid=datastore.data['app_guid'],
has_proxies=proxy_list,
hosted_sticky=os.getenv("SALTED_PASS", False) == False,
diff --git a/changedetectionio/blueprint/watchlist/templates/watch-overview.html b/changedetectionio/blueprint/watchlist/templates/watch-overview.html
index 55fbb9e0..70012e09 100644
--- a/changedetectionio/blueprint/watchlist/templates/watch-overview.html
+++ b/changedetectionio/blueprint/watchlist/templates/watch-overview.html
@@ -71,6 +71,13 @@ document.addEventListener('DOMContentLoaded', function() {
{%- for uuid, tag in tags -%}
{%- if tag and tag.title -%}
{%- set class_name = tag.title|sanitize_tag_class -%}
+{%- if tag.get('tag_colour') -%}
+.button-tag.tag-{{ class_name }},
+.watch-tag-list.tag-{{ class_name }} {
+ background-color: {{ tag.tag_colour }};
+ color: {{ wcag_text_color(tag.tag_colour) }};
+}
+{%- else -%}
{%- set colors = generate_tag_colors(tag.title) -%}
.button-tag.tag-{{ class_name }} {
background-color: {{ colors['light']['bg'] }};
@@ -92,6 +99,7 @@ html[data-darkmode="true"] .watch-tag-list.tag-{{ class_name }} {
color: {{ colors['dark']['color'] }};
}
{%- endif -%}
+{%- endif -%}
{%- endfor -%}
diff --git a/changedetectionio/processors/__init__.py b/changedetectionio/processors/__init__.py
index fbb448aa..032245f3 100644
--- a/changedetectionio/processors/__init__.py
+++ b/changedetectionio/processors/__init__.py
@@ -341,6 +341,18 @@ def get_processor_descriptions():
return descriptions
+def wcag_text_color(hex_bg: str) -> str:
+ """Return #000000 or #ffffff for maximum WCAG contrast against hex_bg."""
+ hex_bg = hex_bg.lstrip('#')
+ if len(hex_bg) != 6:
+ return '#000000'
+ r, g, b = (int(hex_bg[i:i+2], 16) / 255 for i in (0, 2, 4))
+ def lin(c):
+ return c / 12.92 if c <= 0.04045 else ((c + 0.055) / 1.055) ** 2.4
+ L = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b)
+ return '#000000' if L > 0.179 else '#ffffff'
+
+
def generate_processor_badge_colors(processor_name):
"""
Generate consistent colors for a processor badge based on its name.