Tag: Selector

@supports selector()

I didn’t realize the support for @supports determining selector support was so good! I usually think of @supports as a way to test for property: value pair support. But with the selector() function, we can test for selector support as well. It looks like this:

@supports selector(:nth-child(1 of .foo)) {  }

You just drop the selector right between the parens and that’s what it tests for.

That selector above is a pretty good test, actually. It’s a “selector list argument” that works for the :nth-child ‘n’ friends selectors. As I write, it’s only supported in Safari.

So let’s say your ideal situation is that the browser supports this selector. Here’s an example. You know that with <ol> and <ul> the only valid child element is <li>. But also say this list needs separators, so you (and I’m not saying this is a great idea) did this kind of thing:

<ul>   <li class="list-item">List item</li>   <li class="list-item">List item</li>   <li class="separator"></li>   /* ... */ </ul>

Then you also want to zebra-stripe the list. And, if you want zebra striping, you need to select every other .list-item, ignoring the .separator. So…

li:nth-child(odd of .list-item) {   background: lightgoldenrodyellow; }

But only Safari supports that… so you can do:

@supports selector(:nth-child(1 of .foo)) {   li:nth-child(odd of .list-item) {     background: lightgoldenrodyellow;   } }

If you didn’t care what the fallback was, you wouldn’t even have to bother with the @supports at all. But say you do care about the fallback. Perhaps in the supported situation, the zebra striping does the heavy lifting of the UX you are shooting for, so all you need for the seperator is a bit of space. But for non-supporting browsers, you’ll need something beefier because you don’t have the zebra striping.

So now you can style both situations:

@supports selector(:nth-child(1 of .foo)) {   li {     padding: 0.25em;   }   li:nth-child(odd of .list-item) {     background: lightgoldenrodyellow;   }   li.separator {     list-style: none;     margin: 0.25em 0;   } } @supports not selector(:nth-child(1 of .foo)) {   li.separator {     height: 1px;     list-style: none;     border-top: 1px dashed purple;     margin: 0.25em 0;   } }

If we get the @when syntax, then we can write it a little cleaner:

/* Maybe? */ @when supports(selector(:nth-child(1 of .foo))) {  } @else {  }

Anyway. The end result is…


Not Supported

There is a JavaScript API for testing support as well. I wasn’t sure if this would actually work, but it appears to! This fails in Chrome and passes in Safari as I write:

CSS.supports("selector(:nth-child(1 of .foo))")

While I was putting this together, I was thinking… hmmmmmmm — what CSS selectors are out there that have weird cross-browser support? It’s really not that many. And even of those that do have weird cross-browser support, thinking of the number of use-cases where you care to actually wrap it in an @supports (rather than just let it fail) is fairly few.

The ::marker pseudo-element would have been a great one, but it’s pretty well supported now. I was thinking the case-insensitive attribute selector, like [href$ ="pdf" i], would have been a good one, but nope, also well supported. Same deal with the comma-separated :not(a, .b, [c]). Maybe something like :fullscreen / :-webkit-full-screen would be interesting and useful because it’s uniquely not supported in iOS Safari?

The post @supports selector() appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.



Meet `:has`, A Native CSS Parent Selector

The reasons that are often cited that make container queries difficult or impossible is things like infinite loops—e.g. changing the width of an element, invalidating a container query, which changes the width again, which makes the container query take effect, etc. But that was solved with containment. A “parent selector”, or :has as it is now been officially dubbed (I like it, that’s how jQuery rolled, although Adrian pointed out a tweet noting that it’s more versatile), has traditionally had similar problems. Things like requiring “multiple pass” rendering which is too slow to be acceptable.

Brian Kardell says:

Primarily, even without :has() it’s pretty hard to live up to performance guarantees of CSS, where everything continue to evaluate and render “live” at 60fps. If you think, mathematically, about just how much work is conceptually involved in applying hundreds or thousands of rules as the DOM changes (including as it is parsing), it’s quite a feat as is.

Engines have figured out how to optimize this based on clever patterns and observations that avoid the work that is conceptually necessary – and a lot of that is sort of based on these subject invariants that has() would appear to throw to the wind.

The fact that there is a spec now is super encouraging, and that it has Igalia’s eye on it. Apparently, some of the performance problems have either been surmounted or, through testing, determined to be negligible enough to remain a shippable feature.

Adrian Bece digs into it all!

The team at Igalia has worked on some notable web engine features like CSS grid and container queries, so there is a chance for :has selector to see the light of day, but there is still a long way to go.

What makes relational selector one of the most requested features in the past few years and how are the developers working around the missing selector? In this article, we’re going to answer those questions and check out the early spec of :has selector and see how it should improve the styling workflow once it’s released.

Let’s cross our fingers. I’ve been watching this for 10 years and trying to document use cases.

Direct Link to ArticlePermalink

The post Meet `:has`, A Native CSS Parent Selector appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Did You Know About the :has CSS Selector?

File this under stuff you don’t need to know just yet, but I think the :has CSS selector is going to have a big impact on how we write CSS in the future. In fact, if it ever ships in browsers, I think it breaks my mental model for how CSS fundamentally works because it would be the first example of a parent selector in CSS.

Before I explain all that, let’s look at an example:

div:has(p) {   background: red; }

Although it’s not supported in any browser today, this line of CSS would change the background of a div only if it has a paragraph within it. So, if there’s a div with no paragraphs in it, then these styles would not apply.

That’s pretty handy and yet exceptionally weird, right? Here’s another example:

div:has(+ div) {    color: blue;  }

This CSS would only apply to any div that directly has another div following it.

The way I think about :has is this: it’s a parent selector pseudo-class. That is CSS-speak for “it lets you change the parent element if it has a child or another element that follows it.” This is so utterly strange to me because it breaks with my mental model of how CSS works. This is how I’m used to thinking about CSS:

/* Not valid CSS, just an illustration */ .parent {   .child {     color: red;   } }

You can only style down, from parent to child, but never back up the tree. :has completely changes this because up until now there have been no parent selectors in CSS and there are some good reasons why. Because of the way in which browsers parse HTML and CSS, selecting the parent if certain conditions are met could lead to all sorts of performance concerns.

Putting those concerns aside though, if I just sit down and think about all the ways I might use :has today then I sort of get a headache. It would open up this pandora’s box of opportunities that have never been possible with CSS alone.

Okay, one last example: let’s say we want to only apply styles to links that have images in them:

a:has(> img) {   border: 20px solid white; }

This would be helpful from time to time. I can also see :has being used for conditionally adding margin and padding to elements depending on their content. That would be neat.

Although :has isn’t supported in browsers right now (probably for those performance reasons), it is part of the CSS Selectors Level 4 specification which is the same spec that has the extremely useful :not pseudo-class. Unlike :has, :not does have pretty decent browser support and I used it for the first time the other day:

ul li:not(:first-of-type) {   color: red; }

That’s great I also love how gosh darn readable it is; you don’t ever have to have seen this line of code to understand what it does.

Another way you can use :not is for margins:

ul li:not(:last-of-type) {   margin-bottom: 20px; }

So every element that is not the last item gets a margin. This is useful if you have a bunch of elements in a card, like this:

CSS Selectors Level 4 is also the same spec that has the :is selector that can be used like this today in a lot of browsers:

:is(section, article, aside, nav) :is(h1, h2, h3, h4, h5, h6) {   color: #BADA55; }  /* ... which would be the equivalent of: */ section h1, section h2, section h3, section h4, section h5, section h6,  article h1, article h2, article h3, article h4, article h5, article h6,  aside h1, aside h2, aside h3, aside h4, aside h5, aside h6,  nav h1, nav h2, nav h3, nav h4, nav h5, nav h6 {   color: #BADA55; }

So that’s it! :has might not be useful today but its cousins :is and :not can be fabulously helpful already and that’s only a tiny glimpse — just three CSS pseudo-classes — that are available in this new spec.

The post Did You Know About the :has CSS Selector? appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, ,

Parsel: A tiny, permissive CSS selector parser

If you’ve ever thought to yourself, gosh, self, I wish I could have an Abstract Syntax Tree (AST) of this CSS selector, Lea has your back.

If you’ve ever thought that same thing for an entire CSS file, that’s what PostCSS is, which has gone v8. PostCSS doesn’t do anything by itself, remember. It just makes an AST out of CSS and gives it a plugin interface so plugins can be written to transform CSS with it. No shade on PostCSS, but it is funny how saying “We use PostCSS” doesn’t mean anything the way “We use Sass” does.

Direct Link to ArticlePermalink

The post Parsel: A tiny, permissive CSS selector parser appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,

CSS :nth-of-class selector


A Use Case for a Parent Selector

Having a “parent selector” in CSS is mentioned regularly as something CSS could really use. I feel like I’ve had that thought plenty of times myself, but then when I ask my brain for a use case, I find it hard to think of one. Well, I just had one so I thought I’d document it here.

A classic parent/child:

<div class="parent">   <div class="child"></div> </div>

Say it makes a lot of sense for this parent to have hidden overflow and also for the child to use absolute positioning.

.parent {    overflow: hidden;    position: relative; }  .child {    position: absolute;  }

Now let’s say there’s one special circumstance where the child needs to be positioned outside the parent and still be visible. Hidden overflow is still a good default for the vast majority of situations, so it’s best to leave that rule in place, but in this very specific situation, we need to override that overflow.

.special-child {    position: absolute;     bottom: -20px; /* needs to be slightly outside parent */ }  /* Not real, but just to make a point */ .special-child:parent(.parent) {    overflow: visible; }

That selector above is fake but it’s saying, “Select the parent of .special-child,” which would allow that override as needed. Maybe it’s like this:

.parent < .special-child {  }

…which is selecting the element on the left rather than the right. Who knows? Probably both of those are problematic somehow and the final syntax would be something else. Or maybe we’ll never get it. I have no idea. Just documenting a real use case I had.

You might be thinking, “Why not just use another special class on the parent?” I would have, but the parent was being injected by a third-party library through an API that did not offer to add a class of my choosing on it. Ultimately, I did have to add the class to the parent by writing some custom JavaScript that queried the DOM to find the .special-child, find the parent, then add the class there.

Do y’all have some other use-cases for a parent selector?

The post A Use Case for a Parent Selector appeared first on CSS-Tricks.


, ,