Repository: WeblateOrg/weblate

Issue:
Add night theme
**Is your feature request related to a problem? Please describe.**
I like to work at night. It would be nice to give my eyes some rest and be able to switch between day and night mode at any time.

**Describe the solution you'd like**
Possibility to switch between these modes.

**Describe alternatives you've considered**
//

**Additional context**
//



Discussion:
Day/Night mode switcher position proposal:
![daynight](https://user-images.githubusercontent.com/33127074/63413578-477b1600-c3fa-11e9-81ab-af63118701e0.png)

+1
I agree. I have to decrease my brightness to the lowest level but even that isn't good. Much better would be a dark theme that you could enable right in the Weblate without any extension.
Not yet, nobody contributed it so far.
@Kyuuu If you aren't using custom CSS, or a plugin to do it. Isn't just the next website going to look too bright?
Having every page implement this, even if they all did it the same way, is not a good solution. Android went through this problem with apps and solved it with a system setting https://developer.android.com/guide/topics/ui/look-and-feel/darktheme
Just want to note the existence of the `prefers-color-scheme` feature of CSS to check the user's system preference on light/dark mode.

> The prefers-color-scheme CSS media feature is used to detect if the user has requested the system use a light or dark color theme.
> \- https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme

When/if this gets implemented, it would be nice if Weblate can default to the system theme if not explicitly set in Weblate itself.
Would love to see this. So my users don't have to have bleeding eyes, nor stop being the night owls they are..
Hi everyone 👋,

I just generated a **dark mode** CSS stylesheet for weblate using the Dark Reader browser extension.

I haven't dealt with weblate's source code so far. So unfortunately I have no knowledge considering the JavaScript used.

But generally speaking I'd solve the integration of the light/dark mode switch using either a server-side variable stored with the user's profile information or client-side using preferable local storage or cookies.

Please see the attached CSS file. Please note that the file extension is `.txt` instead of `.css` as for unkown reasons the upload of the latter is blocked by GitHub.

[📎DarkReader-weblate-dark-mode.css](https://github.com/WeblateOrg/weblate/files/7720011/DarkReader-weblate-dark-mode.css.txt)

Regards,

Jean-Luc
The generation of high contrast or sepia colored themes using the Dark Reader browser extension would be possible as well. So this might improve the accessibility of weblate in general.
I would be very grateful if @nijel could look into this any time soon.

Btw just wanted to express my gratitude for maintaining weblate. It's a awesome piece of software.
Thanks for sharing the CSS, unfortunately full integration is not that easy.

* The dark mode CSS needs to be updated when doing other CSS changes. Ideally, this would be automated, otherwise it will be forgotten.
* The CSS change does not cover some icons in the UI making them really low contrast: ![image](https://user-images.githubusercontent.com/212189/146375179-f85b27a6-cc20-4344-baab-71ae520aabc4.png)
Switching to SVG/Font based icons instead of images is fairly simple and there's also a way to invert the colors of an image with CSS.

Regarding inverting colors:
https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/invert

Regarding SVG/Fonts (There are many open source projects such as Feather, [etc.](https://www.designer-daily.com/6-great-open-source-svg-icon-libraries-you-should-check-out-now-108088)), there are some equivalent icons:
Font Awesome: [link-simple](https://fontawesome.com/icons/link-simple), [crown](https://fontawesome.com/icons/crown), [lock](https://fontawesome.com/icons/lock), [eye](https://fontawesome.com/icons/eye), etc.
There are some in the other projects but these are quite different than what is already used.

The simplest approach is to create root element in CSS for the dark/light mode and switch when necessary.

I created an example here:
https://github.com/Maagan-Michael/dud-design

Thanks :)
> The CSS change does not cover some icons in the UI making them really low contrast

I haven't checked the code, but I would assume those icons are SVGs.
If you just change the `fill` attribute to the value `currentColor` (supported in all major browsers) it'll inherit the `color` (font color), from there it'll be easy to override between themes.

Should be an easy fix for the icons, and is not a hack. (This is how things like FontAwesome achieve setting the color of their icons through CSS.)
Yes, using fill is probably the way to go. We already use that for coloring SVGs (and all icons are SVGs). It was just a statement that CSS in https://github.com/WeblateOrg/weblate/issues/2969#issuecomment-994826966 does not cover this.
Any update on this? That white mode is killing me :/

Resolving patch:
diff --git a/weblate/accounts/forms.py b/weblate/accounts/forms.py
--- a/weblate/accounts/forms.py
+++ b/weblate/accounts/forms.py
@@ -262,6 +262,7 @@ class UserSettingsForm(ProfileBaseForm):
     class Meta:
         model = Profile
         fields = (
+            "theme",
             "hide_completed",
             "translate_mode",
             "zen_mode",
diff --git a/weblate/accounts/migrations/0025_profile_theme.py b/weblate/accounts/migrations/0025_profile_theme.py
new file mode 100644
--- /dev/null
+++ b/weblate/accounts/migrations/0025_profile_theme.py
@@ -0,0 +1,30 @@
+# Copyright © Michal Čihař <michal@weblate.org>
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Generated by Django 4.2.1 on 2023-06-08 12:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("accounts", "0024_rename_be_latn"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="profile",
+            name="theme",
+            field=models.CharField(
+                choices=[
+                    ("auto", "Sync with system"),
+                    ("light", "Light"),
+                    ("dark", "Dark"),
+                ],
+                default="auto",
+                max_length=10,
+                verbose_name="Theme",
+            ),
+        ),
+    ]
diff --git a/weblate/accounts/models.py b/weblate/accounts/models.py
--- a/weblate/accounts/models.py
+++ b/weblate/accounts/models.py
@@ -17,7 +17,7 @@
 from django.dispatch import receiver
 from django.utils import timezone
 from django.utils.functional import cached_property
-from django.utils.translation import get_language, gettext, gettext_lazy
+from django.utils.translation import get_language, gettext, gettext_lazy, pgettext_lazy
 from rest_framework.authtoken.models import Token
 from social_django.models import UserSocialAuth
 
@@ -395,7 +395,16 @@ class Profile(models.Model):
     translated = models.IntegerField(default=0, db_index=True)
     uploaded = models.IntegerField(default=0, db_index=True)
     commented = models.IntegerField(default=0, db_index=True)
-
+    theme = models.CharField(
+        max_length=10,
+        verbose_name=gettext_lazy("Theme"),
+        default="auto",
+        choices=(
+            ("auto", pgettext_lazy("Theme selection", "Sync with system")),
+            ("light", pgettext_lazy("Theme selection", "Light")),
+            ("dark", pgettext_lazy("Theme selection", "Dark")),
+        ),
+    )
     hide_completed = models.BooleanField(
         verbose_name=gettext_lazy("Hide completed translations on the dashboard"),
         default=False,
@@ -663,6 +672,7 @@ def dump_object(obj, *attrs):
                 "translated",
                 "uploaded",
                 "hide_completed",
+                "theme",
                 "secondary_in_zen",
                 "hide_source_secondary",
                 "editor_link",


Test patch:
diff --git a/weblate/accounts/tests/test_views.py b/weblate/accounts/tests/test_views.py
--- a/weblate/accounts/tests/test_views.py
+++ b/weblate/accounts/tests/test_views.py
@@ -373,6 +373,7 @@ def test_profile(self):
                 "translate_mode": Profile.TRANSLATE_FULL,
                 "zen_mode": Profile.ZEN_VERTICAL,
                 "nearby_strings": 10,
+                "theme": "auto",
             },
         )
         self.assertRedirects(response, reverse("profile"))
@@ -392,6 +393,7 @@ def test_profile_dashboard(self):
                 "translate_mode": Profile.TRANSLATE_FULL,
                 "zen_mode": Profile.ZEN_VERTICAL,
                 "nearby_strings": 10,
+                "theme": "auto",
             },
         )
         self.assertContains(response, "Select a valid choice.")