skip to Main Content

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?

3

Answers


  1. 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>
    Login or Signup to reply.
  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>
    Login or Signup to reply.
  3. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search