diff --git a/weblate/configuration/tests.py b/weblate/configuration/tests.py index ea681b23c10c..fd7b58d3247f 100644 --- a/weblate/configuration/tests.py +++ b/weblate/configuration/tests.py @@ -33,3 +33,11 @@ def test_cache(self) -> None: CustomCSSView.drop_cache() response = self.client.get(reverse("css-custom")) self.assertEqual(response.content.decode().strip(), "") + + def test_split_colors(self): + self.assertEqual([None, None], CustomCSSView.split_colors("")) + self.assertEqual([None, None], CustomCSSView.split_colors(None)) + self.assertEqual(["#ffffff"], CustomCSSView.split_colors("#ffffff")) + self.assertEqual( + ["#ffffff", "#000000"], CustomCSSView.split_colors("#ffffff,#000000") + ) diff --git a/weblate/configuration/views.py b/weblate/configuration/views.py index 0159388849c8..a27ff492b6c1 100644 --- a/weblate/configuration/views.py +++ b/weblate/configuration/views.py @@ -26,6 +26,12 @@ class CustomCSSView(TemplateView): template_name = "configuration/custom.css" cache_key = "css:custom" + @classmethod + def split_colors(cls, hex_color_string): + if hex_color_string: + return hex_color_string.split(",") + return [None, None] + @classmethod def get_css(cls, request: AuthenticatedHttpRequest): # Request level caching @@ -35,10 +41,30 @@ def get_css(cls, request: AuthenticatedHttpRequest): # Site level caching css = cache.get(cls.cache_key) if css is None: - css = render_to_string( - "configuration/custom.css", - Setting.objects.get_settings_dict(SettingCategory.UI), - ).strip() + settings = Setting.objects.get_settings_dict(SettingCategory.UI) + split_colors = cls.split_colors + # fmt: off + custom_theme_settings = { + "header_color_light": split_colors(settings.get("header_color"))[0], + "header_color_dark": split_colors(settings.get("header_color"))[1], + "header_text_color_light": split_colors(settings.get("header_text_color"))[0], + "header_text_color_dark": split_colors(settings.get("header_text_color"))[1], + "navi_color_light": split_colors(settings.get("navi_color"))[0], + "navi_color_dark": split_colors(settings.get("navi_color"))[1], + "navi_text_color_light": split_colors(settings.get("navi_text_color"))[0], + "navi_text_color_dark": split_colors(settings.get("navi_text_color"))[1], + "focus_color_light": split_colors(settings.get("focus_color"))[0], + "focus_color_dark": split_colors(settings.get("focus_color"))[1], + "hover_color_light": split_colors(settings.get("hover_color"))[0], + "hover_color_dark": split_colors(settings.get("hover_color"))[1], + "hide_footer": settings.get("hide_footer"), + "page_font": settings.get("page_font"), + "brand_font": settings.get("brand_font"), + "enforce_hamburger": settings.get("enforce_hamburger"), + } + # fmt: on + + css = render_to_string(cls.template_name, custom_theme_settings).strip() cache.set(cls.cache_key, css, 24 * 3600) request.weblate_custom_css = css return css diff --git a/weblate/templates/configuration/custom.css b/weblate/templates/configuration/custom.css index 4f8164a6ba66..2fea7713edfb 100644 --- a/weblate/templates/configuration/custom.css +++ b/weblate/templates/configuration/custom.css @@ -1,53 +1,127 @@ -{% if header_color %} +{% comment %} Light theme color variables {% endcomment %} +{% if header_color_light %} +body { +--header-color: {{ header_color_light }}; +} +{% endif %} + +{% if header_text_color_light %} +body { +--header-text-color: {{ header_text_color_light }}; +} +{% endif %} + +{% if navi_color_light %} +body { +--navi-color: {{ navi_color_light }}; +} +{% endif %} + +{% if navi_text_color_light %} +body { +--navi-text-color: {{ navi_text_color_light }}; +} +{% endif %} + +{% if focus_color_light %} +body { +--focus-color: {{ focus_color_light }}; +} +{% endif %} + +{% if hover_color_light %} +body { +--hover-color: {{ hover_color_light }}; +} +{% endif %} + +{% comment %} Dark theme color variables {% endcomment %} +{% if header_color_dark %} +body[data-theme="dark"] { +--header-color: {{ header_color_dark }}; +} +{% endif %} + +{% if header_text_color_dark %} +body[data-theme="dark"] { +--header-text-color: {{ header_text_color_dark }}; +} +{% endif %} + +{% if navi_color_dark %} +body[data-theme="dark"] { +--navi-color: {{ navi_color_dark }}; +} +{% endif %} + +{% if navi_text_color_dark %} +body[data-theme="dark"] { +--navi-text-color: {{ navi_text_color_dark }}; +} +{% endif %} + +{% if focus_color_dark %} +body[data-theme="dark"] { + --focus-color: {{ focus_color_dark }}; +} +{% endif %} + +{% if hover_color_dark %} +body[data-theme="dark"] { + --hover-color: {{ hover_color_dark }}; +} +{% endif %} + +{% if header_color_light %} .navbar-inverse { - background-color: {{ header_color }}; + background-color: var(--header-color); } {% endif %} -{% if header_text_color %} +{% if header_text_color_light %} .navbar-inverse .navbar-brand, .navbar-inverse .navbar-nav > li > a { - color: {{ header_text_color }}; + color: var(--header-text-color); } .navbar a path { - fill: {{ navi_text_color }}; + fill: var(--navi-text-color); } {% endif %} -{% if navi_color %} +{% if navi_color_light %} .sort-cell:hover { - color: {{ navi_color }}; + color: var(--navi-color); } .nav-pills > li > a, .btn-default { - color: {{ navi_color }}; + color: var(--navi-color); } .btn-link:hover path { - fill: {{ navi_color }}; + fill: var(--navi-color); } .green path, .btn-default path { - fill: {{ navi_color }}; + fill: var(--navi-color); } .btn-group-settings a:hover { - color: {{ navi_color }}; + color: var(--navi-color); } .format-item span { - background-color: {{ navi_color }}; + background-color: var(--navi-color); } a { - color: {{ navi_color }}; + color: var(--navi-color); } .btn-info { - color: {{ navi_color }}; + color: var(--navi-color);}; } .btn-info:focus, .btn-info.focus { - color: {{ navi_color }}; + color: var(--navi-color); } .btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { - color: {{ navi_color }}; + color: var(--navi-color); } .btn-info:active:hover, .btn-info.active:hover, @@ -58,46 +132,46 @@ .btn-info:active.focus, .btn-info.active.focus, .open > .dropdown-toggle.btn-info.focus { - color: {{ navi_color }}; + color: var(--navi-color); } .btn-link { - color: {{ navi_color }}; + color: var(--navi-color); } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { - border-color: {{ navi_color }}; + border-color: var(--navi-color); } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { - background-color: {{ navi_color }}; + background-color: var(--navi-color); } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { - background-color: {{ navi_color }}; + background-color: var(--navi-color); } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - background-color: {{ navi_color }}; + background-color: var(--navi-color); } } .pagination > li > a, .pagination > li > span { - color: {{ navi_color }}; + color: var(--navi-color); } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { - border-color: {{ navi_color }}; + border-color: var(--navi-color); } {% endif %} -{% if focus_color %} +{% if focus_color_light %} .btn-info { - border-color: {{ focus_color }}; + border-color: var(--focus-color); } .btn-info.disabled:hover, .btn-info[disabled]:hover, @@ -108,10 +182,10 @@ .btn-info.disabled.focus, .btn-info[disabled].focus, fieldset[disabled] .btn-info.focus { - border-color: {{ focus_color }}; + border-color: var(--focus-color); } .btn-warning { - background-color: {{ focus_color }}; + background-color: var(--focus-color); } .btn-warning.disabled:hover, .btn-warning[disabled]:hover, @@ -122,80 +196,80 @@ .btn-warning.disabled.focus, .btn-warning[disabled].focus, fieldset[disabled] .btn-warning.focus { - background-color: {{ focus_color }}; + background-color: var(--focus-color); } .btn-warning .badge { - color: {{ focus_color }}; + color: var(--focus-color); } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { - color: {{ focus_color }}; + color: var(--focus-color); } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { - color: {{ focus_color }}; + color: var(--focus-color); } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: {{ focus_color }}; + color: var(--focus-color); } } .navbar-inverse .navbar-link:hover { - color: {{ focus_color }}; + color: var(--focus-color); } .navbar-inverse .btn-link:hover, .navbar-inverse .btn-link:focus { - color: {{ focus_color }}; + color: var(--focus-color); } .btn-info { - border: 1px solid {{ focus_color }}; + border: 1px solid var(--focus-color); } .navbar .navbar-brand:focus path, .navbar .navbar-brand:active path, .navbar a:hover path { - fill: {{ focus_color }}; + fill: var(--focus-color); } .btn-primary:hover { - background-color: {{ focus_color }}; + background-color: var(--focus-color); } .btn-info:hover { - background-color: {{ focus_color }}; + background-color: var(--focus-color); } {% endif %} -{% if hover_color %} +{% if hover_color_light %} a:hover, a:focus { - color: {{ hover_color }}; + color: var(--hover-color); } .text-primary { - color: {{ hover_color }}; + color: var(--hover-color); } .bg-primary { - background-color: {{ hover_color }}; + background-color: var(--hover-color); } .btn-link:hover, .btn-link:focus { - color: {{ hover_color }}; + color: var(--hover-color); } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { - background-color: {{ hover_color }}; + background-color: var(--hover-color); } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { - background-color: {{ hover_color }}; + background-color: var(--hover-color); } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { - color: {{ hover_color }}; + color: var(--hover-color); } .pagination > .active > a, .pagination > .active > span, @@ -203,18 +277,18 @@ .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { - background-color: {{ hover_color }}; - border-color: {{ hover_color }}; + background-color: var(--hover-color); + border-color: var(--hover-color); } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { - background-color: {{ hover_color }}; - border-color: {{ hover_color }}; + background-color: var(--hover-color); + border-color: var(--hover-color); } .nav-pills > .active > a, .nav > .active > a:hover { - border: 1px solid {{ hover_color }}; + border: 1px solid var(--hover-color); } .btn-default:hover, .nav-pills > li > a:hover, @@ -222,18 +296,18 @@ .nav-pills .open > a, .nav-pills .open > a:hover, .nav-pills .open > a:focus { - color: {{ hover_color }}; + color: var(--hover-color); } .btn-default:hover path, .green.btn-link:hover path { - fill: {{ hover_color }}; + fill: var(--hover-color); } .format-item { - background-color: {{ hover_color }}; + background-color: var(--hover-color); } .nav-pills > .active > a, .nav > .active > a:hover { - border: 1px solid {{ hover_color }}; + border: 1px solid var(--hover-color); } .btn-default:hover, .nav-pills > li > a:hover, @@ -241,11 +315,10 @@ .nav-pills .open > a, .nav-pills .open > a:hover, .nav-pills .open > a:focus { - color: {{ hover_color }}; + color: var(--hover-color); } {% endif %} - {% if enforce_hamburger %} {% comment %}From https://stackoverflow.com/a/56229201/225718{% endcomment %} .navbar-header { @@ -328,8 +401,8 @@ } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - {% if focus_color %} - color: {{ focus_color }}; + {% if focus_color_light %} + color: var(--focus-color); {% else %} color: #2eccaa; {% endif %} @@ -339,8 +412,8 @@ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #ffffff; - {% if navi_color %} - background-color: {{ navi_color }}; + {% if navi_color_light %} + background-color: var(--navi-color); {% else %} background-color: #1fa385; {% endif %}