-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Add *
variant for targeting direct children
#12551
Conversation
Co-authored-by: Gregor Kaczmarczyk <[email protected]>
I prefer the word |
|
|
I fear that one day, they will want to add a variant to target all children but * is already taken |
Thought about this pretty hard before deciding on this API and I just don't see myself ever wanting to target all children ever. If that's ever needed though you can use an arbitrary variant |
Excited to see this, wanted something like this for a while :D.
My two cents on the syntax - I think intuitively, if I was introduced to this selector while reading a codebase, I would see Either way, stoked to see this ship! |
I think you should do a poll. Please do the more I look at the star the more I cringe. <nav class="hover:*:underline"></nav> |
@adamwathan I promise I’m not just trying to pick a fight here lol — imo using Just my 2¢ — a great value-add regardless 🙂 |
For those questioning .my-class > * {
/* ... */
} ...and here's the one for all children: .my-class * {
/* ... */
} The https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator So while it's definitely ambiguous, I'm not super keen on adding this to core unless we can keep the name really short, because even <div class=">:underline"> Just looks like a bug to my eyes, even though of course it's fine in reality. |
TL;DR
That said, there are downsides. This feature gives us our very first first-class way to break code colocation in Tailwind. (I don't consider arbitrary variants to be first-class, they're more clearly an escape hatch.) A two-character sequence This also gives us our first variant (again, excluding arbitrary variants) that has nothing to do with element state. All other variants apply styles to an element based its state, or the state of an ancestor or sibling. This variant's behavior is arguably similar to This is nitpicky and negligible for smaller use cases like the one in the original PR comment, but for larger cases, I'm hoping the community continues to reach for solutions that don't break code colocation:
Here's an idea of how to implement this feature with
|
@adamwathan & @AlexVipond Great thoughts all around. I appreciate the thoughts behind I'm with it. 🚀 I'm not worried about the collocation concerns here, as we use |
I target all descendants in just about every project! Can give some use-cases if you're interested. I also target all direct-children in just about every project, so I'm very used to both |
@MichaelAllenWarner I was in the same party, but I couldn't come up with any practical examples for targeting all descendants. I would love to see some. 🙂 |
Most common use-case is when I'm setting up a background image that will involve a real <div class="absolute inset-0 [&_*]:w-full [&_*]:h-full [&_img]:object-cover">
{{ imageMaybeWithSomeWrappers }}
</div> (though I suppose I could just do Another one I often use is If I've got a component with multiple elements that have to transition simultaneously on hover, then I might do Those are the main examples I can think of right now. There are some other non-inherited properties that I do target all descendants for (like |
* add `*` as child variant * add `*` as allowed variant character * update test to reflect Lightning CSS output * add `childVariant` test * Update changelog --------- Co-authored-by: Adam Wathan <[email protected]> Co-authored-by: Robin Malfait <[email protected]> Co-authored-by: Gregor Kaczmarczyk <[email protected]>
Not sure I'm missing something, but what's wrong with just using |
@gukj I think all of those, even This is just my guess |
@brandonmcconnell I think it's more about trying to look like the css it's representing. Otherwise (I would argue) it makes zero sense for something as uncommon as this to get If the aim was for tailwind to be short rather than readable, I would maybe agree. |
@gukj Fair enough, though like some here, myself included, have pointed out – if the goal was for this to look like the logic in CSS it represented, it would have used Seems like it's more than that |
@brandonmcconnell True, true. If the reasoning really is saving a couple characters in case someone needs to apply multiple classes to children, I would much rather see some kind of multi-target syntax. Something like
|
@gukj Hence https://github.com/brandonmcconnell/multitool-for-tailwindcss 😉 |
@brandonmcconnell Where have you been all my life 😍 |
@gukj spamming tailwind labs with pull requests and crafting custom TailwindCSS plugins 😆 |
This PR adds a new
*
variant for targeting direct children of an element:This can also be combined with other variants, such as
hover
:The above example would only add an underline to each link when each link is hovered, as per our documentation on stacked variants/modifiers.
Known limitations
One thing you'd probably expect to work that won't currently work is overriding a child selector style with a utility directly on the child itself:
This is because the generated selector for a child variant has the same specificity as a regular utility class, but appears later in the CSS file so it takes precedence.
We can reduce the specificity of this selector using
:where
:However this reduces the specificity
0,0,0
which is lower than the styles in Preflight, which means Preflight styles would now defeat child variant styles, which is no good.This can be solved by taking advantage of the new proper CSS
@layer
rule, which ensures that rules in later layers always take precedence over rules in earlier layers, regardless of their specificity.We don't want to do that in the v3.* series of Tailwind because it could be considered a breaking change due to the fact that Tailwind would stop working in browsers it currently works in, but we do intend to make this change for v4. So in v4, we can introduce the use of both
:where()
in the child variant, and native@layer
rules which will make it possible to override child variants with utilities the way you'd expect.We originally resisted adding this feature because of this limitation and because we didn't know what the best solution for it would be, but now that the correct solution is clear we feel comfortable introducing this feature despite the limitation because the path forward will not require drastically re-imagining the API or how the feature should work.