Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSS rules of the form .foo.bar don't behave as expected with peer-checked:bar #9056

Closed
TastyPi opened this issue Aug 9, 2022 · 7 comments · Fixed by #9262
Closed

CSS rules of the form .foo.bar don't behave as expected with peer-checked:bar #9056

TastyPi opened this issue Aug 9, 2022 · 7 comments · Fixed by #9262
Assignees

Comments

@TastyPi
Copy link

TastyPi commented Aug 9, 2022

What version of Tailwind CSS are you using?

v3.1.8

What build tool (or framework if it abstracts the build tool) are you using?

postcss-cli 10.0.0

What version of Node.js are you using?

v16.14.0

What browser are you using?

N/A

What operating system are you using?

Linux

Reproduction URL

https://play.tailwindcss.com/4zvuUG5xci

Seems Tailwind Play is still on v3.1.5, but I see the issue in v3.1.8

Describe your issue

If I have a standard CSS rule defined like this:

@layer components {
  .foo.bar {
    background-color: green;
  }
}

Then I would expect peer-checked:bar to define the following:

.peer:checked ~ .foo.peer-checked\:bar {
  background-color: green;
}

Instead it generates:

.foo.peer:checked ~ .peer-checked\:bar {
  background-color: green;
}

.foo is applied to the .peer instead of the .peer-checked\:bar.

Changing the original rule to .bar.foo fixes the issue (i.e. it works as expected with the first class in the list).

@adamwathan
Copy link
Member

Hey! Can you share a more real-world example of why you would even be doing this? I agree the behavior doesn't feel right here but the usage looks really bizarre to me (.bar is not a utility in your example, it's part of a complex selector), and I'm honestly more inclined to throw an exception here than to change the behavior.

@Fuller05767

This comment was marked as spam.

@Gonzales2022

This comment was marked as spam.

@tailwindlabs tailwindlabs deleted a comment from Lee6546 Aug 26, 2022
@TastyPi
Copy link
Author

TastyPi commented Aug 30, 2022

Sorry for the delay, I've been on holiday for the last couple of weeks. Our use case is admittedly a bit weird and complicated so I'm not surprised it hasn't really been an issue.

We have a "visual check box" component where we control how it appears using CSS classes, e.g. adding the checkbox--checked class makes it appear checked and checkbox--disabled makes it appear disabled. This enables us to build more complex components using Tailwind, for example we can use <input type="checkbox" class="sr-only peer"> to control one of these visual check boxes by adding classes like peer-checked:checkbox--checked peer-disabled:checkbox--disabled.

This works really well and allows us to create custom components that are controlled entirely by CSS, no JS involved. However, if we want to render a checked and disabled checkbox ideally we would define the style using a .checkbox--checked.checkbox--disabled rule, but due to the bug above that doesn't work with peer-checked:checkbox--checked peer-disabled:checkbox--disabled. Instead we've had to create a checkbox--checked-and-disabled class and use that with the appropriate Tailwind modifiers.

Note that we use this in various different situations not just with peer. There are obviously other ways we could implement this that might result in simpler CSS, but the flexibility was important for our use cases.

@thecrypticace
Copy link
Contributor

Hey, I just merged in the fix for this. It'll be in our next tagged release. If you want to try it before then you can test our insiders build: npm install tailwindcss@insiders — this should be available in a few minutes.

@TastyPi
Copy link
Author

TastyPi commented Sep 13, 2022

I haven't had time to investigate this fully, however I suspect this won't achieve exactly what I needed (it's still a good improvement though).

My original CSS looked something like this:

@layer components {
  .checkbox--unchecked {
    @apply bg-white;
  }

  .checkbox--checked {
    @apply bg-blue-600;
  }

  .checkbox--disabled {
    @apply bg-gray-100;
  }

  .checkbox--disabled.checkbox--checked {
    @apply bg-gray-300;
  }
}

The important part here is the "disabled and checked" colour is different from both the "enabled and checked" and "disabled and unchecked" colours. In order for it to work correctly with just peer-checked:checkbox--checked peer-disabled:checkbox--disabled Tailwind would have to generate something like:

.peer:checked:disabled ~ .peer-checked\:checkbox--checked.peer-disabled\:checkbox--disabled {...}

Which seems very complicated, is a niche use-case, and has the danger of causing an exponential explosion in the number of rules Tailwind generates (it would have to generate a rule for every combination of prefixed checkbox--checked and checkbox--disabled). So I suspect this use-case is out-of-scope for Tailwind, but the bug fix is still a useful fix and having a record of this limitation might be useful to someone else.

The only way I could see this maybe being implemented is something like [.peer:checked:disabled~&]:checkbox--disabled.checkbox--checked, where checkbox--disabled.checkbox--checked would be a made up Tailwind feature that uses the rules that match .checkbox--disabled.checkbox--checked. I won't hold my breath though 🙂.

@thecrypticace
Copy link
Contributor

Yeah in this case you're trying to match a "single utility" made of multiple classes but NOT utilities made of the individual classes. We cannot do this generally and it would require a specialized feature. This is explicitly not something we would support. We encountered a similar case with @apply and the way it generates rules and explicitly decided against this idea.

The only real way I can think of is to:

  1. Write the CSS yourself instead of using the peer variant to generate it — probably the best solution for this case
  2. Or, side-step apply, and use this utility: peer-checked:peer-disabled:[&.checkbox--checked.checkbox--disabled]:bg-gray-300 which handled peer checked, disabled, and the fact that the element itself needs both checkbox--checked and checkbox--disabled classes. It's super long and looks wild af tho. I would personally just write the CSS in this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants