Search code examples
htmlcsscss-variables

Inverting colors with CSS Variables


I want to create a local inverted theme (modern browsers). Color shades are set using CSS Vars (CSS custom properties). Some elements have more contrast, others are low contrast. Now the inverted container has a black background. Everything within there, should be reversed. Dark grey should be light grey. Light grey should be dark grey.

My goal is to achieve this without reassigning the vars in CSS selectors. For this example it would be easy, but the actual code base is big and there are many selectors. So instead of that I just want change the CSS Vars. Also, I want keep the original CSS Vars to be editable.

Final goal mockup enter image description here

Simple reassignment of the Vars (light = dark, dark = light) does not work, obviously. I tried to transpose the values to a new placeholder var, but that also didn't worked. Maybe I was doing it wrong? Is there a clean way? I don't think so.

I am aware of workarounds using SASS, or hacks using mix-blend-mode.

Playground:
https://codepen.io/esher/pen/WzRJBy

Example code:

<p class="high-contrast">high contrast</p>
<p class="low-contrast">low contrast</p>

<div class="inverted">
  <p class="high-contrast">high contrast</p>
  <p class="low-contrast">low contrast</p>
</div>

<style>
  :root {
    --high-contrast: #222;
    --low-contrast:  #aaa;
  }

  .high-contrast { color: var(--high-contrast) }
  .low-contrast  { color: var(--low-contrast)  }

  .inverted {
    background-color: black;

    /* Switching vars does not work 
    --high-contrast: var(--low-contrast);
    --low-contrast:  var(--high-contrast);
    */

    /* Transposing Vars also doesn't work:
    --transposed-low-contrast: var(--low-contrast);
    --transposed-high-contrast: var(--high-contrast);
    --high-contrast: var(--transposed-low-contrast);
    --low-contrast: var(--transposed-high-contrast);
    */
  }

  /*

  I am aware of this solution (see description above):

  .inverted p.high-contrast { color: var(--low-contrast);   }
  .inverted p.low-contrast  { color:  var(--high-contrast); }

  */
<style>

Solution

  • What about something like this:

    :root {
      --high-contrast: var(--high);
      --low-contrast: var(--low);
      --high: #222;
      --low: #aaa;
      /* Yes I can put them at the end and it will work, why?
         Because it's not C, C++ it's CSS
         And the order doesn't matter BUT we need to avoid 
         cyclic dependence between variables.
      */
    }
    
    .high-contrast {
      color: var(--high-contrast)
    }
    
    .low-contrast {
      color: var(--low-contrast)
    }
    
    .inverted {
      --high-contrast: var(--low);
      --low-contrast: var(--high);
    }
    <p class="high-contrast">high contrast</p>
    <p class="low-contrast">low contrast</p>
    
    <div class="inverted">
      <p class="high-contrast">high contrast</p>
      <p class="low-contrast">low contrast</p>
    </div>