0

In a TailwindCSS variant - purely out of curiosity, since its usefulness is limited - I want to declare a dark: variant that works inside .dark, but gets overridden when inside a .light parent, and continues to behave this way for nested elements. I created a playground where green indicates that the variant is working as expected.

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@custom-variant dark (&:where(.dark, .dark *):not(.dark .light, .dark .light *));
</style>

<div class="dark text-red-500 dark:text-green-500">
  DARK
  <p class="text-red-500 dark:text-green-500">
    DARK

    <div class="light dark:text-red-600 text-green-600">
      > LIGHT
      <p class="dark:text-red-600 text-green-600">
        > LIGHT

        <div class="dark text-red-500 dark:text-green-500">
          >> DARK
          <p class="text-red-500 dark:text-green-500">
            >> DARK

            <div class="light dark:text-red-600 text-green-600">
              >>> LIGHT
              <p class="dark:text-red-600 text-green-600">
                >>> LIGHT

                <div class="dark text-red-500 dark:text-green-500">
                  >>>> DARK
                  <p class="text-red-500 dark:text-green-500">
                    >>>> DARK

                    <div class="light dark:text-red-600 text-green-600">
                      >>>>> LIGHT
                      <p class="dark:text-red-600 text-green-600">
                        >>>>> LIGHT
                        
                        <div class="dark text-red-500 dark:text-green-500">
                          >>>>>> DARK
                        </div>
                      </p>
                    </div>
                  </p>
                </div>
              </p>
            </div>
          </p>
        </div>
      </p>
    </div>
  </p>
</div>

Understanding the task, this is really just a plain CSS question; the generated CSS looks like this:

Input

@custom-variant dark (&:where(.dark, .dark *):not(.dark .light, .dark .light *));

Output

.dark\:STYLE_NAME {
  &:where(.dark, .dark *):not(.dark .light, .dark .light *) {
    /* properties for STYLE_NAME */
  }
}

Or extended example:

Input

@custom-variant dark {
  /* default dark */
  &:where(.dark, .dark *):not(.dark .light, .dark .light *) {
    @slot;
  }
  /* dark apply inside light */
  &:where(.dark .light .dark, .dark .light .dark *) {
    @slot;
  }
};

Output

.dark\:STYLE_NAME {
  &:where(.dark, .dark *):not(.dark .light, .dark .light *) {
    /* properties for STYLE_NAME */
  }
  &:where(.dark .light .dark, .dark .light .dark *) {
    /* properties for STYLE_NAME */
  }
}

I'm just looking for the proper selectors that target .dark and all its children, except .light and all of its child elements.

1 Answer 1

0

Review #1

@custom-variant dark (&:where(.dark, .dark *):not(.dark .light, .dark .light *));

Yes, your first example doesn't meet your expectations because the first .light permanently disables .dark for all its children, and it cannot be reactivated later.

Review #2

@custom-variant dark {
  /* default dark */
  &:where(.dark, .dark *):not(.dark .light, .dark .light *) {
    @slot;
  }
  /* dark apply inside light */
  &:where(.dark .light .dark, .dark .light .dark *) {
    @slot;
  }
};

In your second example, you handled the first case manually, but it's not dynamic and doesn't work infinitely, unless you manually create selectors up to a certain level. Unfortunately, this example only handles the first .light and its children. It does not handle a second one afterward. That said, it could still be sufficient for many use cases, since using .dark .light .dark .light consecutively is not very common. At that point, I think it would be considered a design flaw.

Solution

The main issue lies in selecting the children. If acceptable as a solution, it is possible to filter out elements with the .light class. However, this requires that every element where you don’t want .dark to apply must have the .light class. I believe this is the most consistent way to achieve the goal:

@custom-variant dark (&:where(.dark, .dark *:not(.light)));

<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<style type="text/tailwindcss">
@custom-variant dark (&:where(.dark, .dark *:not(.light)));
</style>

<div class="dark text-red-500 dark:text-green-500">
  DARK
  <p class="text-red-500 dark:text-green-500">
    DARK

    <div class="light dark:text-red-600 text-green-600">
      > LIGHT
      <p class="light dark:text-red-600 text-green-600">
        > LIGHT

        <div class="text-red-500 dark:text-green-500">
          >> DARK
          <p class="text-red-500 dark:text-green-500">
            >> DARK

            <div class="light dark:text-red-600 text-green-600">
              >>> LIGHT
              <p class="light dark:text-red-600 text-green-600">
                >>> LIGHT

                <div class="text-red-500 dark:text-green-500">
                  >>>> DARK
                  <p class="text-red-500 dark:text-green-500">
                    >>>> DARK

                    <div class="light dark:text-red-600 text-green-600">
                      >>>>> LIGHT
                      <p class="light dark:text-red-600 text-green-600">
                        >>>>> LIGHT
                        
                        <div class="text-red-500 dark:text-green-500">
                          >>>>>> DARK
                        </div>
                      </p>
                    </div>
                  </p>
                </div>
              </p>
            </div>
          </p>
        </div>
      </p>
    </div>
  </p>
</div>

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

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.