DEV Community

Chris Burnell
Chris Burnell

Posted on • Originally published at on

The Flip-Flop Technique

I recently implemented a colour scheme toggler in the footer of my website, following Andy Bell’s guide, Create a user controlled dark or light mode, and I found a wonky but fun alternative solution for styling my dark theme which leverages CSS’s filter property.

Check out the article on my website to see how this all performs in your native browser.

Andy does a great job at explaining how this solution works, so I recommending reading his article if you want more information.

The gist is that we have some JavaScript that hooks onto a number of CSS Variables exposed on our pages, and we’re toggling their values back and forth between a light and dark palette by changing an attribute on the root element:

:root {
    --color-text: black;
[data-user-color-scheme="dark"] {
    --color-text: white;


I need to put in some time to work on the colours used in my dark theme, but I wanted an interim solution because I was so excited to implement this, to be honest!

Eventually, I realised that with a handful of filter values, we can come up with a decent-enough inversion of my light-themed styles.

Figure 1

Theme: unaltered

Figure 2

Theme: invert(1)

Inversion complete. We’ve gone from a light background to a dark one. Now we need to fix the hues of our colours—in this case, we want our brown to be blue.

Figure 3

Theme: invert(1) hue-rotate(180deg)

Now we’ve managed to get our blue back, but the Dragon emoji looks completely wrong. This is where the flip-flop technique comes in.

Figure 4

Theme: invert(1) hue-rotate(180deg)
Emoji: invert(1) hue-rotate(180deg)

That’s done it. By applying the same filter again to the emoji, it flip-flops back to its unaltered appearance.

Hue, Saturation, Lightness

But something’s off. The colours of the emoji in the final example seem less saturated or less vibrant than the unaltered emoji in the first example, which is most noticeable on the yellow hair of the dragon. Interact with this demo to see the unaltered state alongside the fully-filtered state and see for yourself.

Even more confusing to me is that this discrepancy only exists when I look at it using my default light theme—when viewed with my dark theme the unaltered emoji appears just as unsaturated as the final product.

The Code

video {
    @extend %asset-elements;

@mixin theme-dark() {
    @supports (filter: invert(1) hue-rotate(180deg)) {
        %asset-elements {
            filter: invert(1) hue-rotate(180deg);

    @supports not (filter: invert(1) hue-rotate(180deg)) {
        --color-black: #{$color-white};
        --color-mineshaft: #{$color-alto};
        --color-kaiser: #{$color-dove};
        --color-dove: #{$color-kaiser};
        --color-alto: #{$color-mineshaft};
        --color-white: #{$color-black};

@include media("dark") {
    :root {
        --color-scheme: "dark";

    :root:not([data-user-color-scheme]) {
        @include theme-dark;

/*:root*/[data-user-color-scheme="dark"] {
    @include theme-dark;

Maybe someone knowledgable about colours or filters on the web has an idea of what’s going on here, but I can’t seem to get the emoji to return to its original colour using any combination of filters to try to flip-flop back to its unaltered state.

I’m definitely missing something, but it’s close.

Top comments (0)