 Welcome to the CSS podcast where we cover all things CSS from accessibility to Zindex. Today we're talking about nesting. This is a highly requested feature for CSS that's existed in preprocessor tools like SAS for almost a decade. So it's really exciting to talk about and it's really great that the CSS Working Group has been working super hard on making this a reality. And the first working public draft for nesting is inching towards completion with an implementation behind Canary 109 that you can try today. No preprocessor tools needed. The spec is still settling on final syntax though, so this episode is subject to change. Adam, for those people that don't know what nesting is or haven't used it, can you give us a breakdown? I can, but we're gonna play a game first now. We're just gonna play with some words because nesting is funny and it sounds like birds should be sitting in this CSS feature, but they don't. So I'm gonna give you some clues and then you're gonna have to figure out what kind of nest I'm actually talking about because these aren't CSS nesting, but they're fun nesting anyway. Okay, so when all the kids are gone, what's that called? Oh, leaving the nest. An empty nest, yes. Oh, empty nest. I mean, that's pretty close, good. Okay, the spot on a boat where a pirate looks out, you know, to land hoe, what is he sitting in? Or she, I don't know. Oh, I don't know, I don't know this. If I do this, does he give you a hint? No, what was that? This is a crow's nest, it's a crow's nest. A crow's nest, thing I learned. All right, anyway, okay, I'm not gonna be very good at this. It's more fun that way. Okay, a kind of chocolate. Oh, Nestle. Yes, Nestle, that one's the trickiest of them. I think you got a nice, okay. Do not try and get honey out of one of these. Oh, a bee's nest. Bee's nest, yep. Okay, you wake up, you got bed head, you slept for 12 hours, your hair looks like a. A bird's nest. Rats nest. Rats nest, I guess I could add some better clues there. Okay, this one's a movie, Jack Nicholson, one flew over the. Cuckoo's nest. Okay, that was all I got. Okay, that was, I did okay. You did okay. I said that was a C, plus. I definitely didn't wanna stump you, I was just like, you know, maybe I was looking at nesting too long and I was like, it needs a joke, it needs a nest joke somewhere. Those were good, those were good, I like that. Okay, cool, so a silly intro out of the way, let's talk about nesting and what is it? Cause there are a substantial amount of people that have no idea what nesting is as it's come to, I've come to find out. So let's talk about it. It's when style rules can go into other style rules. That's it at the basics, but it's also like thinking of it like inheriting or continuing a parent selector context. So think of like a field set and a legend element or like a UL and an LI. These usually move together in HTML and your CSS. But in CSS, we have to repeat the parent element over and over again on our selectors. But with nesting, you can open up the UL selector, type some styles, then nest LI selectors and continue to style them. You've now co-located your list item styles with the list container, making it easy to find the related styles as well as not repeating that UL over and over again as you add more styles to different list items. It can feel a lot like nesting objects in JavaScript or HTML, but it's in CSS. Just carrying that selector context with you to a new selector. Totally. So this is like a DevX improvement, like a developer experience improvement. It reduces redundancy of like writing code. It helps you write drier code. And it's something that we've actually seen in a lot of different ways through a lot of different strategies. So there's been things like SAS nesting, hugely popular, less stylist, CSS and JS nesting. And there are more than that, but native CSS hasn't been able to adopt any of these strategies because there's a lot of ambiguity that the CSS parser just can't match that something like SAS can. But give it time and a few brains and a solution can emerge. So Adam, what has emerged? Where is the spec today and what does the syntax look like? Yeah, it's funny. I wonder was it Lesser SAS that did it first? Do you remember? Lesser was the first pre-processor. Lesser was the first. I don't remember. I feel like Lesser had the same feature set as core SAS, but SAS had an easier integration with CSS with the SCSS syntax. And that's why SAS became more popular. Where you didn't have to have .less files. You could just use .scss and use the same syntax as CSS. Right, that TypeScript upgrade thing. Yeah, I feel like Lesser had it first, but SAS kind of gained the popularity and did it better. Another funny thing too about that ambiguity is like, it's not that CSS can't do what SAS has done. It's that SAS is handling this ambiguity and just no one knows that it's there. So like you can do certain combos and SAS is generating tons of stuff. And it's just sometimes throwing things away for developers, they just don't know it. After all these years, selectors that you've written using nesting have been thrown away because they're like, we're pretty sure the developer didn't mean to do these other 10 versions. SAS does a lot of really smart stuff. And I mean, there is power in being a pre-processor that looks at your code and transforms it into a different format that we just can't do with native CSS, dynamic CSS on the fly. With SAS, you have some benefits that you can't do the same kind of abstract syntax tree parsing because it does transform things before they get out there. But I'm saying also that it's like not a benefit. It's actually been baggage in a lot of ways. There's been developer ergonomic issues with it. There's been difficulty in maintaining it. So it's like, we'll get into an example later where the native nesting or just like the built-in nesting that we're gonna have could do this certain nesting scenario that would have exploded into like 27 versions in SAS, but it's only three in the nesting spec. So it's kind of like, it's an evolution I'd say. And it's sort of a little bit more precise. Anyway, we'll get into all that stuff. Okay, so first, what does this new nesting syntax looks like? I'm gonna start with a warning, which is the syntax just underwent a really big change. It became more relaxed and that's a good thing. And it became more relaxed in the usage of the and character. So if you've been following the spec for like the past four or five years, you'd be really familiar with the and character. And if it originally required and pretty much everywhere, well, not pretty much, it did require it everywhere. And anytime and wasn't gonna be at the front, you had to use at nest, which allowed you to put and somewhere other than at the beginning. But that requirement has been relaxed recently just a couple of weeks ago, actually. And and is now optional in many cases. And there's also no at nest style anymore. So the tool I'm gonna read is that you aren't required to use and as often, hence the verbiage of it being more relaxed, but it is still required in certain situations. In fact, it might be many situations. And we'll cover some of those in this episode. Yeah, so also I will use the term ampersand in relating to that little and symbol because I think it's a little bit more clear. And I just, I don't know, I'm used to calling it ampersand. I love ampersands. Yeah, they're a cool symbol. Okay, I'll call it an ampersand too. What did I call it, the and symbol? Yeah, ampersand. But it's the same thing. So let's talk about some essential rules to know with this new syntax. So first to be safe, you can always use an ampersand if you want to. You can always start your nesting line with an ampersand. And when you're nesting, if you want to visually think about it, it's say you have like a div and then you have your open curly brace and then on the next line, you might want to have like an indent and an ampersand and an element. Like that's nesting. That's where you have things nested inside of one another in your CSS. So you can always have an ampersand. And ampersands are great because I like to think of them as anchoring mechanisms. So they anchor to the top of the rule set where if you have an ampersand in a space and an element, now you have the top of the rule set characters so say div space and then the element. So that's what I do. If you don't have any space between the ampersand and the element, then it anchors directly. So you could have a class under it if you had ampersand with no space, div.class. So you can do that. You can always have ampersand if you want. But with this new syntax, you can get rid of the ampersand if you have something that is not an indent value. So if you have some sort of syntax and if you want to nest with a space between the topmost element of the rule set and the nested elements. So that means that you could do this with anything that is not like a alphabetical character, essentially, like adopt to indicate a class or a greater than sign to indicate a direct child or a colon to indicate something like a hover functionality. Although you'd probably want to use ampersand, colon hover because you'd want it to be directly on the parent. Yeah, that'll be in the got you this section. Yep. But another solution is to use like colon is or colon where to wrap a direct element without using the ampersand for nesting. So for example, you mentioned field set and legend at the earlier in this episode. So both of those are elements or idents in CSS language land. So if you were to start with a field set and then you wanted to nest a legend inside of it, you can do that in one of three ways. First, you could do field set, open curlies, ampersand, space, legend. So then you're using the ampersands, clear that you're starting to nest there. Another way to do that is field set, and you have your curlies, greater than space legend. And now you add the direct descended character to nest the legend from the field set. So the parser knows that nesting is occurring. And the third way is you could do field set, open your curlies, and then colon is and then in parentheses, legend. So this wraps legend and is selector, so that no longer starts with an ident. It no longer starts with just like a basic element. It actually has some syntax there and it is a functional pseudo class selector that wraps that element. So the prior syntax the working group resolved on was a way to always use the ampersand. And as Adam mentioned, this syntax is more relaxed. You could still always use the ampersand if you want to, but you don't have to if you have some sort of non-element or a non-ident character that it starts with. And if you're wondering why this weird rule, like why can I use, not have to use ampersands for nesting a class, but I have to use them for nesting an element, the answer comes down to this, yes, parser. So imagine you have like a card and you wanted to nest a div if you had like dot card and then open Curly's div, the parser needs to determine whether that div is a div element and you're beginning a nesting scenario or the beginning of a property value pair. So as it starts to look at every character as it's reading the CSS, it sees something that could possibly be a property and could possibly determine some style for that parent card, but that's not what you mean. You want to start a nesting. And CSS can't like look back and forth because that would really greatly degrade performance. It looks forward and you don't want to do that because it would be just super, super slow. So while we'd love to have parity with something like SAS with the beautiful nesting that doesn't require any sort of syntax, it just doesn't work in CSS. So that's why we have to have these weird rules for certain things, but again, you can just be consistent and use an ampersand if you want. Nice, excellent introduction there. So cool, let's cover like some more examples. We've got like, let's come back to the rule of just don't start nesting with an IDENT. Let's cover some really basic use cases to develop a mental model of the syntax where we're not using IDENTS. So let's talk about some classes. Let's say we have grid, a grid class and some item classes. We'll start our grid selector with dot grid. We'll open up our curly brackets. We can write some styles. And then we can just say dot item and open up curly brackets and write some more styles. And this nesting example is the same as if you'd write written dot grid, open up curlies in the grid styles, close your curlies and then dot grid space dot item, open up your curly item styles. Because the parser saw the dot before the item and knew that a class selector was coming. It then continues the item styles as if there was a space between the grid class and the item class. So it's just like how many preprocessors work where nesting like this is assumed to put a space or like a descendant child selector between the classes. Let's say you wanted to merge two classes together instead of having a space between them. This is where you have to use ampersand because how else would you know that you wanna do this really? So this is also kind of going back to that anchor example where you might have something like dot grid and open your curlies. You can have some grid styles. And then if you have ampersand dot item with no space in between the ampersand and the dot, then when you nested that, it would be dot grid dot item. So what this would tell would mean is I am styling something that has both the class of grid and the class of item. I'm not styling an item class inside of a grid class parent. I am looking for something that has both classes. So the ampersand tells the parser exactly where the previous selector context should go, no assumptions or magic about it and you're in full control. Yep, that ampersand becomes like a signal just to be really, really clear about where you want it to go. And yeah, it gives you that optionality and it's really nice. And just to be like well-rounded, let's cover using the direct descendant selector like we did with field set and legend, but this time we're not introducing the direct descendant character, the greater than sign, to disambiguate an identity. We're gonna use it to just target nested item classes inside of a grid class. We have like dot grid, open up your curlies, write some grid styles, then you direct descendant space dot item, open up your curlies, write some more styles and close your curlies out. And you've got yourself say dot grid set of styles and a dot grid direct descendant dot item set of styles. And it can be really, really nice using these. So it's almost like there's a new advantage to kind of keeping and using these direct descendants and various things, these little non-idents that will allow you to kind of avoid any collisions or things that you're unexpected. Yeah, good call. And another cool thing is that you can nest media queries and this one comes in super handy as you don't need to completely navigate to another part of your file, like all the way down the bottom to see what the dark mode styles are or what the large view port styles are. You can actually just co-locate them right with the rest of your styles as you write the base styles for your element. Oh yeah. So it's great. I feel like this is really useful for components like cards or sidebar or footer sections. Super, super useful. So you could open up something like a dot card and then have the styles there and then inside of that put at media. And then in parentheses, width is greater than or equal to 720 pixels, for example, and then write the styles there for what that card would look like if it was on a tablet or larger. And that would be the same as writing the opposite way of doing like at media and then the media query and then within that the card styles. So it's just a super handy thing to do with nesting. Super handy. One of my favorite use cases is adaptive colors or building light and dark themes. You got colon root, you open up, you put all your light theme custom properties, then inside of that root, you nest at media prefers color scheme dark and then you write your dark custom properties or you overwrite those light ones with their dark values or whatever it is you need to do but you can nest them all right next to each other right in the same selector context. It's really great. But I have a question. So for these two with the at media for either size or prefers color scheme, do you need a trailing ampersand to specify that you're actually putting this ahead of the top of the whatever you're nesting within? Nice. So what we've done here with our two examples are the basic use case of nesting media which implies an implicit and that's placed for you but you can continue nesting further inside of a media query if you want to. So you could have like dot card at media prefers color scheme dark inside of there you could say and dot active and you'd be saying it's a card dot active in dark mode. And so you have that ampersand character comes with you inside of an at media allowing you to build more selectors and to nest even further inside of that context. So could you also do something like say you have dot card and then you have like dot meta ampersand at the end of the line because in SAS you can do that. And what that would do is this example makes no sense but say you have like a dot card and then dot highlight ampersand that's a better example. So what this would do in SAS is it would create a selector that is dot highlight space dot card or would flip it kind of like what this is doing implicitly is that still an option with the nesting spec? Yes. And I think we'll get into these examples there where we're not just continuing to build out a selector to the right. So what you're describing is take the parent context and allow me to place it somewhere other than the beginning whether it's the middle or the end of a new selector. And so yeah, you've got like dot dark is a good example. So you have a card, let's say you don't wanna use the prefers media query you wanna use a class name that's on your HTML. So you've written your dot card styles you've written a bunch of styles then you say dot dark space and and that would make a new selector called dot dark space dot card and then you could write your dark card styles in there. So it's essentially kind of like a parent selector mentality where you're starting fresh with a new selector but you get to take the context of what you're nesting inside of and put it anywhere you want. So that ampersand character kind of feels like a macro or like a shorthand for the context that you're working with and whether or not you're inside a media query you're inside of other nesting that ampersand always represents that for you and allows you to put it anywhere that you want. So the and can go in the beginning to save you from not having to do a space or you can say dot dark and then put your ampersand anywhere that you want. So it is just like SAS in that way. Very cool. And then with these media queries would a trailing ampersand be the same as not having it because it's implicit? Yeah, you can do that if you want. So the original spec didn't have implicit media query nesting. So like we gave this example of root. So we had root light properties and then app media prefers color steam dark dark properties with no additional selector in there. It just assumes you wanna put those inside of a root selector. But if you don't like the assumption you can open up app media prefers color scheme dark open up your queries say and open up your queries and be very explicit that you're bringing in root to here inside of this media query. And then, you know, you could hit enter after that and do and space something else or do dot dark space and and so you have that full power of putting and somewhere where you need it. But media queries are the only scenario where it's kind of optional. It's kind of implicit. As long as you don't write more styles inside of there. So there's kind of an edge case, but yeah. Okay, cool. That's super useful. Thanks. You're welcome. Well, let's get into some of the gotchas cause, well, I mean, we're kind of just talked about a few, but right. But like one of the main ones here is it's it's SAS like, but it's not SAS. And I think this is gonna be a gotcha. So the whole concept of avoiding IDents doesn't exist in SAS. So if you're copying and pasting from SAS and you'd be like, oh, nesting is it's built into the browser now. I'm just going to paste all my styles over there. You're going to need to add ands here and there where IDents were used. So if you were targeting divs, if you were targeting field sets, targeting sections, mains, any of that stuff, you're going to have to put an and in there to be explicit about what you meant because the IDents were going to confuse it. Another thing that folks love to do in SAS is kind of concatenating selectors together, treating them like strings. So we do this in BEM all the time where you have like this partial of a selector, whether it's like a dash-dash modifier or maybe you're combining underscores. So you have like a block underscore and another underscore like for an element. People loved to build their selectors like there were strings. And that's not going to work in the built-in nesting. And that's because SAS did treat selectors as strings, but the CSS part or parser treats them as objects. So you'll need this mental model shift of thinking about selectors as live lists of stuff and not strings. And this is why you saw like an example that's like dot some dash block and underscore underscore modifier or dash-dash modifier. These won't work because modifier looks like an identity. It looks like an element. Maybe it's a custom element. It didn't start with a dot. It didn't start with a direct descendant selector. It didn't start with an and. And again, these things aren't strings being put together. These are live selectors. These are rich objects. And so you have to treat them that way. And it's going to be kind of a mental shift for people. Wow, that's a really good call because I definitely remember writing selectors this way where I would use the ampersand to write out values in selector when the BEM model was how I namespaced. I think that we have more advanced namespacing now with things like CSS modules. So I haven't really done that in a few years, but yeah, it's really key to note that these are not strings. These are objurnus. Yeah, that quest for dryness where you're like, I just don't want to type stuff over and over again. In some cases, you will have to because these aren't strings. Yeah, another interesting fact is that is powers things under the hood when you want to have a comma in your parent selector and target multiple items. So for example, if you had something like input comma button open curly is dot active and then some active styles, this would convert to colon is and then in the is selector input comma button and then curly is then dot active and then the active nested styles. So this might sound innocent enough or maybe it doesn't, but it can mean that the way that your brain thought you were nesting isn't the way that the cursor understands it. And you may need to be conscious about the abilities of is especially the specificity. So it might be worth brushing up on episode 47, which is all about is and where. Yeah, and if you're wondering right now, well, why does is power this? There's a couple of reasons. It allows arbitrary grouping, which is required for nesting to work for scenarios like let's say you have field set, open up Curly's and legend comma and input. And then you have inside of there and hover. So you've kind of gone triple levels in your second level had a selector list, right? There was a comma, there was legend and input there. So what that de-sugars to is field set space colon is parentheses legend comma input and parentheses colon hover. And the this shows the power of grouping in the middle of a selector, right? This is one of the value adds of is in general is that it got rid of all these like repeated selectors that we had to do because we can now group things in the middle, beginning or end. Well, that power comes into here as well. It's not just a power. It's like a heavy reduction of complexity as well so that you can nest a selector list inside of something else and then do a selector list again. And essentially the rule is if you're making a selector list where you've put a comma, expect an is to wrap that. And this grouping also reduces the amount of work on just de-sugaring as it doesn't need to expand all the cases like pre-processors do. So think like three selectors with three nested selectors with three nested selectors in that de-sugars to in this built in strategy is three selectors space is three selectors inside the parentheses space is three selectors. So it's just three select and it looks it's like it fits on one line but in a pre-processor, it literally de-sugars to 27 created selectors and they're hideous and confusing. But that's the reality of what you wrote and the cool reality of this new CSS future with is is that it can be succinct and elegant. So it's kind of cool. I don't know, I like it. That's really interesting. There's so much under the hood that is like clever. So another gotcha that I mentioned earlier is when you were nesting with pseudo classes and elements. So you might think that intuitively, you know, something like hover, it doesn't start with an identity. It's not an element. I could just nest that directly but what you would be doing by doing something like button and then nesting colon hover is it would output as button space colon hover which doesn't mean anything. You need to connect the two so that you are telling CSS that you're looking to apply a state or a style when the button is in the hover state. So you need the ampersand there and you want to write ampersand colon hover so that it converts to button colon hover and there's no space between the nesting context button and the hover pseudo selector. So this is the same case with pseudo elements like before and after. You need to do ampersand colon colon before ampersand colon colon after and it is a good reminder that you can always use the ampersand if you just want to be really explicit about your intent for if you want to space between the nesting context or if you don't. Yep, all those cool pseudo classes we have in CSS they're going to need an and in front of them almost always. So yeah, that's the gotchas. You're going to want to write it without it but you're going to realize something's not working and you got to put that hand in there. Another gotcha is around order. So like what happens if you add more styles after you nested something? Like you have a dot grid, you open up your queries, you write some grid styles, you know background, display, blah, blah, blah. Then you do some nesting like ampersand space dot item. So now you've got some item styles in there. You close your curly brackets on that dot item and then you write some more styles before you close the curly on the grid. What happens? It's like you wrote some styles, you nested, then you wrote more styles. And well, don't worry about it. It turns out that the more grid styles get hoisted up to where the other grid styles are. This is a lot like JavaScript variable hoisting if you're familiar with that. So like, because otherwise in general nesting that occurs after other nesting like hover and focus within like these follow normal cascade order. So the last one to be defined is going to win over the earlier ones. But the case with just sort of more styles after nesting, those are going to be hoisted up. So you might try to leverage that in some way to like get an advantage of maybe I wrote the styles and they're going to win, but they don't. They all get hoisted up and grouped together in the beginning. So you can also nest in different contexts. You can nest in a cascade layer. You can nest in scope or you can nest in both. And Adam's been doing a ton of work around nesting, exploring it, kind of being one of the folks that are leading the headspace behind nesting. So we've got a demo for you in the show notes if you want to explore that a little bit more. So I guess to wrap, what's next for nesting? Yeah, it helps when I'm doing all these explorations that I'm a spec editor. So that definitely, you know, I know all the context and know what to try to go poke. I'm like, ooh, maybe the engineer forgot this one. Okay, so what's next for nesting is we need to settle on a syntax. The syntax that we just described is, well, I don't know, we could change it probably won't, but maybe it will. I don't know, I thought the previous syntax wouldn't change and then it did. So who knows, but- I feel like this, so we already have gone through so many options. I can't imagine it changing too much at this point. So the next thing about what's next for nesting is there's a lot of edge cases now. The new syntax created a lot of edge cases. Whereas the previous when ampersand was always required, a lot of stuff wasn't ambiguous, but we've introduced this partial ambiguity, right? That's when now we're having to talk about like idents and remembering that you can't just nest a div without an and that's introduced a lot of potential parsing errors and just errors in general. So we're gonna have to work out a lot of these edge cases and write tests for them since we kind of complicated the syntax a little bit. We made it easier for authors, but we complicated the implementation details. What's next is also we need folks to try it out. So and participate in the conversations, which people have been good at participating in the conversations. You can try it out with post CSS. You can try it out in Canary. So a lot of tools have already updated their spec following nesting preprocessor transformations to work with this new spec, which is kind of cool. We also need to teach DevTools how to look at it, which I was just jamming with a DevTools designer today working on what that's gonna look like to have DevTools represent nesting. Whereas right now it just sort of is in the styles pane all crossed out looking silly. So if you try it out today and it looks like it's not working in the styles pane, but your styles are applying, that would be because the DevTools are completely inexistent for this feature so far. And what? Yeah, yeah, we're working on it. Depends on what you're listening to this show. That's true. Hey, maybe by the time it comes out we'll have some DevTools, we'll see. In November, 2022, everything breaks in DevTools. And then the last thing that's next is we gotta find more ways to have it be as enjoyable as we can. So we've articulated today this relaxing of the syntax that this is just nesting version one. You could expect a nesting module two to come out later and nesting module three, each iteration, adding features, more sugar, it's sweeter, it's more enjoyable to use, all that sort of good stuff. So we're not done just cause nesting one is heading towards completion. It doesn't mean we will be done. So that's where nesting is headed. Yeah, that's a good wrap. And thank you, Adam, for bringing nesting into our lives. I just poured water on it. Tab had planted all the seeds, it looked solid and it just was kind of a little dormant. And I was like, Tab, can I show up and just help you carry this? Love. But that's all we have for our rundown on the current state of CSS nesting. Thank you for joining us. This was a fun episode. Yeah, if you have any questions we'd love to answer them on the show. Tweet us with the hashtag CSS podcast. I'm at Yuna, that's at UNA. And I'm at ArgyleLink, A-R-G-Y-L-E-I-N-K. Your question can help a lot of people. A lot of people are gonna wanna know about these nesting edge cases and gotchas. So go ahead and ask away. And if you like the show, please give us a review on whatever podcast app you're using or share this podcast with a friend or a coworker because those reviews help other people discover our show. And that means that we get to spend more time making it and delivering cool stuff for you in audio form. You know it. Thanks y'all. Look forward to your questions. We'll see you next time. Bye. Bounc. Bounc. Bounc. Bounc. Bounc. Bounc. Bounc. We have some outro music I think we do. Something like that.