1

When enabled, my website uses a filter applied to the entire site to obtain a simple dark mode.

// this class applied to the entire body (when dark mode enabled)
.dark-mode {
    filter: invert(1) hue-rotate(180deg);
}

Since the backgrounds of all of my pages are either white (or light) and the text is all black (or dark), this filter works great for shifting to a dark mode without actually specifying every single color for the site.

The problem comes when there are a few things that shouldn't be filtered. Like right now I'm trying to create a color legend describing elements going on a map. The color legend goes from red->yellow->green->blue->purple. The legend looks great without dark mode applied, but looks wrong when I apply the dark mode filter, so I wanted to negate the dark mode filter for this color legend as well as for the elements going into the map.

// this class applied to elements that should counteract the dark mode filter (when dark mode enabled)
// notice that I just reversed the invert and hue-rotate from .dark-mode class
.negate-dark-mode {
    filter: hue-rotate(180deg) invert(1);
}

The problem is... this filter doesn't seem to counter act the .dark-mode class filter

Here's an example showing what's happening

<div style="position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 400px;
            height: 400px;
            background-color: white;
            z-index: 1000;
            border: 4px solid black;
            background: linear-gradient(to right, hsl(0, 100%, 50%), hsl(0, 100%, 50%) 0%, hsl(75, 100%, 50%) 25%, hsl(150, 100%, 50%) 50%, hsl(225, 100%, 50%) 75%, hsl(300, 100%, 50%) 100%, hsl(300, 100%, 50%));">
  Parent
  <div style="position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                width: 100%;
                height: 300px;
                background-color: white;
                z-index: 1000;
                border: 4px solid black;
                filter: invert(1) hue-rotate(180deg);
                background: linear-gradient(to right, hsl(0, 100%, 50%), hsl(0, 100%, 50%) 0%, hsl(75, 100%, 50%) 25%, hsl(150, 100%, 50%) 50%, hsl(225, 100%, 50%) 75%, hsl(300, 100%, 50%) 100%, hsl(300, 100%, 50%));">
    Child
    <div style="position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    width: 100%;
                    height: 200px;
                    background-color: white;
                    z-index: 1000;
                    border: 4px solid black;
                    filter: hue-rotate(180deg) invert(1);
                    background: linear-gradient(to right, hsl(0, 100%, 50%), hsl(0, 100%, 50%) 0%, hsl(75, 100%, 50%) 25%, hsl(150, 100%, 50%) 50%, hsl(225, 100%, 50%) 75%, hsl(300, 100%, 50%) 100%, hsl(300, 100%, 50%));">
      Grandchild
    </div>
  </div>
</div>

enter image description here

The parent div shows a nice color legend, the child div shows the same color legend but with the dark mode filter (notice it doesn't look very nice), the grandchild div shows yet another color legend but with the attempt at negating the dark mode filter (notice it doesn't fully negate the filter applied to the Child div)

Clearly my understanding of filters isn't right... I figured if the dark mode filter was applying an invert(1) followed by a hue-rotate(180deg) then surely negating it would just be hue-rotate(180deg) followed by invert(1).

Any assistance is greatly appreciated.

Wongjn
  • 8,544
  • 2
  • 8
  • 24
Stanton
  • 904
  • 10
  • 25
  • 1
    not related to your question but I highly recommend you to drop the filter method to create dark/light mode. You have no idea about all the issues you will face. – Temani Afif May 03 '23 at 13:09
  • 1
    indeed... your comment is not related to my question... – Stanton May 03 '23 at 13:14
  • The `hue-rotate(180)` gives the most headache: `invert` does a *linear* color change (red to cyan) , while `hue-rotate` does a *perceptive* color change (red to [Very dark cyan](https://www.colorhexa.com/006b6b)). So if you *negate* that, the Very dark cyan becomes [Very light red](https://www.colorhexa.com/ff9393) as the inversion is *linear* instead of *perceptive*. With HSL colors you can circumvent *linear/perceptive* issues by adding `180deg` to the gradient colors using a CSS *custom property* when you need to counter an `invert`. Use `invert` only, with the suggested HSL manipulation. – Rene van der Lende May 03 '23 at 14:51
  • BTW: HSL hue + 180 does a *linear* color inversion, just like `invert(1)`... – Rene van der Lende May 03 '23 at 14:55
  • @Stanton - Sorry for offtopic, it's about your [question](https://stackoverflow.com/questions/76473722) - SetPoint sends `Ctrl + C` the same way as if you pressed it on your keyboard, there is nothing wrong here with your mouse software. The problem might be in VS. – ESkri Jun 14 '23 at 15:02

1 Answers1

1

One would think the two filters (invert(1) and hue-rotate(180deg)) I'm using are both "reversible" by simply applying them a second time, but after doing some more research it appears the implementation of the hue-rotate() filter is just an approximation and that it also has roundoff error and that applying hue-rotate(180deg) twice does not result in the original pixels.

Taking the advice from some of the answers in that post, I'm going to drop the saturation values from 100% to instead be 75%. The colors still look just fine when not in dark mode, and when moving to dark mode (and negating the dark mode for the legend) the colors look almost exactly the same as before the 2 filters were applied.

Stanton
  • 904
  • 10
  • 25