bookwyrm icon indicating copy to clipboard operation
bookwyrm copied to clipboard

Automatic Light/Dark Theme from prefers-color-scheme

Open FIGBERT opened this issue 2 years ago • 8 comments

The light and dark themes, as they exist currently, are wonderful. However, for those of us who set our theme dynamically, it is frustrating to have to manually adjust the theme in settings whenever the sun (dis)appears.

It would be wonderful if we could add an Automatic (Light/Dark) theme that utilizes either some media queries in CSS for prefers-color-scheme or, if that's not possible, even some client-side JS.

More than happy to help implement this, would just need some pointers as to where to start.

FIGBERT avatar May 11 '22 16:05 FIGBERT

I would love for this to be a thing, and even more delighted to have you implement it.

The theme is determined in the context processor, which has access to the HTTP request object. That should have the prefers-color-scheme in the request.headers object.

mouse-reeve avatar May 14 '22 15:05 mouse-reeve

Hi @mouse-reeve! What I believe @FIGBERT is referring to is the CSS prefers-color-scheme media declaration (MDN link). That information is not sent in the headers, but used by the browser when painting the page.

If the code for what CSS theme is served up is entirely determined by the backend, I suspect you'll need the JS route, perhaps via setting a cookie on login or even page load that queries the preferred state via code like this:

const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)'); // note double parens
if (darkModeQuery.matches) {
   // set cookie here!
}

I don't speak Python so I can't comment on your backend, but you may be able to use that. I will add that modern CSS techniques I see used tend to avoid relying on the server for this reason, and instead set colors via CSS variables and media queries such as the above.

Hope this helps!

ChaosExAnima avatar May 14 '22 19:05 ChaosExAnima

I see! Thank you, I just assumed it was an HTTP header without checking. The backend does indeed determine which theme is presented, which ends up rendered into the template here: https://github.com/bookwyrm-social/bookwyrm/blob/main/bookwyrm/templates/layout.html#L11.

Doing it as a cookie set in javascript or a media query both seem like good solutions to me, although I haven't fully worked out in my head how the heirarchy of theme preferences would work. By default, there are light and dark themes, but instances can have any number of arbitrary themes as their default server-wide or available to users, so the logic needs to prefer:

  1. A logged-in user's selected theme (stored in the database)
  2. The instance's selected theme
  3. The browser's request for light or dark

mouse-reeve avatar May 14 '22 21:05 mouse-reeve

@mouse-reeve one way I'm seeing WordPress approach things with their FSE system is split apart the theme concept into two elements:

  • the HTML + CSS
  • variables that determine typography, UI spacing, color, images, etc

The latter is dynamically inserted via a style tag in the header of the site before the main CSS is loaded. The CSS then reads that and cascades accordingly. For your use case I immediately think of something like this:

  • Themes declare variables (via JSON or similar), but can react to elements like browser preferences
  • Instances have the themes as options
  • Users pick the theme as well as the variables
  • The code loads the user preferences, the theme CSS/templates, and outputs accordingly.

That could allow some pretty significant customization using some basic HTML input elements, and would be fun. I know as a sci-fi nerd myself I'd definitely want a nebula background! 👾

Of course this would be a big lift, so just offering a suggestion more than anything here. 🙂

ChaosExAnima avatar May 14 '22 21:05 ChaosExAnima

That's a very interesting suggestion, but it sounds like it would be a complete overhaul of the existing theme system and maybe how the templates work, so I think it's out of scope for this ticket :)

mouse-reeve avatar May 14 '22 21:05 mouse-reeve

This has been on my mind. Possibly counter-intuitively, I think the simplest way to do it would be to have three bookwyrm themes: system (default), light and dark. system would effectively be the other two combined, with a prefers-color-scheme check to determine which rules to use. This would allow for legacy functionality of explicitly choosing a light or dark theme, but make the default simply pick up the system preference.

hughrun avatar Nov 07 '23 07:11 hughrun

FYI, Bulma has a banner teasing Bulma v1 with "Dark Mode" as a new feature. I can't yet find what this means exactly, but if it uses prefers-color-scheme we may want to use it when v1 comes out.

Minnozz avatar Jan 20 '24 15:01 Minnozz

Bulma v1 has been released, including (automatic) dark mode. The setup with three themes (system/light/dark) that @hughrun describes is possible.

The biggest change for us is that Bulma is now written in Dart Sass instead of node-sass. We use the django-sass-processor and libsass Python packages, which are based on the deprecated libsass. We may have to change some things about how we compile our themes to be able to update to Bulma v1.

Minnozz avatar Mar 24 '24 21:03 Minnozz