0

I have a problem I need to solve with CSS selectors. I am trying to make automatic color overrides for Tailwind to have our site always comply to WCAG guidelines.

I have a tailwind plugin I've written which automatically fixes this problem - but I've realized the way I'm targeting the CSS selectors is off.

Assume there is a color, white. And another color - light grey. When light-grey is imposed on white, I want to automatically correct the color to black.

I am doing this currently with the following selectors

.bg-white .text-light-grey {
  color: black;
}

However, this introduces problems! It works fine on this HTML structure:

<div class="bg-white">
    <span class="text-light-grey">This is now black.</span>
</div>

But it will ruin this structure

<div class="bg-white">
    <span class="text-light-grey">This is now black.</span>

    <div class="bg-black">
        <span class="text-light-grey">This is also now black, but it shouldn't be - It's inside a black container!.</span>
    </div>
</div>

How can I modify the CSS selectors, so that when there is another background specified between them, the color is not corrupted.

Something like this

.bg-white :not(some fancy way to make sure there are no other bgs inbetween bg white and the text color) .text-light-grey {
  color: black;
}

This has got to be possible right? Maybe something with class~= or something?

Edit: Does anyone with knowledge of the :has() selector know if this could be solved using it?

Bryan Rayner
  • 4,172
  • 4
  • 26
  • 38
  • I think the minimal example I have there is enough to demonstrate. The concept only gets worse; Since this is a web-app, you could have a white background at the root of the page, and then a colored UI element which is nested 50 levels deep in the DOM. The way these are structured, that deep element is still getting its colors overridden; even though they'd be fine if it was taking into account the background colors higher up the tree. – Bryan Rayner Jun 28 '23 at 19:19
  • I am asking this question to both ChatGPT and this site. Getting some good pushback from ChatGPT, it's suggesting I do a "reset" rule in the plugin which would re-iterate the colors being left "as-is" for combinations that are Ok. – Bryan Rayner Jun 28 '23 at 19:20

3 Answers3

2

The idea that has come to my mind is to use two selectors. One targets the immediate child and the other checks all intermediary elements with classes for background color. Would this suffice in your situation?

.bg-white {
  background: white;
}

.bg-black {
  background: black;
}

.text-light-grey {
  color: lightgrey;
}

.bg-white>.text-light-grey,
.bg-white :not([class^="bg-"]) .text-light-grey {
  color: black;
}
<div class="bg-white">
  <span class="text-light-grey">This is now black.</span>

  <div class="bg-black">
    <span class="text-light-grey">This is not black!</span>
  </div>

  <div>
    <span class="text-light-grey">This is now black!</span>
  </div>
</div>

EDIT: If you don't have a lot of classes that require adjusted color, you may consider this way:

.bg-white {
  background: white;
}

.bg-black {
  background: black;
}

.text-light-grey {
  color: lightgrey;
}

.bg-blue {
  background: blue;
}


/* Similar to what you wrote in the comment */

.bg-white .text-light-grey {
  color: black;
}

[class*="bg-"]:not(.bg-white) .text-light-grey {
  color: lightgrey;
}
<div class="bg-white">
  <span class="text-light-grey">This is now black.</span>

  <div class="bg-black">
    <span class="text-light-grey">This is not black!</span>
  </div>

  <div>
    <span class="text-light-grey">This is now black!</span>
  </div>

  <div>
    <div class="bg-blue">
      <div>
        <span class="text-light-grey">This is nested and not black!.</span>
      </div>
    </div>
  </div>
</div>
Ivan Beliakov
  • 529
  • 3
  • 14
  • That's a great start. We use the tw- prefix in production, so it didn't work (had to use *= instead). Also, it just didn't work with the deeper nesting we have with real life components. But it seems like this works: .bg-white>.text-light-grey, .bg-white :not([class*="bg-"]) .text-light-grey { color: black; } .bg-white [class*="bg-"] .text-light-grey { color: lightgrey; } We change back the color when there _is_ a class with a background in the way. – Bryan Rayner Jun 28 '23 at 19:58
  • Thanks for the feedback. Perhaps you will find useful the option I have added in my answer – Ivan Beliakov Jun 28 '23 at 20:17
2

Consider leveraging the cascade. Use CSS variables that automatically cascade down on the bg-* classes. This has the advantage that it would work for an infinite amount of nesting:

tailwind.config = {
  theme: {
    extend: {
      colors: {
        'light-gray': 'rgb(var(--light-gray) / <alpha-value>)',
      }
    },
  },
};
.bg-white {
  --light-gray: 0 0 0;
}

.bg-black {
  --light-gray: 255 255 255;
}
<script src="https://cdn.tailwindcss.com"></script>

<div class="bg-white">
  <span class="text-light-gray">This is now black.</span>

  <div class="bg-black">
    <span class="text-light-gray">This is also now black, but it shouldn't be - It's inside a black container!.</span>

    <div class="bg-white">
      <span class="text-light-gray">This is now black.</span>

      <div class="bg-black">
        <span class="text-light-gray">This is also now black, but it shouldn't be - It's inside a black container!.</span>
      </div>

      <div class="bg-white">
        <span class="text-light-gray">This is now black.</span>

        <div class="bg-black">
          <span class="text-light-gray">This is also now black, but it shouldn't be - It's inside a black container!.</span>
        </div>
      </div>
    </div>
  </div>
</div>
Wongjn
  • 8,544
  • 2
  • 8
  • 24
  • This is great, but the only problem with it is I have to reset a lot of colors for every single background color where they're ok. I guess that's fine - but it could get problematic over time. I guess it's better though than all other options. – Bryan Rayner Jun 28 '23 at 20:45
0

I had a go using currentcolor. Not sure how that would affect the rest of your project but sharing it in case it helps at all. It is similar to @Wongjn's solution.

.bg-white {
  color: black;
}

.bg-black {
  color: white;
}

.text-light-gray {
  color: currentcolor;
}

https://play.tailwindcss.com/hQ4VW1RysN?file=css

And another one, even more similar to @Wongjn's solution, but sharing in case it helps.

.bg-white {
  --text: black;
}

.bg-black {
  --text: white;
}

.text-light-gray {
  color: var(--text);
}

https://play.tailwindcss.com/CdT0KVvQga?file=css

stickyuser
  • 2,552
  • 15
  • 15