cockpit icon indicating copy to clipboard operation
cockpit copied to clipboard

dark mode

Open garrett opened this issue 5 years ago • 41 comments

Dark mode should be implemented for:

  • when someone is using Cockpit at night or in a dark room
  • accessibility reasons (low to medium migraine, various vision issues)
  • (as a preference for) people who like dark mode

As Cockpit has a lot of moving parts, it's important that we align everything toward one release so that Cockpit can be completely dark without flashes of light or pages that are bright

Proof of concept

  • PF4 dark mode for a subset of pages, to see what happens
    • are we able to add it to page.scss?

Minimum implementation for a Cockpit release

Dark mode works:

  • based on system theme
  • across all of base Cockpit
  • in all of our own plugins
  • at the login screen
  • in Cockpit Client
    • respects system dark mode with the UI (titlebar)
    • passes system dark mode to Cockpit (unless WebKit does this automatically, which it may)
  • in graphs
  • in the metrics page
  • in terminal, as there are themes, so dark doesn't get inverted to a light theme
  • in non-PF4 pages (filter hack)
  • in remote older versions of Cockpit?

It doesn't make sense to have a half-baked dark mode (unless we have some opt-in beta hack), so these should really land to be available to users in the same release.

Meanwhile

Once we have the start of an implementation that is working, we should reach out to other teams who work in the Cockpit ecosystem, such as 45Drives (who work on several 3rd party Cockpit plugins), SUSE, and the installer teams (at Red Hat and SUSE).

Possible follow-ups

  • Setting
  • Light mode (where the navigation and top bar are also light)

garrett avatar Oct 01 '18 08:10 garrett

A lot of apps are adding dark mode these days. I wondered if there would be an easy way to do this via CSS filters.

This morning, I tested a simple proof of concept by adding:

.area-ct-content {
   filter: hue-rotate(180deg) invert(150%) brightness(1.5);
}

And it's nearly perfect.

(Before using the brightness boost, I did a text-shadow: 0 0; to make the light colored fonts render better on a dark background. The brightness boost is probably better, as it also makes subtle colors like light lines and the hover color a little more noticeable.)

In order to properly implement it, we'd probably want:

  1. a user preference (most likely stored in a cookie, so it's really per-browser)
  2. handle background screens behind dialogs (invert the color to be lighter instead of darker, so it shows up darker when inverted — it's basically one more line of CSS)
  3. adjust the navigation UI (make it a little more contrasty / darker — but it's actually not bad as-is)
  4. prevent the flash of white when switching between pages (probably by splitting out the inversion & hue-rotate into something more top-level, then re-inverting the navigation UI and apply the brightness boost to the content in its own filter)
  5. have an auto setting for time & date switching?

Similarly, we can have a high contrast or low contrast accessibility mode done in a very similar manner.

The biggest drawback is that we do not have precedent to have settings stored outside the system itself. (And try to avoid that.) As this would only affect the browser, having a cookie setting would probably still make sense despite that?


I can't tease a dark mode without screenshots. So here are some screenshots. Remember, this is the only 1 line of CSS proof-of-concept.

System overview

screenshot_2018-10-01 system - sunny

Storage page

screenshot_2018-10-01 storage - sunny

Dialog on the storage page

(Here's where the fix for item 2 would be needed.) screenshot_2018-10-01 storage - sunny 1

garrett avatar Oct 01 '18 08:10 garrett

Mobile

(I scaled this down so it wouldn't be super-huge as a preview here.)

screen shot 2018-10-01 at 10 46 25

garrett avatar Oct 01 '18 08:10 garrett

Very Nice!

X1Aaron avatar Oct 03 '18 00:10 X1Aaron

See also https://stackoverflow.com/a/51799496/176160 It is likely there will soon be some support for determining if the browser is running in dark mode.

rhwood avatar Oct 17 '18 14:10 rhwood

Firefox 67 landed this week and now includes dark mode support.

  • On OSes that have a toggle, it supports the toggle.
  • On Linux, it checks the GTK foreground and background colors and infers a theme
  • On all OSes, there are overrides

OS-based dark mode in browsers (prefers-color-scheme) is only supported in Firefox and Safari. Chrome support is forthcoming (and should land some time this year).

garrett avatar May 23 '19 14:05 garrett

I'm currently working on overhauling all our colors in issue https://github.com/cockpit-project/cockpit/issues/11913.

Once done, this should allow us to support dark mode in a cleaner and more performant way, by redefining all the colors. (That said, I didn't notice performance issues. But it probably would have at least a little impact to flip the colors around.)

The method used above could be useful for parts of Cockpit that might not be easy to re-theme (such as the graphs), other top-level add-ons (such as Composer) or remote machines running old versions of Cockpit. The latter two exapmples would probably need additional work — perhaps by adding a special class in an iframe to enable color-variable-based dark mode and then defaulting to the above inversion method otherwise (if the class does not exist). We'd want to make sure there isn't a jarring white background in dark mode, in other words, and would need to mix-and-match techniques to get it done.

garrett avatar May 23 '19 14:05 garrett

The color reworking was absorbed in to an overall restyling of Cockpit in https://github.com/cockpit-project/cockpit/pull/11987.

Not all the colors have been ported over to variables, but most have.

Adding a proper dark mode would help spot other colors that have not been adjusted.

Meanwhile, here's a great article on the whens, whys, & hows of dark mode: https://web.dev/prefers-color-scheme/

garrett avatar Jul 18 '19 06:07 garrett

Coded a flexible and working solution for the Dark Mode.

Demo in here: https://dorson.github.io/CSS-Dark-Mode-and-color-switch/

Works with basic CSS variables + short JavaScript.

Can be used to define multiple color themes in one CSS file or other dynamic style setting options like text size, etc...

Have fun using my code !

Dorson avatar Apr 09 '20 07:04 Dorson

More dark mode info, BTW: https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/

garrett avatar Jul 02 '20 12:07 garrett

@Dorson: Thanks for the link. However, Cockpit is pretty complex, being a mix of PF3, PF4, and a lot of custom code. We'd have to have our own implementation of dark mode. PF4 already has some preliminary dark widget support, but that would only get us so far. Whatever is made for Cockpit would have to handle everything. We'd probably have to use more than one technique. (Like an optimized path when possible and possibly even a fallback one that is slightly less performant for legacy code and third-party extensions that don't directly support a dark mode.)

Plus, there are various design considerations to take in mind when developing a dark mode (more than inverting + shifting hue), as mentioned in the link I just shared above.

Glad you know that you're interested in dark mode though! The higher the want for it, the more likely we can get to it sooner than later (if at all).

garrett avatar Jul 02 '20 12:07 garrett

Copy/pasted from https://github.com/martinpitt/performance-graphs/pull/64#issuecomment-702187607:


Ideally, PatternFly would have a class for dark mode. IIRC, they would enable it at the top of the page or at a specific widget level.

So we could do something like this in SCSS:

.foo {
   color: var(--some-color);
  .dark-mode & {
     --some-color: #fff;
     background: red;
  }
}

And SASS would unwrap that as:

.foo { ... }
.dark-mode .foo { ... }

And the specificity of the dark mode overrides would win out. It would also be afterward too. :wink:

FWIW: PF would probably go with .pf-m-darkmode or something such.


Also: If dark mode is applied to the same widget, then we'd do this instead:

.foo {
   &.dark-mode {}
}

But, in most cases, we'd want dark mode at the top of the page and have everything inherit.

garrett avatar Oct 01 '20 14:10 garrett

Ideally, we'd try to use PF4's dark mode (with it lands) and have a fallback for non-PF with the example I outlined above (in which we'd probably also drop or adjust the dropshadows so they're not "dropglows").

And we'd have a little bit of CSS like the above comment for one-off overrides where necessary.

garrett avatar Oct 01 '20 14:10 garrett

Nobody else asked for this as far as I can remember, but this issue has a PoC and is clearly stated, so if anyone wants to have a go at this, it's still relevant.

martinpitt avatar Feb 27 '21 20:02 martinpitt

Removing and updating the colors we use (#15183) was a walk in the direction of this issue. And PatternFly is working on support. Once PF lands dark mode, we can try to see what happens when enabling it.

I'm still certain we'll need a PF4 native solution and a compatibility one (based on this proof of concept) as well, as mentioned above.

garrett avatar Mar 02 '21 09:03 garrett

Nobody else asked for this as far as I can remember

It's an accessibility feature and a user preference. Over the past few years, it's been showing up almost everywhere (with good reasons).

elementary OS (opinionated Linux-based OS that has an app store) founders have a good writeup about implementing a dark style preference... it's a nice read and makes a lot of great points (especially around accessibility) and is mostly not elementary-related: https://blog.elementary.io/the-need-for-a-freedesktop-dark-style-preference/

Meanwhile, coincidentally, I have my blinds mostly closed, sitting in a dark room, actually using dark "mode"* today as I have a bit of a headache and I want to keep working and try to not go into massive migraine mode. (It's important to me and very likely a lot of our users.)

* Technically, it's the adwaita-dark theme in gnome-tweaks. Firefox recognizes the light text and dark background, skins itself into a dark mode and then sets its own internal settings to to prefers-dark. This then causes GitHub and several other websites to flip to dark mode too.

garrett avatar Sep 16 '21 10:09 garrett

@garrett Have you tried the Dark Reader browser extension? I can't live without it!

jchristi avatar Sep 16 '21 11:09 jchristi

@jchristi: Yeah, Dark Reader is great! I use it on my phone especially (via Firefox on Android).

It doesn't really work with Cockpit, however, as we have a bunch of CSP (Content Security Policy) protections on which (rightfully) get in the way. We'll need to have our own solution for dark mode.

garrett avatar Sep 16 '21 11:09 garrett

Related, upstream PF dark theme proof of concept: https://github.com/patternfly/patternfly/pull/4203

garrett avatar Dec 07 '21 16:12 garrett

Would definitely like the modern look of Patternfly 4

septatrix avatar Mar 10 '22 13:03 septatrix

@septatrix: Cockpit has had the modern look of PatternFly 4 for a few years already... If your version of Cockpit is still using PatternFly 3, then you might want to consider updating.

If you're on an older version that ships in Debian Stable or Ubuntu LTS, we have backports instructions @ https://cockpit-project.org/running.html#debian (RHEL ships newer Cockpits and we have a COPR available for those who want the absolute latest in the form of a preview.)

Cockpit is currently at version 263 as of right now.

As for the next iteration of PatternFly 4, PatternFly itself hasn't finalized that yet. But it's probably when upstream dark support will land, and then we could use it in Cockpit (although with a bit of effort on our end too, for sure).

garrett avatar Mar 10 '22 15:03 garrett

Oh yes please ignore me. I only check the dashboard sparingly and confused it with FreeIPA and the Ansible Galaxy homepage :D

septatrix avatar Mar 10 '22 16:03 septatrix

I would like to know how to implement it on Cockpit @garrett from your first comment

.area-ct-content {
   filter: hue-rotate(180deg) invert(150%) brightness(1.5);
}

if not directly maybe trough Nginx

archef2000 avatar Apr 25 '22 20:04 archef2000

@Archef2000: That won't work, as Cockpit has changed a ton since this issue was first opened. We're using PatternFly 4 now, instead of PatternFly 3. And we're using many more PatternFly widgets instead of a lot of custom ones too.

An updated version of the proof of concept hack would be to add something like this, probably in page.scss:

.pf-c-page {
  filter: hue-rotate(180deg) invert(150%) brightness(1.5);
}

However, this is not the right way to do things:

  • It affects performance (in most cases: very slightly)
  • It causes icons to be too light
  • It doesn't work with the native PatternFly dark mode (which is upcoming)
  • Shadows become odd glows
  • Backdrops behind modals are lighter instead of darker, causing a blast of lightness in dark environments
  • It doesn't respect the user's prefers-color-scheme settings (which has landed in all browsers since I first opened this issue)

Most of these can be worked around with additional CSS. And we'd need to do something like this anyway without PatternFly 4's native dark mode setting, for pages that don't support that.

But it's finally probably around the time where we can start looking into adding some kind of dark mode.

We'd want to have fallback dark mode first, then patch in PF4 dark mode with a way to override the fallback (most likely using :not()), as having partial dark mode support does not make sense. (It's a bad experience to get some pages and some widgets in dark mode with others not.

garrett avatar Apr 26 '22 10:04 garrett

Where can i find the page.css ?

archef2000 avatar Apr 26 '22 10:04 archef2000

Note: a better fallback might be to redefine the PF color variables. (And I think this is what PF does for their implementation?) But we'd probably still need a hacky fallback too, as not every page in Cockpit would support it. In those cases, we might even need to attach the CSS with a more generalized selector, like body; perhaps body:not(.pf-c-page) (which would say pages that do NOT use PatternFly 4). (We'd need example pages.)

garrett avatar Apr 26 '22 10:04 garrett

Where can i find the page.css ?

It's page.scss in the Cockpit source. You'd need to build Cockpit from source for that. But, again, it's a complete hack and would need more work done.

Otherwise, injecting the CSS in an already-compiled Cockpit somewhere would probably work... but I think you'd need to do that on every page's compiled CSS file?

There are also some browser extensions that provide a means of adding CSS to pages, such as Stylus, and others that provide a hacky dark mode, like Dark Reader... but these kinds of extensions may have issues with Cockpit's CSP (content security policy) settings.

garrett avatar Apr 26 '22 10:04 garrett

And in Nginx could you injekt scss code as i cloud use it this way. @garrett

archef2000 avatar Apr 26 '22 10:04 archef2000

And in Nginx

I don't know about Nginx. That's completely out of scope of implementing a dark mode in Cockpit.

When I do use a proxy in the front, I use Caddy... Sorry; I can't help further with Nginx. A quick search on DuckDuckGo showed this result that might help you: https://forum.level1techs.com/t/infrastructure-series-use-nginx-to-inject-css-themes/174165

I can eventually implement dark mode directly in Cockpit. I know @jelly has also expressed interest in this. We want to try to tackle it "soon", but we're both juggling many other tasks in Cockpit (and Cockpit-Podman, Cockpit-Machines, etc.), so I can't give you an estimate aside from our wish to try to get it done sooner than later.

garrett avatar Apr 26 '22 10:04 garrett

Meanwhile, PatternFly has an issue open about guidance for dark theme implementations for software that uses PatternFly 4, such as Cockpit (but this is a TODO item):

  • https://github.com/patternfly/patternfly-design/issues/1152

And they have a meta-issue ("epic") about their dark theme implementation @:

  • https://github.com/patternfly/patternfly-design/issues/754

Where they say:

dark theme is not yet available for general use. We currently have an alpha version that we are testing with selected projects and don't have any firm date on when this might be fully supported


Additional notes: As the PF dark theme can be implemented at a page or a widget level, for a fallback theme to work properly, we'd want to use a way that cascades down as to not invert an already-dark mode. That is, we would want to probably use something like the CSS custom property trick to have a cascading boolean in CSS which would let us either apply or skip application of any dark mode settings, so we don't accidentally apply more than 1 dark mode change at the same time. (This would especially be true for any filter-based implementation, as inverting to dark twice (or any even number of times) means it's back to the original colors... but with degraded performance.)

https://lea.verou.me/2020/10/the-var-space-hack-to-toggle-multiple-values-with-one-custom-property/

Using this trick

So, in order of ideal implementation to less ideal, it would be something like:

  1. PatternFly 4's dark theme setting
  2. CSS variable redefining (where PF4's dark theme is not supported, which in some case, might even be some PF4 widgets that might not have their own dark mode setting yet... and in other cases, Cockpit's custom widgets)
  3. CSS filter to invert (especially on pages where PF4 does simply not exist... like in some 3rd party apps / add-ons for Cockpit)

If 1 is active, we don't want 2 or 3. If 2 is active, we don't want 3 (and 1 wouldn't be possible if 2 is active). If 3 is active, neither 1 nor 2 would be possible.

So, in other words: Fallbacks on fallbacks.

Also, this wouldn't be able to apply to remote servers, unless the remotes would also be dark mode capable (that is: on whatever version Cockpit would have a dark mode).

Cockpit is much more complicated than any other app using PatternFly 4, as we have iframes with embedded pages that might or might not be developed by us — and that might or might not even be made using PatternFly. And, we can also have remote servers with different versions of Cockpit too.

garrett avatar Apr 26 '22 11:04 garrett

A for a CSS variable redefinition method, a good first pass to figure out which colors would need to be reassigned (when dark mode is on) would be to view the PF color page (https://www.patternfly.org/v4/guidelines/colors/) and use a simple invert on the html or body tag, such as: 'filter: invert() hue-rotate(180deg);`

It would then look like this preview:

image

And then someone (probably me) would have to redefine all the PF color variables to the color picker version of the colors shown there. Some would have to be tweaked with something like the above-mentioned filter (filter: hue-rotate(180deg) invert(150%) brightness(1.5);) — this would most notably be the the grey colors, to provide a bit more contrast.

This would provide a way to have a dark mode based on color redefinition, like I mentioned as the first fallback (# 2 in the above list), and we already have a hacky approach for a # 3 (the CSS filter with some additional CSS to correct some of the things it gets wrong).

Then, the largest task would be integrating all of this and making sure the most preferable approach is used, with fallbacks where necessary. (We'd also want to adjust the CSS variable redefinition to whatever PF winds up using in their implementation too. But the approach I mention in this comment is a good first pass for a starter implementation.)

garrett avatar Apr 26 '22 12:04 garrett