3

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>

2 Answers 2

8

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>

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! A solution in only 15 minutes! I kinda sensed that I was missing something, glad that there is a clean solution.
1

one solution is to use filter:invert(100%) as so:

.light {
  --invert:invert(100%);
}

.dark {
  --invert:invert(0%);
}

div {
  filter:var(--invert)
};

wheither your div is associated to .dark or .light class you'll get the desired invertion.

const button = document.getElementById('toggle');

let theme = 'dark';

button.addEventListener('click', () => {

  const div = document.getElementsByTagName('div')[0];
  
  div.classList.toggle(theme === 'dark' ? 'light' : 'dark');

})
.dark{
  --invert:invert(100%)
}

.light{
  --invert:invert(0%)
}

.to-invert {

  height:100px;
  width:100px;
  background-color:blue;
  filter:var(--invert);
  margin-bottom:1rem;
  
}
<div class="dark">
  <div class="to-invert">
  </div>
</div>
<button id="toggle">Invert</button>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.