477: Native CSS Module Scripts, Container Queries, and Writing Scoped CSS
We're talking about CSS Module scripts, container query issues, wondering about writing scoped CSS, Tailwind, and custom property gotcha's.
Chris Coyier and Dave Rupert
Time Jump Links
MANTRA: Just Build Websites!
Dave Rupert: Hey there, Shop-o-maniacs. You're listening to another episode of the ShopTalk Show, a podcast all about front-end Web design and development. I'm Dave Rupert, and with me is Chris Coyier. Hey, Chris.
Chris Coyier: Hey!
Dave: How are you today?
Chris: I'm doing pretty good. I'm doing pretty good. Hey, let's talk about CSS Modules, dude.
Dave: I love it because I learned about it this week. [Laughter]
Chris: Yeah! And I said the thing. I've already kind of committed a faux pas. Why is everybody talking about the word faux pas this week? It's come up a lot of times.
Dave: Fox pass. Fox pass.
Chris: Yeah. [Laughter] It's because we're going to talk about -- I guess we can talk about the existing one, too. There's a popular concept that's been around for years and years and years called CSS Modules, but it's a build process thing, right?
Chris: It's a thing that you stick in your-- You know it's often thought of as CSS and JS because it's built to work with--
Chris: Then you still write class name title in your HTML, and you still write .title in your CSS, but in both places they get obfuscated. They get obfuscated in the HTML and the CSS. That's the scoping that you need and that's why this library exists. It's super popular.
Chris: It's so popular that it's just built into Next.js, for example. You don't have to use it. But if you want to, you just name your file .module.css or .scss, and it just works. It works with Sass. You can use them both. It's just the scoping.
I think it maybe has one or two little syntactical things that it can do.
I bring that up because not only is it really popular, and that's part of the story here, but I like it and use it. I think it's really good. I think just the scoping is something that I really want out of CSS, so we can get to that conversation, too, because that's heated up.
There are lots of combined stories that are happening here, but let's start with the idea that (all of a sudden, I guess) coming out of the Chrome cannon. [Laughter]
Dave: Yeah. In Chrome 93, which is about to come out (based on this episode), it's the next thing. There's this new feature called CSS Module Scripts.
Chris: Which of course they call CSS Modules, hence the overlap.
Dave: Which is called CSS Modules and the "scripts" was added after the fact to be like, "Hey, bro. It's not the same." [Laughter] You know?
Dave: I say import my CSS from CSS file -- whatever my CSS .css -- and then you have to add this little assert curly type equals or type colon CSS.
Chris: Yeah. Yep.
Dave: It's just a little thing.
Chris: Just a little bonus to the end of the single line for importing that CSS file. That's following in the footsteps of another assertion of type JSON that they also rolled out somewhat recently. It's like, here's a way to import JSON into CSS.
It's kind of like, why do you need to do that? Can't you just fetch these things? Dave, can't you?
Dave: Yeah, I mean you could, but you fetch, and then you set a variable, and then you set a thing. What if it's just import this as whatever posts. Import post from post.json. Isn't that easier? That's what I want to do. That's what I want to express. I feel like that's a decent way to do it. You're auto-instantiating a variable that you can use or a global that you can use.
Chris: Yeah. That feels spiritually connected to how ES modules work. From my perspective (and I think most people's perspective) that's applauded.
Even the JSON, when I saw that came out, I was like, "Hey, nice!" You know?
Chris: It's just a few less lines. It's a little syntactical sugar, but it feels good. Then along comes this third thing that we can import called CSS Module Scripts. [Laughter] Can't help but laugh to say the name (a little bit).
Chris: But it's syntactically just like the other two, right?
Dave: The other imports, yeah.
Chris: It feels in line with it. Then here's the part that I think is relevant here is that once you have that as a variable, it doesn't just come in, like JSON comes in as an object.
Dave: Mm-hmm. Mm-hmm.
Chris: React comes in as whatever it was exported as: a function, an object, or whatever.
Dave: Mm-hmm. Mm-hmm.
Chris: CSS comes in as a special thing. It doesn't come in as an object. It comes in as a constructable style sheet, which is yet another Chrome thing, which I don't think anybody else has shipped yet. But it's this special type, I guess, in the browser that is specifically designed for CSS. You don't even need to care about that necessarily because your Web component will just take the thing as is.
Chris: But what it does give you is programmatic access to what's in that CSS file. If you want to loop over the rules or something, you can do that. You don't have to parse it. You don't have to bring your own CSS parser (like post CSS or something) into the party.
Chris: You kind of have some native abilities to loop over and change stuff in that CSS if you want to. That's a big deal. I otherwise think that you're really on your own for that, you know, having to RegEx through CSS to change stuff for something. Ack!
Chris: So, there's that. There's goods, I guess. There's certainly a pros and cons column, one of the pros being that programmatic access, one of the cons being that they stepped on the name of this open-source project. What are the other pros and cons?
Dave: Pros are you can also do a constructable style sheet from a string or a template literal or something like that. I can just A curly brace color red. I could type that out and then I could build my own. I could do a replace sync is the method on the style sheet, this constructable style sheet, and it would basically generate the same import--I guess it's called a constructable style sheet--the same constructable style sheet, and then I can do document.adoptedstylesheets and then push that style sheet into that, so it's an array of style sheets that are just living not in the DOM but in-- Sorry. I guess it would be in -- ooh, this is where it gets confusing. It's not in the HTML. It doesn't output on the page. It's just kind of this invisible style sheet that's being applied to the page.
Chris: Yeah. Just because you import a style sheet this way, it's not on the page.
Chris: That might be a little confusing to people who, for example, are used to Webpack. You can just type import and then quote and then the path to a CSS file. It's implied that it not only gets imported but injected. You know?
Dave: Mm-hmm. Yeah. Yeah.
Chris: Used on the page. This does not do that. Now you just have this constructable style sheet in your hands, just like if you were to do .createelement or something. You just have the element. It doesn't go on the page.
Dave: Yeah, which actually solves a problem with some of these CSS and JS frameworks because some of them would inject a stylesheet on the page or a style tag with all the crunched up rules (robo-classes and stuff), and it's actually if the style changes and a user logs in and all their links turn green instead of red, and they need to redo the style brick in there, it would actually just append a new style brick and reset all the classes because it's more expensive to remove that brick because then you have a style calc to remove. Then you have an inject. Then you have a style calc to inject.
Chris: So, it just slaps another one there and allows the CSS specificity to override it?
Dave: Yeah. Yeah, yeah.
Dave: This sort of solves a little bit of that problem of injecting. I'm sure you get still the reflows, but you're not modifying the DOM to inject a style brick on the page. So, it solves one little problem there.
I think one problem that it creates is a lot of these CSS Modules or other CSS and JS sort of things where, they were kind of camping or extending, let's say, the import syntax of import my style.css, and it would import a CSS file, Webpack bundling, and stuff.
Chris: Is that why we have to have the assertion gibberish at the end there because we just have to?
Dave: Maybe that's what makes the Webpack fail or something like that.
Chris: I could see that.
Dave: Or that's a hook Webpack can go in.
Chris: A hook, yeah.
Chris: I do like -- I did read at one point that you can't trust a file extension. Just because it ends in .css, that's not good enough. The server needs to return the correct content type.
Chris: So, having an assertion in the syntax forces that content type, and you kind of need that.
Dave: Yeah. You can name all your CSS files virus.exe if you wanted. [Laughter] You know?
Chris: Yeah. As long as your server knows what to do, yeah.
Dave: Right. It might actually try to -- well -- oh, now there's an existential question. Is CSS a virus?
Dave: Go talk amongst yourselves. I expect blog posts by the end of the week.
Chris: Yeah. I've read a few white papers that would suggest that it is.
Dave: [Laughter] So, I think it creates a problem with bundlers just because that namespace was kind of camped by these tools that were trying to use the familiar import syntax or even that same mechanism, and then auto-magic the type.
Dave: Me personally, I think they had to do what they had to do. But that sort of gets into extending prototype territory. You're inventing grammar. Kind of like steal grammar or you abuse grammar that exists in the language. You're going to create a problem later down the line, unfortunately, but that's just me. I understand why everyone did that. No-fault there for creating import CSS file, but you probably created a problem. It probably should have been some other thing, like some function or some global function called.
Dave: I don't know.
Chris: You almost don't like the syntax. You wish there was another, like require or something - not to make it worse.
Dave: No. Yeah, but I think you end up in a smoosh gate, right? MooTools used array.flatten.
Chris: Flatten, yeah.
Dave: And so, we couldn't use flatten. We had to use flat and flat map or something like that. You end up in this weird situation where if you say import CSS, now the W3C can't just say import CSS and we have to have import CSS assert type CSS. That's actually maybe good.
Dave: What if we have something other than CSS in the future? You've got to think of it that way, too. What if we have DSS, Dave Style Sheets, and Dave Style Sheets are more rad.
Chris: That's true, but can't you just -- isn't that what type handles? Then it would be of type Dave?
Dave: Well, that's what I think. I think the assert mechanism is maybe good long-run - maybe.
Chris: Oh, yeah. I definitely think so. Yeah, now I fully understand your argument. Yeah, I mean we don't have importing for SVG.
Chris: We don't have importing for HTML.
Dave: Well, so there is HTML Modules is going to be a thing.
Chris: Is it?
It comes in. You import the HTML, and then it's parsed, like DOM parsed. When you inject it into the DOM, it actually knows what it's supposed to do. Anyway, there are some cool things on the horizon there.
We have JS Modules or ES Modules, HTML Modules, and CSS Module Scripts. [Laughter] So, anyway--
Chris: That's a lot. Okay. There's a lot to chase down here. Here's some pushback on it. There's one thread from Evan You (Vue and Vite and has been on the show). He doesn't like it for a couple of reasons and specifically calls them out. One of them is that the name clash or the same syntax, that same import syntax. We just covered that.
Dave: Ajax event, yeah.
Chris: Pops in and then does it? Okay.
Dave: I can -- do we want a play-by-play? I feel like you could create a bundler that pre-fetches it and then pulls it in because there's the manual way I talked about.
Chris: Yeah. I know it doesn't work with bundlers today, but it does seem bundler-friendly, doesn't it?
Dave: I think you could do it. I don't work on bundlers, so I'm really naive in saying that. But I feel like it's a possibility. The bundle could have the style sheet in it and fetch it and do it and preprocess that. Yeah, I'm just going to say you can, but no one has done it.
Chris: Yeah, and which then solves--
Dave: It doesn't exist yet. It doesn't exist yet.
Chris: Yeah, I think that would solve the SSR issue, too. Then it becomes, do whatever you want with it once the bundler has it.
Dave: Yeah. SSR and Web components is an issue. This is a bigger issue, too. A lot of people's feelings are (about CSS Modules here, at least in these threads) I feel tainted by Web component experiences and Web component people experiences. That's fair because it's all the same people working on it. It's Googlers on the Polymer Lit team that are drafting this sort of stuff. That's fair, totally fair.
Dave: Yeah, yeah. Yeah, it's all -- we're all serfs in Google's kingdom here. [Laughter]
Chris: You have this tool then, this native tool for pulling in a style sheet and giving you programmatic access to it, which is kind of good. But then what does it solve? I think that's some of the criticism, too, is that what it solves is pretty minimal.
I would argue that it makes Web components a little better because I like the syntax of you just point to a style sheet. "Here. That's the style sheet for you. You got it." That's a better styling story than I've seen exist with Web components so far.
Chris: This is not exclusive to Web components, though. It's just a primitive. You do whatever you want with that style sheet once you got it.
But if there is some connection to the GitHub project CSS Modules in this, this does not solve scoping. It absolutely doesn't help scoping.
Chris: I think the pushback (at least I heard from Justin, who we've had on the show before, too) is just like, "This is the least we could possibly do - so far."
Chris: It doesn't mean that it will never change. It just means this was the building block from which other things could potentially come. Fine. Hard to argue against that.
Chris: But it's true, right? This doesn't help scoping at all, and that's where you had a little play-ski.
Dave: Well, yeah> So, I saw chatter. It was just like, "This doesn't solve everything." You know? I was just like, "Oh, you know, that's actually a good point."
The scoping, it does do scoping in Shadow DOM because Shadow DOM has its own little weird style encapsulation. We've talked about that probably ten episodes. I have a talk. [Laughter]
Chris: Yeah. Sure. Right. But it's not helping with the scoping. Shadow DOM is helping.
Dave: Shadow DOM is doing it, so when you import the style or you adopted style sheets, you can do shadow root dot adopted style sheets, and so you can - boom - chuck it into your components shadow root. Wow! Automatic encapsulation.
But that does not help Evan or Rich Harris from Svelte is also on there. They actually support Web components. God bless them. [Laughter] They're really trying to work with this.
They support Web components, but their frameworks don't use Shadow DOM. And so, they need a way to scope styles to components because their frameworks and stuff already supports scoping styles components. So, how do you get those robo-hashes and barfs into your elements and into the classes or into the component DOM? Then how do you get it into the CSS file itself?
I just was like, "Is this even possible?" And Westberg Johnson was kind of on the same page, but I just was like, "I'd never played with CSS Modules," so I just was like, "I'm going to fire up some CodePens," and so I fired up a CodePen, I constructed a style sheet, and just was like, "Oh, what's it give me?" You know?
It gives you this object, and it has CSSRules. Then you open CSSRules. Inside CSSRules, it's an array, so there's zero. [Laughter] And so, then you open up zero, and that is your first rule, A) color red. Then you open up the next rule and it's B) font-weight bold.
Dave: But that rule is actually parsed out, and so that rule is an object, and so the object there has the selectorText and then the cssText.
Chris: This part of it makes me feel like that's a nice interface. Good job.
Dave: Right, so it automatically splits it out. If you're just like, "Okay, I'm just looking at selectors. I want to find all my - whatever - bad selectors or something."
Dave: Whatever weird stuff you're into.
Dave: For me, I just was like, "Okay, can I get that selector, and then could I clone something and - whatever - mess with the selector?" What's cool is if you edit the selector, if I say rule.selectorText and I change my anchor to a bracket href, so I'm saying only anchors with href should be read, I can do that. Then the cssText, which is the actual full a color red. It turns into a href color red. It does that automatically.
Then you have to do -- and then I can adopt the style sheet with that change. But what I had to do, I just was like, "I'm just going to try," because I like Vue. CSS Modules -- we talked about it -- turns it into class equals zxymb, or whatever.
Dave: But the way Vue does it is it adds a data-zxymv to every element in that scope.
Chris: Which is the same CSS specificity. It's just an attributeSelector instead of a cccSelector.
Dave: Right, and it doesn't -- it's non-destructive, so all your classes that you had are already there, and so it doesn't try to intelligently roll-up or something like that. So, it just injects a data attribute.
And so, I just was like, "Can I do that?" I figured out how to do that, and you can just inject this data attribute into wherever. Then I made a random little hash for it.
Chris: Yeah. Okay, so that makes sense because you can programmatically alter the CSS and then give each rule a randomized hash, like Vue does.
Chris: But how do you do that in the HTML then? How do you match the selector that should be scoped in the HTML?
Dave: I put a scope on my little hashinator function. That's what made all the hashes and hashed out the CSS. I put a scope but, in that scope, I just said scope.querySelectorAll*@dataAttributedataHash, and so that's what gives every element in that scope the data hash, and then all my CSS gets the data hash, and so then they match up, they're married, and the scoping works.
Chris: Hash to all elements in scope. It bends my brain a little bit how that works.
Dave: It's just like adding a class to everything.
Chris: Yeah, so it adds this. You do set attribute and it adds the hash, but how does it only add the hash to the thing that it should add the hash to?
It's basically saying document.querySelectorMyID or some boundary.
Dave: ID some boundary .querySelector*, so go find this element and then query all the things in that element. Then apply the data hash.
Chris: Oh, I see. The scope hash. [Laughter] It's pretty clever, really.
Dave: It kind of works.
Dave: I don't even know what the edge cases would be. Have you found one? It seems like it would just do the job.
Dave: It seems like it works.
Chris: The problem is does the client-side not before client-side. You know?
Dave: Not before client-side, and this is where I don't work on server-side render or hydration machines, so I don't really know. But if the idea is -- I'm thinking of, like, Astro or something like that. If you're just running it before, isn't that it? You're just running it.
Chris: Yeah, you just run it before, and they do something -- they do scoped, too. I don't know if they just stole it from Vue or whatever or they wrote their own, but I kind of think they wrote their own because it's probably something a lot like this.
Dave: It probably looks 80% like this.
Dave: If I had to guess. But, hey, I'm totally -- this is naive. This doesn't work with server-side rendering or anything like that, but it's just a CodePen, but it sort of proved to me that idea that CSS Modules are actually -- module scripts, I should say -- are kind of cool because you can import a style sheet and then you can mess with it because that's actually a power you don't really have with CSS Modules or whatever.
Chris: No, that's what I was saying at the top of the show. That programmatic access to the style sheet is a big open door here that wasn't open before, unless you do something whacky like pull it as a string.
Dave: RegEx Town, USA.
Chris: Use a CSS parser on the client-side and those things are like two megabytes. That's not going to work.
Dave: Yeah, so it just seems like -- I don't know. This works client and, in theory, server-side, but it seems very lightweight. It's naïve. I will totally acknowledge that, but the idea that you can programmatically manipulate something kind of gets us that scoping in CSS that Vue has without using Vue or without using--
Dave: You can make any component this scope, if you wanted. So, it's interesting. You could even do this in Web components without attaching to the ShadowRoot.
Dave: You could maybe escape the whole DOM crap. [Laughter] I don't know. Maybe. I don't know.
Chris: Interesting. Yeah. It does change the math for me. You've looked into this more than I have, but we do follow -- we officially follow Web components on this podcast. If you rip out Shadow DOM from Web components and say, "That's not useful anymore," where are you left?
Dave: I love it.
Chris: That was my favorite part. You know? But only favorite because of the encapsulation, though. If you get that benefit of encapsulation without Shadow DOM, do I like them more now? Maybe.
Dave: Yeah. I don't know. For me, this is Dave Rupert Web component community group member. Total just plebeian in this group, this W3C group. Shadow DOM is cool for script and style encapsulation. It's just a baby webpage that's in your page.
But I've seen CSS people -- we're in a whole chatroom full of people. We have a ShopTalk Discord. You have a blog about CSS. There are enough footguns around Shadow DOM and style encapsulation that it seems too hard. It becomes maybe less utilized; Web components are less utilized because it's a little bit difficult and a little bit footgunny.
There's a proposal by Miriam Suzanne for @scope in CSS, and you wrote about it today on CSS-Tricks.
Chris: Ooh. Did that go out today? Good.
Dave: I think so. I read it, so it must have. [Laughter] I don't have secret URLs.
That is such an awesome proposal because you can say, "Start the scope here, but also end the scope here." I just think we're doing all this stuff, like CSS Shadow DOM and BEM and robo-classes to achieve scoping. Wouldn't it be cool if CSS just had scoping?
Chris: Yeah. That's it. You nailed -- you wrote three things just there, which is probably not even all of them. Think of just how many component-based CSS libraries there are all in an effort to just be like, "Oh, it's because CSS doesn't have scoping." If this lands, and I very much hope it will in some form, it's huge.
Dave: Yeah. Nah, because I mean your Web component would just ship with an @scope block. Your RAD component would just ship with an @scope block. [Laughter] Your HTML page of whatever--
Dave: --patched together CSS crap, that would just ship with an @scope block.
Dave: Then if Chris Coyier is smart and good, he can come along with his good @scope block and override my @scope block because -- guess what -- [laughter]
Chris: That's how it works.
Dave: --the last thing wins in CSS, and that's perfect. You know what I mean?
Chris: It is kind of perfect, right? You have this override mechanism that isn't about fighting specificity. It's just leverages regular CSS.
Dave: Fighting defaults.
Chris: I didn't even think about that. Yeah.
You know I know a big part of this that makes it extra good, this scope proposal, is a couple of nuance things that it solves that were hard otherwise. I think I had wrote (and we've talked about before) how there was an ancient -- this must have been eight years ago (or something) where you could just drop a style with the scoped attribute, like Vue does and whatever.
Chris: Because you don't have to scope styles in Vue, right? If you want to, you put the scope attribute on the style tag and then you get it, but you don't have to.
Chris: That's cool, but the way that that used to work was then, okay, the styles are scoped to that parent HTML element and everything down the chain. And that's not what they call -- that's just scope, but not donut scope. [Laughter]
Chris: Donut scope is at some point then that stops. You said lower boundary, they call it, so that those styles stop handling it. Vue does this, and CSS Modules does this because it only applies to the things that you very specifically apply to it.
It mostly handles that case because it's just being really specific about what it's handling. If you put another nested component inside, it probably won't mess with it. I think there's maybe some footguns there. You can still do a descendant tag selector and the descendant tag would then be harmed by the non-donuted scope. Oh, God. That was hard to say.
Chris: But still, it mostly achieves that scope.
Dave: Well, you're threading the needle, right?
Dave: You're shooting a rule through the donut.
Dave: And so, I don't know. We need a cool term for that. Whatever. Harpooning the donut? That's what we call it.
Chris: Shoot the moon.
Dave: You've got to harpoon the donut if you want to do that deep styling you're talking about.
Chris: Yeah, so that style scope didn't really handle that whereas this @scope rule does. Then that involved HTML, too. I'm always a little like, "Why is HTML involved here? This is a styling concern," so I like that this proposal is purely in CSS, which is great too. God, just the problems this things solves is just amazing.
Chris: We absolutely need this.
Dave: Well, I think it makes everyone's life better. It's just like, ah, you know what. I have a tab. I need some special tabs. But guess what. I don't want to make every link have a little file folder tab around it, so I don't know. I would like to just scope this to this.
But then if you worked on any large projects or maybe I should say any large project I've worked on, you come up with these sort of magical rules. Everyone is like, "Naming things are hard," but you're just like, "Okay, you've got to use this class here and then this class here and then this class here. And that's how you do it."
What if you didn't have to use the classes? You had a donut scope and you're like, "Okay. From here to here, I know there are going to be four buttons in there, so I'm just going to target those." It's cool.
Chris: It is cool. It's cool.
[Banjo music starts]
Chris: This episode of ShopTalk Show is brought to you in part by HubSpot and specifically HubSpot's CMS Hub. It's a developer-friendly CMS designed to help businesses grow.
On August 3rd, HubSpot launched a new tier called CMS Hub Starter for $25 a month. Hub Starter comes with all the features needed for fast, secure, reliable websites including SSL, a firewall in front of it, globally hosted CDN - all really good stuff.
A lot of CMSs can be rather opinionated about how you then build the site around the CMS. Well, not CMS Hub. You're still building locally even though this is like cloud hosting and the CMS is in the cloud and all that. You're still building locally with all these tools and the frameworks you prefer. So, however you like to build websites (as a front-end dev), you can do it.
From there, if you want to upgrade to CMS Hub Pro or enterprise for more advanced functionality and develop sophisticated user experience, stuff like personalization (which is pretty rad), and dynamic content based on CRM data (also very fancy), those kind of features are on higher plans. Learn more at hubspotdev.co/cmshub.
[Banjo music stops]
Chris: You know I hate to trot this out. Remember when container queries were all the talk? And they still are. I mean, we got the prototype. But I feel like the story is still a little unclear.
Dave: When and how. Yeah.
Chris: Is Safari going to do it? That would be a big deal.
Dave: Oh, way to wet-blanket the whole Internet there.
Chris: Well, this is all true of all this stuff we're talking about, too.
Dave: True. True. True.
Chris: CSS Module Scripts, is any other browser going to do that? Pfft. Who knows?
Dave: I have heard Firefox is, like, maybe there's a flag or something that does it, but it's not on. In fact, man, if you look this stuff up on the MDN - nothing.
Dave: Literally zero results for CSS Modules, and so I think there are some problems, looming problems.
Chris: Yeah. So, container queries are like that, too, and so is the scope stuff - all these features. This is a classic story. When can we use it?! [Scoffs] I have no idea.
Chris: But when we talked about container queries, what we trotted out -- or I did a lot because I thought this was true -- is that most CSS we write (or at least most media queries we write) would be in a container query. I don't know yet. We just don't know. A preliminary look suggests that maybe that was a little bit of an over-exaggeration, perhaps.
Dave: I don't know.
Dave: I think it's going to be pretty common. Hold on. I've got to type in an order. Steak burrito or fish tacos, Chris? Steak burrito? Fish tacos?
Chris: Steak burrito, for sure.
Dave: Yeah, that's what I was going to choose. Okay. Good.
Chris: If you're going to the place, you get the fish because then it's fresh.
Dave: Yeah, it's fresh. Okay. Yeah. Result. Okay.
Chris: But the green sauce. Get the green.
Dave: Oh, the verte, obviously. Obviously. Okay. [Laughter]
Back to CSS. I think container queries will help in most circumstances. I think it's still this, like, "I'm not going to type that because I don't know how that's going to turn out yet." You know? I think we still have some -- until we see some signals or some movement from other people, it's not going to happen. You know what I mean?
Chris: Okay. So, let's apply this to scoped CSS, @scope.
Chris: Is it too early to say or would you say that most CSS written will be scoped?
Dave: Whoa! I would say most. It's actually kind of cool because it's kind of a--
Chris: Safety mechanism.
Dave: Yeah, right, because CSS, the way it works, if it encounters the @whatever and doesn't understand it, it bails. It just says, "Dude, you wrote '@pizza.' I'm just going to ignore this whole thing. I'm not even going to worry about what's in here."
Dave: That's a cool part of CSS. If it encounters that scope -- it depends because the fallback is going to be just DOM if your styles don't render. My tabs just look like links or buttons.
Chris: Mm-hmm. Mm-hmm.
Dave: You know? That's not a bad fallback.
Chris: No, I think it's a responsible thing to do. Depending on what you're styling, just scope the styles to that thing. I think, in teams, that will just be the default way to do it because of the inherent scaredness people have to CSS. Just picking a new class out of a hat and applying it to a style sheet and styling with that, there's always some risk to that.
Chris: The risk is that you're going to trounce on something else, so invent a scope. Scope the styles to it. Then you're more protected in that way. Then the scope kind of goes around a chunk of CSS that feels a little bit like, "I know what this is for, and this will be easier to delete this whole chunk later on if I can just tell that that scope isn't in use anymore."
But I feel like that story is tied to the CSS Module Scripts story, too, because even though those aren't scoped yet (unless you use Dave's scoper machine), it still kind of implies that I'm importing that for a reason. I'm probably importing that for a really specific reason.
To me, that feels like a good authoring technique, and especially if they come in scoped. That's just the way front-ends should be built, if you ask me. That's the right primitive kind of thing.
I think even Evan pushed against that kind of concept, too, is that because then they're not combinable. Now you have one ajax request for every component out there.
Chris: I don't know if HTTP2 or 3 really has determined that's okay, especially with the ... stuff.
Dave: It doesn't make it the best, but it also isn't--
Chris: Mm-hmm. Mm-hmm.
Dave: In my opinion. Whatever. We can get Scott Jehl and Tim Kadlec on and they can tell us we're wrong. [Laughter]
Chris: I don't know. I'm sure the performance implications of CSS Module Scripts have not shaken out yet. I don't know.
Dave: You didn't have to run some ugly magic on it. It's there.
Dave: It's kind of what the browser needs, and so, gosh, I think it's cool, and my brain is spinning right now because if you had @scope (let's say @scope and I'm using CSS Modules), uh-oh, Chris. My browser doesn't support CSS Modules. Guess what...
Sorry. My browser doesn't support @scope. Guess what. Now that I know how to loop through all the CSS rules and tinker with them, I can write a preprocessor in the browser that changes @scope to my gnarly hash name.
Chris: Oh, my god! That's fancy. That's how the Polyfill for modules would work? Maybe.
Dave: I don't know. I'm just thinking. I'm braining. I'm mouth blogging right here, live on the show.
Chris: [Laughter] Right.
Dave: I'm just like, you could write a client-side CSS preprocessor that does all this stuff, that Polyfills CSS on the fly.
Chris: Yeah. I mean if somebody was going to write a PostCSS plugin for @scope, that's how they would do it.
Dave: Absolutely, but we can do it client-side now.
Chris: Client-side. Crazy.
Dave: Polyfilling CSS. Look at what we've just made there.
Dave: Polyfill IO. Just call me. Give me the bucks for this idea. Come on.
Chris: [Laughter] Think of all the things we've talked about that went from worst to better that we talked about. We talked about, oh, styling Web components just doesn't have a good styling story. Oh, that went from worst to better because now we can just import the style sheet and use it.
People write CSS that's too global. Well, fine, then import this and run CSS Modules over it if you need to or use the new scope stuff if it ever lands. That's gone from worst to better.
People write styles that are too coupled to global media queries. Well, container queries are coming. That went from worst to better.
It's just kind of cool to think of that. And unused CSS. Oh, there's too much-unused CSS in projects. Well, just import the styles that you need with these, like rearchitect.
I'm not saying it goes worst to better overnight. It's just that the tools are starting to land in place that's like, "Oh, these are better primitives for authoring front-ends."
Dave: Yeah. No, I don't think I'm all doom and gloom like everyone else is. [Laughter] But I do want to say I appreciate Evan and Rich's thoughts. Any time they have a problem with the Web platform, I like it, actually, because they're invested. They build tools for it. Then when the native thing shows up and it can't meet their needs, it's like, man, I understand why that's frustrating because you would love to offload this to the browser. But it doesn't do what you want to do, so I understand that.
I do think there needs to be a little bit like, "Hey, this thing is literally V 0.1," so it's not even out yet and people are already complaining. But I think you've just got to -- I think we need a little bit of, like, "Hey, I think this little building block is good, but it can get better. How do we get it to better?" I don't know unless you want to land a big thing in V1. People do, but those take decades on Web time, not a year or whatever.
Chris: Right. Uh... That was a lot.
Dave: Well, that was a fun, fun journey. Yeah. I was going to say your post was, "We had 90% unused CSS because everybody was afraid to touch the old stuff." That was the Kent Dodds post.
Chris: Yeah, I didn't really dig into @scope that much. I just linked to the fact that it feels related, to me, that if you can scope styles, I think it goes a long way in combatting long-term unused CSS.
Dave: Yeah, because if you just see @scope my tabs, guess what, I know exactly what that whole block does.
Chris: Yeah. Yeah, it's amazing. It's great.
Dave: I ain't afraid of it. I'm just going to mess it up and, no, I'm not messing up anything.
Chris: It's true, though, that you still have to name .tabs, though, and that could be a conflict later whereas part of the beauty that you get with the old school CSS Modules is you can call it .tabs and it doesn't matter because it's going to get obfuscated away to something that will not conflict with something else, and I do kind of like that. I use repetitive class names all the time in my CSS Modules, like the classic project. I always do .root (.root is just the parent thing).
Chris: You never have to think about it. You never have to think of a name for the parent of the thing. It's just the root of the thing.
Dave: Mm-hmm. Root.
Chris: You do it, and I can use root over and over and over and over. If there's a title and a card, I just call it .title because I don't want to think too hard about it, so I just call it .title. I'm sure Tailwind people would be like, "I never have to name anything ever."
Dave: [Laughter] It's not .title. It's .blue-red85.
Dave: Yeah, we love you, Tailwind people.
Chris: I read a cool thing about Tailwind that it had the appropriate amount of nuance, which I always appreciate that kind of thing. I think it was on the Viget blog. That it doesn't just mean one thing, and I think people forget that sometimes.
When you say, "Oh, we use Tailwind," what does that actually mean? It can mean super different things. You can just CDN load a style sheet of a default build of Tailwind with every class it has to offer. That's one way to do it.
You can wire up Tailwind to remove all the unused classes, right? That purge CSS, that's kind of like the correct way to use Tailwind, I'd say, or one of them.
Dave: With purge CSS, yeah.
Chris: Another way is to really lean into their @apply rule stuff is that you are still writing regular CSS but you @apply all the classes that it has available to it. You're still just using Tailwind styling, but you're just using your own classes.
Dave: It's like Sass mix-ins.
Dave: Like an -ish vibe.
Chris: Right. Right.
Chris: Then there's a way. Or you could really lean into Tailwind's configuration, and you still use all the classes but what those classes do is entirely customized by you. They have different colors, different fonts, different spacing, different everything.
Or you can use Tailwind UI, which are prebuilt components. That's like five ways.
Dave: I would love to know how often Tailwind, the configure gets changed or what people changed. I know colors is popular. "I'm getting my brand colors in."
But I'd love to know are y'all really messing with padding sizes? [Laughter] I don't know.
Dave: It seems like then all your Tailwind UI stuff wouldn't work anymore - if you were using both of those.
Dave: Yeah. Yeah. I don't know.
Chris: Yeah, okay, so--
Dave: It's cool.
Chris: Yeah, that was--
Dave: There's a lot of avenues in Tailwind. That's what you're saying.
Chris: That article you mentioned was a JS Party episode with Kent C. Dodds on it. This wasn't really all about that. It was mostly just about React, really. It was a pretty good podcast, I thought.
Chris: It happened to mention style components was just another (in React specifically), which I almost prefer calling it CSS in React because that's what people mean when they think of CSS and JS because every other popular framework, like all of them as far as I know, have their own blessed built-in styling solution.
Chris: When you're kicking around looking at which CSS and JS library, that means you're on a React project. [Laughter]
Dave: That's true.
Chris: You get to pick from the different ones, which is fine, but you know. No blessed solution in React just yet. I doubt there ever will be.
Chris: I guess you have to worry about leaking in, but not so much leaking out.
Dave: You don't get lost in a CSS file that's 5,000 lines long.
Chris: And when I want to delete that component because I don't need it anymore, it all goes away together. That's super compelling. And if you can bring all that compellingness to the native platform, which is not there yet -- even with this new stuff we're talking about (but that would be cool, to me) -- there's a zero build process way with all the benefits of what we have with complicated tools.
Dave: It's like Sass partials, sort of. You want to use that CSS for that thing. Yeah, yeah. Ooh, what if CSS got partials? Ooh! That might change. Ooh! Anyway--
Dave: Because it technically has partials. It's just import is terrible. [Laughter]
Chris: Yeah, I do look forward to that. I didn't know that we were talking about importable HTML chunks, but that seems really "of course," right?
Dave: Yeah, well, even if it worked with JSX. I'm sure you could import JSX or import my template.jsx or something maybe one day, or maybe there's a Polyfill for that or something like that. I think it makes your React component leaner because you're just like, "Okay, my template is sort of fixed in how it's going to render, so I just want to do the logic bits." You kind of get away from the single file React component, which people like, which is also good. But you maybe get some organization there. I don't know. It's kind of something to think about there. Your JSX files might just live in -- your render templates might live in a different file. So, different.
Dave: I don't know. In React land, that's maybe unpractical because there are so many if statements and crap in there that render.
Chris: Yeah. Yeah.
Dave: But who knows.
Chris: Well, we should probably quit, but I wrote down one more thing, so I'm going to do it. I'm going to mouth blog this. Are you ready?
Chris: Let's say you do the root thing with your custom property. You do root, and then you do margin, dash margin ten pixels.
Chris: Then you do a button later. Then you say margin:varmargin, so you're using the custom property there, and that works super-duper fine, right?
Chris: But okay, let's say you go too deep. You know how custom properties can reference other custom properties?
Chris: Let's say you wanted two -- you're going to smash together two different custom properties for margin. You're going to go your margin inline and your margin block, so you're going to set those separately. One is five pixels. One is ten pixels. Then you make a third CSS variable that's just called margin that references those other two: varmargin and margin block.
Dave: It's margin, inline margin block. Yep. Yep.
Chris: Got it? You're following me? Okay.
Dave: I'm here. I'm here. I'm here.
Chris: Now you still have dash margin to use. Later, you do button, and you call margin, and it works great, right?
Dave: Mm-hmm. Mm-hmm.
Chris: Then you do button.variant.
Chris: Button.variant changes just margin inline, so it just says dash margin inline, and then you change it to ten pixels. Does that A) work or B) not work?
Chris: The answer is B) it does not work. [Laughter]
Dave: So, you can't just say button.variant--margin:inline or--?
Chris: You can't change the dependency variables.
Dave: Oh... Oh, yeah. Okay. Interesting. Oh, I would have thought that worked, but that makes sense.
Chris: Right! Doesn't it seem like it would work? It seems like it would work.
Dave: Okay. It would seem. Yeah, yeah.
Chris: Because you're changing this dependency variable for this variable that you are using that sets it. But no, it's because you didn't declare that margin, that combo one on button that's just sitting up on root.
Chris: One solution to it is you know where it's just like colon root, like the place you're declaring it, just comma separate it and put comma button. You declare them on the button also. [Laughter]
Dave: Oh, okay. Oh, wow.
Chris: And then because it's declared there, then that override works. It's just really mind-bending--
Chris: --when it will resolve and when it won't.
Dave: Yeah. Yeah. I feel like I've messed myself up with the, like, "I'm going to write a complex variable." Every time I do that, I hate it later. You know?
Dave: Even whatever that fancy, make my heading scales whatever--
Chris: Sure. Fluid type thing.
Dave: Fluid type formula. I love it because it's clever. I want to give everyone all the credit for that CSS trick, but I hate it when I see it in my CSS now because I'm just like, "What?! What is going on?" I don't know.
I have strong opinions about when people do PX to REM and stuff like that too. I'm just not a fan. Just type out what you mean. Just type it out. Declare. Just have whatever, margin inline or margin block, and then every time you want to use them, you specify what you want.
Variables in variables ends up creating an unattainable machine.
Chris: Yeah. Yeah.
Dave: I don't know.
Chris: It just sucks. I could see that if it was one level deeper or something, but just one combo variable didn't seem that bad, especially because imagine this one. Brand color one, brand color two, and then linear gradient. You're setting a linear gradient that goes from color one to color two, and all you want to do in a variant is change color one or color two. And you just can't.
It's not that you can't. There are ways around this. I wrote it out, and this is all credit to Stephen Shaw too because he brought this up in the thing the other day.
Dave: He's the sleuth. Yeah.
Chris: And he made a bunch of demos and crap. I was like, "Wow, is that ever weird." I think Adam Argyle chimed in and said this is the number one gotcha that people face with custom properties. Miriam had a good way of explaining it, so I don't want to sound like the captain of this.
That's what I do. I wrap my mind around somebody else's problem and blog it.
Dave: There you go. That's the formula. [Laughter] I do that, or I get nerd sniped by, "Why do people not like CSS Modules?" I guess I'll spend a day.
Dave: Whoops. So--
Dave: It's fine.
Chris: Yeah. All right, man. Well, thanks.
Dave: Wrap it on up. Yeah, we are heading into the end of summer here, so expect things (for me at least) to get a little more focused, so I'm excited about that. [Laughter]
Dave: Thank you, dear listener, for downloading this in your podcatcher of choice. Be sure to star, heart, favorite it up. That's how people find out about the show. Follow us on Twitter, @ShopTalkShow, for tons of tweets a month.
Buy our mug. Oh, my god. Go to CSS-Tricks store and buy our mug.
And join us over on the patreon.com/shoptalk D-d-d-d-discord, please. That would be awesome to see you there.
Chris, do you got anything else you'd like to say?
Chris: Oh, ShopTalkShow.com.