I feel like this article doesn't explain how `nth-last-child` works clearly.
The example uses (n + 3) in a list of five items. Start counting from the 3rd item - ok, the visualization[1] shows us counting from the 3rd-from-last, so now I'm already confused. And then we count "until the end", which actually shows us counting back toward the beginning[2]. And we end up with 3 items. Are they the last 3? The first 3? I can't tell. The author should have chosen a number that didn't sit at the halfway point of the total number of items.
The description makes it sound like we start at 5 -> 4 -> 3, then select 3 items, so we select 3 -> 4 -> 5. But that's a tautology. I think what it means is that we start at 1 -> 2 -> 3, and select 3 -> 4 -> 5... is that correct?
I entirely agree that the explanation in the article is somewhat confusing, because we're selecting 3 items starting from #3 in a list, counting both from the top and the bottom. So which of those three options does the 3 stand for?
Yes, I had the same experience as you and had to not just read but play around with the MDN docs example to get it.
I still think the functional formatting is confusing. It makes sense, the pattern applies to every "A*n + B"th index (counting from the end) where n is every integer from 0..inf. But in my mind I'm just providing an offset and a step size/direction: (offset=B, step=A) or just (B A). I'd be interested to know why it's the way it is.
There are so many nice new things in CSS, along with :has() and :nth-last-child there is also :is(), :not() and the new nested syntax. All make working with raw css much more pleasant. No more SASS/LESS, and (hopefully) soon no more PostCSS hopefully!
> No more SASS/LESS, and (hopefully) soon no more PostCSS hopefully!
I keep running my CSS through SASS just so I can use single line comments. If they'd add those to CSS natively...
Can't wait to drop PostCSS as I've had its plugins, extensions, whatever they're called break my stack (through subtle bug or breaking changes) more often than Webpack plugins, and that's saying something.
It's to do with how the parsers are implemented in browsers I believe. I think I read that it would require a large rewrite of them all and potentially make them slower.
Personally I think it good that it makes it more explicit, even for none element nested selectors I'm adding the & to make it clear. So for a class "& .something".
Edit:
Found it in the spec
> Nesting style rules naively inside of other style rules is, unfortunately, ambiguous—the syntax of a selector overlaps with the syntax of a declaration, so an implementation requires unbounded lookahead to tell whether a given bit of text is a declaration or the start of a style rule.
> ... that is, the parser might have to hold onto an unknown amount of content before it can tell which way it’s supposed to be interpreting it. CSS to date requires only a small, known amount of lookahead in its parsing, which allows for more efficient parsing algorithms, so unbounded lookahead is generally considered unacceptable among browser implementations of CSS.
Little nitpick, but :not() is not a new feature by most definitions of new. It has been in browsers for over a decade (closer to 15 in browsers not related to IE), which is an eternity in the world of web technology.
I'm all for CSS getting more powerful but there's an interesting flip side: the increased expressiveness may make it more difficult to reason about.
Even an advanced use of grid is tricky to understand. Now combine this with the new parent selector, container queries, layers, and many other new goodies. There's a lot of (new) cognitive demand placed on already burdened developers.
I think this is one of the reasons why a lot of these features have very little or slow adaption. Even something essential as grid, usable for years now, is rarely spotted in the wild.
Disagree. Have been using and developing with CSS as my focus for ten years now. If you take the same amount of time you would take to learn the ins and outs of [pick any other language], you would have a better time of it.
I'd like to add a reason that you do not list and one that I constantly run into is that due to the lack of understanding, large applications have been built and it is simply not cost-effective to re-wire the whole thing using better approaches. The only time I get to apply features and structures that are less than five years old to projects is when it's a green field opportunity. Otherwise, the budget just doesn't cut it.
Personal comment/response:
I will say that CSS seems to be for a particular _breed_ of developer. Almost designers, but not quite. My perspective of this comes with also using JavaScript for the same amount of time. The thinking I use in both of those domains differs at times quite dramatically.
I'll use the number of pages of OReilly's foundational programming books as an example of anecdotal complexity of three languages: JavaScript - 704 pages, PHP - 842 pages, and CSS - 538 pages (the new one coming out will be 1090).
No frontend co-worker that I have had spanning the past ten years has ever worked through the _old_ CSS one from 2006, at all. Even those that loved CSS, did not own a copy of that one. It covered everything CSS and I read it cover to cover.
Without a commitment to CSS on par with commitments that developers make everyday to any given framework or other programming language, anyone trying to wield CSS on the same level as the other tools will continue to be disappointed.
And as long as the culture of web development continues to de-value solid in-depth understanding of both HTML and CSS, then yes, quite a lot of us will remain over-burdened in our day-to-day responsibilities.
That's just my take. As I am also a musician who plays both piano and oboe - I can use that as an analogy as well. I can teach _anyone_ piano. I cannot, at all, simply teach anyone oboe. They _must_ have an inclination towards it and I can literally bet my life savings every time that really good oboists also seem to be really quirky humans. I would put passionate CSS developers at the same table and I would expect them all to get along.
Although :has still shouldn't be use because the spec isn't finalized (and so it's (rightfully) not supported by Firefox), another incredible post.
For the header layout code, the SASS example will be valid CSS once the nested CSS spec is approved & live right? (Although that probably won't be widely implemented enough for use for several years in vanilla css)
Btw, the video for user avatars isn't loading for me
Implementation and spec finalisation are fairly different things these days—in fact, in at least some parts of the web platform (JS), the spec is not permitted to be finalised until two implementations are shipping.
Firefox hasn’t shipped :has() for technical reasons, not because :has() isn’t ready.
(Notwithstanding all this, it is bothersome when incomplete implementations are shipped, which is happening fairly regularly in at least small ways and has already happened with :has() with Safari having to fix important bugs and spec changes after their initial release of it. This kind of stuff often makes feature detection much harder and sometimes impossible.)
> Although :has still shouldn't be use because the spec isn't finalized (and so it's (rightfully) not supported by Firefox)
I don't it's reasonable to hold off on using :has(), something web developers have wanted for close to 20 years just because it's not supported by Firefox. It's far too important and useful.
Especially when 86.93% of the global web audience uses a browser that supports it [1]. And as much I admire Firefox and what they stand for, it's marketshare is 2.77% [2].
Regarding the spec not being finalized… while that may be technically true, the current draft specification has been stable for several months and is what the browser vendors have agreed to implement.
It's been 14 months (March 14, 2022) since :has() first shipped in Safari 15.4 [3]; I think it should be okay to use :has() in production and lets hope Firefox catches up soon.
Note that Statcounter’s data is based on trackers that are blocked by extremely common configurations. This will lead to undercounting of desktop browsers in general (since ad/content blockers are comparatively unavailable on mobile platforms), and Firefox especially.
> Especially when 86.93% of the global web audience uses a browser that supports it [1].
Another point on Statcounter’s methodology (which is what caniuse.com also uses): they don’t track versions for most browsers on Android, instead acting as though everyone’s on the latest version, which is grossly unrealistic.
All up, my vague feeling is that the true figure is probably closer to 80% than to 86.93%. Even if Firefox weren’t a factor, I’d recommend most people avoid depending on :has() for another six months or so. Add Firefox into the consideration, and I’d recommend that most people wait for about a year after it ships in Firefox before depending on :has().
(When I say depending on :has(), I mean “the page will be materially broken if it’s not supported”. Use it completely optionally or with only unimportant style regressions—which can even be as significant as the “you get the same single-column layout as mobile devices even on very large screens” approach was for responsive designs when Grid was newer—and that’s fine.)
That browser marketshare is primarily due to mobile. On desktop they are still around 6% on statcounter. Which is admittedly still not great, but not nearly as disastrous as 3%
They won't implement it until the spec is finalized (is my guess, given that they haven't done so, so far). By encouraging users to use :has already, you're encouraging Chrome's monopoly on the open internet, in direct contradiction to the W3c standard.
caniuse is an important tool, yes, but it's also super important to take into consideration what the literal standard says, which is that :has is not part of the specification. And Chrome directly working in contradiction to that is enormously bad for Internet users. Safari's decision to also implement :has early shouldn't make you bow down to Google in this case imo.
> They won't implement it until the spec is finalized (is my guess, given that they haven't done so, so far).
Last time I checked their bug tracker, it had nothing to do with the specification—they had some technical implementation problems. My take on the situation: if they didn't get derailed by from what I recall were performance problems, they would have shipped it by now.
> but it's also super important to take into consideration what the literal standard says, which is that :has is not part of the specification
I'm not sure we're talking about the same thing; :has() is part of the "Selectors Level 4" specification [1] for a while now. I've read many W3C specifications; there's none of the usual warnings and caveats that are par for the course with experimental or unsupported specifications. That's not to say there won't be changes in the future, but that's true for virtually any recent CSS specification.
As someone who follows this stuff pretty closely (I even read CSS working group meeting notes sometimes), this is the first time I've heard any objection to :has() at all and certainly nothing regarding the spec not being "done".
I don't agree that Safari implemented :has() early; it's been in discussion for nearly 20 years and is something web developers have really wanted for just as long.
The WebKit blog has a pretty good description of the back story why it took so long and what the breakthrough was [2].
The standards commentary is disingenuous (albeit I assume unintentionally).
:is() is also part of the same CSS Selectors Level 4 draft spec and has been shipping in Firefox since version 78 (from June 2020).
There were some open spec questions for :has() a while ago, but those have been resolved for some time. Exact ship date at this point is more a question of relative prioritization by the different browsers. As can be seen in https://bugzilla.mozilla.org/show_bug.cgi?id=418039, the remaining work is around correctness and performance.
This combination feels unnecessarily complicated to me, at least for most of the use cases shown.
Suppose we want to style a list differently if it has five or more items. Since :has can detect any child element, the existence of a fifth item is a sufficient indicator that there are at least five items. So instead of ul:has(li:nth-last-child(n+5)), we could just use ul:has(li:nth-child(5)) which is quite a bit clearer in my opinion.
:has is one of the things I am waiting for, for a while now. Especially when parsing the html output of markdown this would be incredibly useful, as most converters pack images into paragraphs, so you have no way of selecting text paragraphs vs paragraphs with images in them.
Sadly I think people will look back on CSS 3 as the peak of the standard.
people keep pushing, and pushing and pushing to add more and more crap to CSS. they want to make CSS Turing complete, if it isn't already. If you want to style with a programming language, JavaScript has been around for two decades people. with each addition to CSS, you just make it that much slower. people wont stop until CSS can do email:
I was wondering the same. I suspect it has to do with the initial hack needing it since it relies on the general sibling selector (~) which selects all following siblings but no preceding siblings, so it is important to catch the very first element and all its following siblings:
li:nth-last-child(n + 5),
li:nth-last-child(n + 5) ~ li
I don’t think :has has such limitation and I think a simple :has(:nth-child(5)) would yield the same results as :has(:nth-last-child(n + 5)). :nth-last-child(n + 5) will select the first element among five or more siblings, and :nth-child(5) will simply select the fifth element if it exists. I see no reason to write the former over the latter when you’re just wrapping it inside a :has.
Well... firefox has partial support that can be enabled using layout.css.has-selector.enabled in about:config.
The problem is the spec is an unfinalised moving target right now (they've had to remove support for things that were taken out of the spec in their implementation), and when I read their tracking bug, they still have issues related to invalidation.
> Sites should not be enabling draft specifications
Nice in theory, but in reality sites shouldn't be using features that aren't available cross browser.
:is() is from the same draft spec as :has() but has been available in all the major browsers since 2021 (and shipping in Safari and Firefox in 2020, before it was available in Chrome).
Under your suggestion, :is() shouldn't be used by anyone for another couple of years from now, waiting for unrelated issues in the CSS Selectors 4 spec to be resolved so it can be moved to a candidate recommendation.
No, I wouldn't use it for anything critical either. But then, I'm required to support IE11 as long as Microsoft maintains it.
My attitude towards stuff like this is if you're just using it for a bit of visual extra that doesn't break basic functionality, go for it. Comment above was suggesting site was broken without it. That's just a bad idea. We've managed for decades without this, there's no design that requires adding works in progress, even if it only breaks for 1% of your users.
And if something is a spec in progress, definitely be prepared to keep an eye on it for sudden fixes in the future. It's happened more than once.
This feels to me like it's an intended use of the :has and :nth-child - I would worry about it if I thought it was a trick that might stop working in the future, but it seems compatible with the intention of the spec to me.
Both can be true... CSS has a lot of clever tools that feel like hacks. And I'm here for it, even though I almost never want to use most of them. We need more non-JS options in web sites. I just hate that they're so obscure and complex for relatively little gain.
The example uses (n + 3) in a list of five items. Start counting from the 3rd item - ok, the visualization[1] shows us counting from the 3rd-from-last, so now I'm already confused. And then we count "until the end", which actually shows us counting back toward the beginning[2]. And we end up with 3 items. Are they the last 3? The first 3? I can't tell. The author should have chosen a number that didn't sit at the halfway point of the total number of items.
The description makes it sound like we start at 5 -> 4 -> 3, then select 3 items, so we select 3 -> 4 -> 5. But that's a tautology. I think what it means is that we start at 1 -> 2 -> 3, and select 3 -> 4 -> 5... is that correct?
[1] https://ishadeed.com/assets/css-has-nth-last-child/nth-last-... [2] https://ishadeed.com/assets/css-has-nth-last-child/nth-last-...