Scope in CSS

Introduction of Chris Coyier

The host introduces Chris Coyier, highlighting his contributions to the web development community through CSS Tricks, CodePen, and the ShopTalk podcast. Chris is praised for his kindness and enthusiasm, and the host humorously suggests forming a CSS rock band. Chris is set to speak about the concept of scope in CSS, a topic eagerly anticipated by the audience.

Understanding Scope in CSS

Chris Coyier begins his talk by explaining the concept of scope in CSS, emphasizing that it is not a new feature but an inherent part of CSS. He illustrates how every CSS selector is scoped to certain elements, using examples like styling a carousel or applying styles to specific HTML elements. Chris clarifies that scope is fundamental to CSS and not a fancy new technology.

Challenges and Solutions in CSS Scoping

Chris discusses the challenges of CSS scoping, particularly in team environments, where overlapping styles can cause issues. He explains how naming conflicts can arise when multiple developers work on the same project, leading to unintended style overrides. Chris highlights the importance of scoping tools to prevent such conflicts and maintain clean, conflict-free styles.

CSS Modules and Scoping Tools

Chris introduces CSS modules as a tool for scoping styles, describing how it appends unique identifiers to class names to prevent conflicts. He appreciates the simplicity and effectiveness of CSS modules, noting that it allows developers to write regular CSS while ensuring styles are scoped to specific components. Chris also touches on the concept of co-locating styles with components for better organization and maintenance.

Scoping in JavaScript Frameworks

Chris explores how JavaScript frameworks like React handle CSS scoping, often through CSS-in-JS solutions. He critiques the lack of a built-in styling solution in React, which led to a proliferation of third-party tools. Chris contrasts this with frameworks like Vue and Svelte, which offer more straightforward scoping solutions, and discusses the benefits of these approaches for managing styles in large projects.

Web Platform Scoping: Iframes and Shadow DOM

Chris delves into the web platform's native scoping mechanisms, such as iframes and the Shadow DOM. He explains how these technologies provide strict scoping boundaries, preventing styles from leaking in or out. Chris highlights the use of Shadow DOM in web components, which allows for encapsulated styles, making it ideal for design systems and reusable components.

Utility Classes and Tailwind CSS

Chris briefly discusses utility-first CSS frameworks like Tailwind, which inherently provide scoping through class-based styling. He acknowledges the performance benefits and scoping advantages of using utility classes but notes that it's not his preferred method. Chris appreciates the efficiency of Tailwind's approach to minimizing unused CSS.

Introduction to the @scope Rule

Chris introduces the new @scope rule in CSS, explaining its basic syntax and functionality. He humorously critiques its initial simplicity but acknowledges its potential for more advanced use cases. Chris outlines three niche features of @scope, including donut scoping, which allows for more precise control over where styles apply within nested elements.

Donut Scoping Explained

Chris explains the concept of donut scoping, using the example of selectively applying link styles within content areas while excluding navigation links. He describes it as a more elegant way to manage style application across different sections of a webpage, highlighting its utility in maintaining consistent design patterns.

Proximity Scoping and Its Benefits

Chris discusses proximity scoping, a feature of the @scope rule that prioritizes styles based on their closeness in the DOM hierarchy. He illustrates how this can resolve issues with nested themes, where source order previously dictated style precedence. Proximity scoping offers a more intuitive way to manage style variations without relying on strict source order.

DOM Blasters: A New Approach to Scoping

Chris introduces the concept of DOM Blasters, where styles are scoped to their parent elements without explicit selectors. He demonstrates how this approach allows for component-level scoping directly in the DOM, offering a zero-tooling method for encapsulating styles. Chris suggests this could be a viable alternative to traditional scoping methods, especially for CMS-based projects.

Conclusion and Future of CSS Scoping

Chris concludes by summarizing the evolution of CSS scoping, from basic class-based scoping to advanced techniques like @scope and DOM Blasters. He encourages developers to explore these new tools and consider their implications for future web development practices. Chris expresses optimism about the potential of style block scoping to simplify and enhance CSS management.

Q&A with Chris Coyier

In the Q&A session, Chris answers questions about his preferred scoping methods, the potential of build-step-less components, and his thoughts on Tailwind CSS and BEM. He emphasizes the importance of choosing the right scoping strategy based on project needs and team dynamics. Chris also discusses the role of blogging and community feedback in shaping CSS features.

Speaking of handsome man, I'm about to introduce Chris.

Chris Coir. He's our very first speaker of the day.

You might know him from the one who started CSS Tricks back in the day. We've all visited CSS Tricks. We still are today. And you also might know him as the co founder of codepen. We've seen codepen being featured here in the decks many, many times. So I think that will definitely put a smile on your face, Chris. But Chris is also known for just being a nice person in general. Every time I see Chris, he's. He's smiling, he's like, just kind.

And you have that little spark in your eyes as well. So I like that, Chris. It gets you places. Be kind to people who'd have thunk, wow. Also, if you're into podcasts, you might recognize the voice because he also does the shop talk podcast together with Dave, Rupert, and then that's it. Sorry, that's all I do. It's those three things. Dad, father, you know, want to switch places.

And also yesterday we saw Brad run around with the ukulele.

Chris also plays an instrument. He plays the banjo mostly. And then Adam also likes to play.

Jeremy also likes to play. So maybe we should, like, start a CSS rock band or whatever. If we ever started one, Chris would be in it, definitely. But Chris is not here today to play the banjo. You are here to talk about SCOPE in css, which I'm very much looking forward to. So please give a warm hand for Chris.

Thank you very much. Brammer.

Sounds okay. Oh, my God. I knew a Dutch sound guy once.

I knew a Czech one, too.

I read that on Reddit one time. Scope is mouthwash where I come from, but whatever. So that's the joke. You're all intelligent people. I will dispense with the preamble. We're just going to talk about SCOPE a whole bunch, generally. Conceptually. If I was going to teach you about css, if I was going to explain to someone badly how, like, what the authoring experience of CSS is like, you might say it's. I don't know, you write some scope and then you write some styles.

That's what css. It's filthy with Scope. It's just all up in there already. Just in case you were wondering if we're gonna talk about, like, the new. Like, is it some new feature of css? Is it some fancy new technology?

No, it's not. It's just conceptually part of CSS to begin with. For example, every single selector is Scope. So, like, if you think of CSS as like, I got some sweet styles, I'm going to make this sick ass carousel. Good. That sounds fun.

You should do that. Where and when do you want to apply them to?

Which scope do you want to apply it? I was on the famous website CSSDay NL. Pretty good one.

There's some CSS that appears on that website. And hey, I know that deep pink is like the meme one that we're supposed to be doing, but I think named font sizes go even harder.

Do font size X large that rules.

They just ship that. That's the best. It makes me never want to use a real length again for font size. Anyway.

Cool. That scope right there, it's actually pretty hardcore scope. Those styles only apply to just the rare HTML element that happen happens to have class is date on it. That's pretty specific already. That's scope. So end of talk, you know, like, that's scope. That's scope. It goes further though. There's more scope. Like I said, CSS is lousy with scope.

They wrote some more styles that only apply it to screens.

Pretty specific ppk, I'd say, but okay, if that's how you want to roll. And then only on pretty big screens as well.

Scope, scope, scope, scope, scope, scope, scope. Everywhere out here in the real world, perhaps. Not that that wasn't a real website. Totally it was.

But here's another real website codepen. This is how it might go down. Let's say I wanted to style that little bad boy.

That's a day's work.

But anyway, I might be like, what is that thing?

I don't know. It's a card, I guess. I'm so uncreative. I just call everything a card. I am a card. Heavy dude. Cards are actually just little websites. Did you know that Dave said that once on the podcast?

I'll never forget it.

So, yeah, background gray. It's got some padding and border radius or whatever. Cool. I have now scoped that made a little selector to do that later on, I have to style that little component and I go in my little depraved brain and be like, what is that thing now? Is that a card?

Kind of looks like a card, but I don't know. The padding's a little different, the background's a little different. Doesn't have border radius. So I just scope it in my mind. I just feel like I'm not even going to use card. I'm going to use challenge card. And I have accomplished scoping just I've nailed it.

There's no conflict between those styles at all. I've just crushed. Crushed scoping. So good. In fact, the vast majority of CSS I've written by far and in my entire career was that that's all the scoping I ever did. If that's all the scoping you ever do, fine, Sweet. That's all you really need to know about css, I think. But it goes. We can go further than that.

In fact, we might as well, because I'm already up here. So let's go.

So the only reason to start piling on more tooling and syntax and crap to just that basic scoping already, which is learn CSS selectors, is when there's actually some kind of problem. A problem could be that I was tired next Thursday or whatever, and there was another little piece of UI I had to style and I was just writing all global CSS and I was like, what should I call this? I think I'll call it a card. And then this actually affects. The better you get at css, the worse of a problem. This is if you just rip out those styles card and you're just. You got it. There's a linear gradient and you nail it and it looks how you want it to look on the screen and you ship it. But you realize the you broke something else because your styles were perfect, but card was already being used somewhere else and you just weren't looking at that page at that moment. That's actually a problem, but that's you fighting you.

It's a little bit more likely on a team where you're not just yourself.

If it's like you're on a team of like two or more people, the likelihood of that happening is higher, probably like the bigger of the website. I know that's a little arbitrary, but it's highly possible that I style Card.

Rachel comes along on our team and rips out some card styles too, and we just don't notice and we cause problems that, like, does actually happen. I'd hate to say, you know, like, that's why people reach for scoping tools, is that we want to use simple classes like that, not strain our brain with coming up with totally unique names all the time and stuff. And there's also this. It's not quite as dumb as the Peter Griffin one or whatever, but it's similar that the bar stool problem in CSS is that, you know, you can do stuff in css. I mean, to explain the joke, writing CSS can have unintended consequences. Yeah, fine. And it kind, it kind of can. So I guess that's why we're talking about Scope also if you have like depraved ass CSS brain like I do, you look at that and you're like should have used text wrap pretty. You can't have orphans like that in public. That's crazy. And you can't do that. So we're going to look at some tools to take scoping a little bit further. Like a straight up warning. John was all like be like water man. And I'm going to be like, we should scope this shit out of everything. Just let's just push capitalism into whatever. I don't know. You got to pick.

So pick the cool one. I'm not going to tell you which one it is.

There's. So we are going to talk about at Scope and css. Probably most of you assumed that's what I was going to talk about. We'll get there, I promise. We're going to talk about it.

Gotta wait for Miriam to get here.

So there's some tools and the tools to apply scoping. Some tools are like just about scoping. That's the thing that they exist for in the world is to apply scoping to this situation. That's cool.

Pretty specialized tools. Some of them are. They involve the processing of HTML and CSS and JavaScript to pull it off. One of those technologies is CSS modules, which I really think is cool.

I legit think it's like a good project. I happened to click on the little contributors thing on it on GitHub the other day. It's having its 10th birthday, so it's Stephen Hayes birthday and also maybe CSS module's birthday too. Are you 10 already different?

Not your golden birthday, whatever it is. I do also like that they just, they just wrote it in 2015 and then just that's it.

Then they did nothing else. I looked at those 2024 commits like five minutes ago just to see what they were and it was some typos in the readme. I love a technology that's like it's fine, we just wrote it and now it's done.

So that's kind of cool. The way that CSS modules works is that you like, you write and I probably appeals to some people in this room because you just write regular css. It's just a CSS file and you write it and that's fine.

Like a Leah. And then it gets. Ultimately what shows up in the DOM is like that class that you wrote with some computer Gibberish at the end of it, which is the scoping part, because it's so whatever, algorithmically rare or whatever, that it's not going to conflict with something else.

My, like a little baby spicy opinion is that when you f with a language, when you change, when you add any syntax at all to a language, that you shouldn't use the native thing anymore, you shouldn't call it css. CSS modules has colon global and it has this other weird composes keyword. Well, you did it. You messed with the language, so don't call it CSS anymore.

I don't know. I don't have any more to say about that. But somebody in here is going to invent a language someday that's like a slight variation of css.

Just pick a new file extension.

It's getting processed anyway.

Spicy the way CSS modules works.

And it's only really relevant in JavaScript produced HTML anyway, so it's not on your just HTML and CSS website. CSS modules isn't really relevant. But what happens is that you import CSS like that first line, which is pretty much invalid JavaScript. You can't just import styles like that. There is a thing called CSS module scripts that kind of can, and it comes in as an adopted style sheet and stuff, but there's more syntax to it than that. If that is actually a CSS file, that would just be invalid JavaScript. But if you're in a tool that knows what CSS modules is, it's like, oh, I got it. I know what you're trying to do. What you're actually importing is just like an object that maps the class that you wrote to to the gibberish class. So the HTML class that gets produced has the gibberish on it. The HTML that gets produced has the gibberish on it. And now you have scoping. You have like good scoping that prevents all barstool kicking.

Scope. Scope has happened with CSS modules. Pretty cool.

I also like it because, like I said, it's just that the GitHub thing, it's just a readme, it's just an idea.

CSS modules, there's nothing in your package JSON, there's no cli, there's nothing. CSS modules is just like it should just work like that. And all these other tools are like, oh, that's cool, I'll just build it in, or whatever. I think that's just weird in technology today is to have a.

Have a thing that people think of in that way with no way to install it. What it generally looks like in the real world is like that's just a react component and you import the styles which does the CSS modules magic. You apply the class name to it in some way and then kind of crucially where you're importing it from is probably like right next to the component itself. So it achieves that like co location of styles. That's actually pretty nice. It means like the CSS in this file is for this component and they just hang out together, they're buds and they get thrown out together.

So that's what we'll talk about in a minute. I knew this guy I worked with for a little while who always used the word orthogonal and I was like, man, that is a fancy ass word. I looked it up once, it basically means related in the way he was using it. So let's all use the fancy one. There's an orthogonal problem that's called unused CSS and you can even open devtools and it'll be like, okay, you got like a 98, it's pretty good. But I see there's this little piece of CSS that wasn't used by anything and you're like oh, isn't it?

Well, you're looking at the homepage and it's used on the shopping cart page and, and now that CSS is cached so it's actually more efficient. So you know, whatever performance tool. It's a little hard to know actually if CSS is unused. So I don't really trust tools like that.

But anyway, this idea of like kind of co locating styles and scoping your styles is nice with unused css. I also talked to a team one time that all they shipped was global css for like 15 years, right? And you get this, you know your CSS only grows on your site and it was concerning to them. Cause it's actually a little hard to know what CSS you can throw away. It's not particularly hard to know when all your styles are co located. You either don't load that component, don't use it, throw it away, whatever. You have the git lens thing installed in your VS code. When you're on a line in VS code it tells you who touched it last. Sometimes I'm looking at some of our old CSS and I'm like woo, that was me a long ass time ago. I'm not touching that. That's going to stay right there for sure. So yeah, if you don't use in the co located styles world, it's like they're either not loaded or when you throw the component away, it goes away too unused CSS becomes less of a problem. That's kind of nice too.

This whole idea of CSS and JS probably wasn't invented just for scoping. It has all kinds of bad ideas behind it. But scoping is one of the benefits of it too, is that when you write CSS in that way, at least you're applying styles right to it. There's no barstool stuff going on, it's not leaking out and touching other crap. Mostly I mean CSS and React, because some of the other major framework libraries and stuff just shipped with a styling solution. And here's just another little baby little spicy opinion is that the fact that React originally shipped with no opinion whatsoever about how you style things other than like, I guess you can use css, otherwise you're on your own. Which gave birth to just this crazy ecosystem of CSS and React tools that as an industry, I don't know that we benefited a whole hell of a lot from. I don't think there was like so many good ideas baked into that that we're better off compared to like, Vue, who's just like, oh, you know how you do it? You just put a style tag, you want it scoped? Put scoped as an attribute and everybody's like, fine, that sounds great and I'll do that.

That's better. That's just better.

They do it in an interesting way where your classes or any selector, I guess, just your HTML elements that match, just get a data attribute on it and then it styles based on the data attribute, which is nice because it doesn't have to be a class and it maintains the class that you wrote on it, which is kind of nice because then it's like there's a user style sheet hook for it. That's not crazy. And subject to build process breakage and stuff.

Svelte does it too. How do they do it again, they do it with.

They just add a class to everything. They just add another class. So it just bumps up the specificity of your entire style sheet a little bit, which frankly weirds me out. But I've never heard anybody bitch about it, so maybe it's fine.

Seems like it would be weird for third party styles, but whatever.

So as the web platform though, like stuff that CSS gives you naturally, or like the platform as a whole gives you like, it doesn't really help with that. The reason we had to tool that away is because there is no Selector scoping in css.

It's not even talked about as far as I know. So scoping on the web goes from, like, super chill, like, I don't know, just write another class or whatever, to, like, very extreme scoping. So let's look at. Let's look at some hardcore scoping. There's iframes, iframe. Or as some people write it, the iframe or whatever. The iframe, which is my favorite way of writing it for sure, because I picture Steve Jobs like it's a rectangle.

That's awesome. They're hardcore. Because you put an iframe, you have some styles on that page. Those styles aren't going in that iframe, and no styles inside that iframe are coming out. That's about as hardcore scoping as you can get on the web. Pretty badass object.

Same deal, hardcore scoping. The web wanted to give us that for some reason. So now we have it. Now I don't have anything else to say about that. They're both the worst. And every business I've worked for profits mostly from iframe. So that's how that goes.

Shadow Dom. Do you see that movie with Keanu Reeves? That was pretty good for a sonic movie. I have a little daughter. Hey, rubes, if you watch this, Shadow Dom was how I first heard of it. Although it is kind of funny. Ooh, interlude. I hope I don't go over time because of this, but the first time you ever heard of Shadow Dom, I was talking with Jeremy about this earlier. The browser people were like, hey, you know, like, form elements, they're actually just built from css, HTML and CSS internally. Did you know that? Like, isn't that cool?

We just use the same stuff that you have access to, only also we put a huge brick wall in front of it. And you have to keep your grubby fingers out of it because we don't trust you in any way at all.

And you're like, why'd you even tell me that then? That's not even good to know. And then later they ship it in web components and you're like, you know that you can now do that to your users.

You love that. But I first heard of it from SVGs when I was on this crusade against icon fonts, which are bad. And I was like, there's an alternative way. You just put all your icons as symbols and then use the use element in svg and it goes and clones it and moves it down, but it puts it inside a Shadow Dom which had all these styling limitations, but it was still like a half decent idea. But now web components are like for the first time. And web components does not just like =dom.

It's a thing that you opt into if you want it to happen, but you can now, which is pool, so you don't have to use it, but you can. Here's a really simple web component where the first thing we're going to do is define it. So angle brackets, fancy button is like a thing now. And when HTML sees it, this JavaScript calls, the connected callback calls. It will run that JavaScript and what it's going to do is then that's the optional line that I'm saying. I want it to be shadow dom, which is going to wipe out everything that would normally render in there and says, all right, you're on your own.

Whatever you render in there from now on in this shadow root is the stuff that's going to apply. So I'm going to make a style element and jack it in there. But look at that, look at that water ass selector I'm using. Button. Little dangerous, isn't it?

Can't just slag a button selector everywhere. But it's cool because it's in a shadow dom so it's not going to leak out anywhere else probably. So there's fancy button and I have a button inside of it. That selector is now going to apply the one that I've put in the shadow DOM next to it. So it's going to render like, oh, super, super cool button, yay. But the styles aren't going to like leak out, as they say. So if I were to have a button that's in the same DOM as the custom element, it's just going to render like our old Brutalist buddy, the button, you know.

Sweet. That's cool. That's Scope. That was Scope given to us by the web platform. So cool. Thanks. You can imagine wanting to ship stuff like that as like a. I don't know, I don't even know actually.

But the baseline widget from Google, where it has the little browser icons and crap that tells you about a browser feature that has a web computer component and you would expect it to look like that. So it's got some CSS coming from somewhere.

I assume they ship it in a little shadow DOM so that your outside styles don't mess with it. Likewise their internal styles don't mess with anything outside of it, which would be an even bigger deal. So doesn't that sound nice for a design system? You have a design system and you have consumers of it who use your little stupid tabs component or whatever. It better look like tabs. So it's nice that they have co located styles that come along with the web component. Doesn't that sound nice for a design system theoretically? Maybe we should experiment with building design systems like that. Well, whatever. We don't have to experiment. Every major design system by every freaking company, big company, uses web components and shadow dom. So like they're already doing it. It's already. Here we go.

There's another kind of scoping that I don't want to say too much about because I don't care all that much. But when you talk about utility classes these days, it pretty just much just means tailwind. There used to be other stuff, but I think tailwind is pretty much well taken over on that. The expectation is that you style stuff with class names on the HTML thing. And some people say it makes them work faster. They can see in tailwind or whatever. It's not for me, but I get that.

What's kind of a byproduct of it is there's no styles there leaking out to anything else. That is scoping.

And scoping is kind of nice, especially on teams.

So it's just one of the benefits that you get from there there.

Okay, we have to actually talk about Scope though, because we only have so much time here. Let's rock some scope. Just credit to all the good people that write blogs and like, I don't.

It's not like I was involved with Scope. I don't know anything about Scope.

I know about Scope because I read a bunch of shit about Scope and then I made it into slides and I read it from the smart people who work on the websites. And if you have an RSS feed, I probably read it.

And if you wrote about Scope, it's probably in this talk somewhere. So just like it's just a general thank you to the people that publish on the web.

Thank you good people. If you're worried about the browser support kind of stuff, it's already flagged in Firefox, which is the only thing that doesn't have it yet. And it's an interop this year. So Scope is pretty much it's ready to go. I mean whatever. It's that use your own brain.

So here I'm gonna introduce you to the syntax of Scope and how stupid it is with a silly usage scenario that doesn't do anything useful. So you have that HTML and then you write some CSS that styles that HTML cool on the right. And then Scope is like, ooh, I can do that.

Also, look, instead of writing a simple card selector, you could write scope card instead.

And then the same thing applies. It's the same.

There's no difference between those things pretty much.

So you're like, thanks Scope, that's good job. Thanks for coming to the party. Did you bring any weed?

You could also browser test for it if AT rule was supported anywhere. I just thought that was funny earlier when you browser test it for something that's the supports test is newer than the thing that you're testing. So that doesn't work anywhere. It might someday, but you're actually like hurting yourself if you use that because then nothing's whatever. Don't do that. It is testable in JavaScript though.

Just put it in there. Because practical information is nice to have sometimes. So there's the basic usage of Scope where you do scope and then you do a parenthesis and you say what class you're scoping or what selector generally you're scoping. Not particularly useful in my opinion, although we will look at some use cases for it. But there are three baby little niche things that Scope does that I don't hate.

Anyway, sorry, I didn't mean to crap on it too much, but it was a little I really had to dig for to find some stuff in there. One of the things they can do is Donut Scope, which has a wonderful story because in 2011 Nicole Sullivan elegantly described why she would like to have Donut Scope in it. And I think that's cool. It's like a great web standard story. If you want something, you write a blog post and you wait like 15 years and then you get it. Which I feel like is pretty common kind of.

Anyway, she explained it very well in that blog post and good thing she did because we have it now. Here's just one use case that I think is mediocrely useful about Donut Scope. So here's an article on wired.com they appropriately use underlined links in the body copy of of their website. Cool. Links are underlined. It's in the user agent style sheet.

Keep them. Looks good for the most part. But there's a bunch of links on here that aren't underlined. Debatable, but maybe okay because the design affordance for navigation.

I guess I personally feel like that's okay that those aren't underlined, but whatever, let's move on from that and assume that that's the design goal that we're coming out. Some of our links in content are underlined.

Links elsewhere, in some cases are the way I've generally rolled. Like that is just like, leave the default underlining in place and then select the places where you want to remove the underline and do so. So it's like the like six shooter. Like, no underlines for you, no underlines for you. You know, Cool. Or you could go in reverse and you can wipe them all out of everywhere and then say, on the content, though, bring them back.

The opposite approach. That's fun. Isn't that fun in css?

What's the right answer? I don't know.

You can do it totally opposite ways. That must be why some people hate css. You're like, you can do it the exact opposite way. Yes, you can. Here's the Donut Scope way of doing it, though. You could say everywhere in the entire body, all the links. I want to remove the underlines until you hit something with content and then stop doing that. It's just like a slightly more elegant way to describe that same kind of thing.

So it's kind of like. I called it the Stop Caring Selector. It's like, care, care, care, Stop caring on the inside.

So in the pink area there, that's everywhere that the Donut selector is selecting. Then it stops when it hits the white area. So it could be your body area and you could apply it to all your comments below.

That's body copy. So it should have underlines or whatever.

That's the thing. That's donut scoping. That's what it can do. We never used to be able to do that in CSS and now we can. So sick.

Niche feature accomplished. CSS is just a little bit better because we can do that a little bit. Another thing it can do is proximity. It's super weird, but here we go. There's a classic thing where the demo for this is always light mode, dark mode. Questionable, but I'm not above it. I'm going to steal it. And then we'll look at one more example of it too. This is one way that you could implement light mode and dark mode. I don't even think it's particularly popular, but here we are.

Is to use class names on stuff to apply light or dark themes to it. So in this case you can just see it.

Theme is light or it could be dark. And now we have two classes.

We have a dark theme and a light theme class. One of them is, you know how dark themes work. I'm not going to read that. Thing to you.

But the crucial part is the nesting in there. It's saying links in dark mode are a bit brighter, lighter because it's on a dark color. And the dark blue maybe wouldn't work so well, whereas the blue in the light theme is a little bit darker. So great, we've accomplished that goal. It looks good on dark mode, it looks good on light mode.

We had to mess with the blue a little bit the same. It's not every color. But in this case it was appropriate that the blue was adjusted for those modes. But again, not always Mega, mega, mega common. But it's common enough.

You could imagine a site that's light mode and then the footer's dark mode or a it switches modes on a sidebar for like design contrast or something like that. It is possible that you use themes that are nested in some way, not like mega common. But hey, now let's support it and let's see the problem that comes up with a theme within a theme like that. We have the same exact css, but now we have a nested environment.

You know, we have some links that are nested between there. That one works.

Dark mode works. We have the light blue in the body copy and the dark blue in the white box. But this one MERP has failed because the source order matters here.

Like now on that bottom example, they're both dark and they're both dark because the links, even though they're in theme dark, the theme light a selector, those links are within an element that is themed Light 2. It's just the outer one. And so specificity is winning out here and it's screwing up those links. So like, I don't know, it's like a little minor bummer. So Miriam rolls in and she's like, I think we should fundamentally change how browser rendering works.

And now there's like this additional step called proximity that kicks in when you use AT Scope in css. So instead of just the theme, we're just going to make one tiny change, which is me backpedaling on the dumbness of it. It does have an actual feature or it does have something that it does when you use ATScope instead on a class. Now proximity is going to kick in. And proximity is a little bit more powerful than source order, but still less powerful than specificity, which was nicely represented in this chart that I confirmed just moments ago was by Bramis.

Good job. But you got to fix your kerning. There's a. Some issues in there.

See that? So when you're talking about Those teal links, specifically those links, that class that's closer in the DOM has higher proximity than the class that's farther up the DOM away. So now that we've done that, we've applied scope to those classes, it's cool in dark mode and it's cool in light mode, because now the source order doesn't matter. I mean, it's not that it doesn't matter, it's just the proximity matters a little bit more.

Cool we fixed the thing. Really what that means is that I think it's a nice way to stop caring as much about source orders, not worry about that. And plus, in a world of crazy bundlers and just in time loaded styles and all that stuff, to not worry about how the order in which that styles get loaded is kind of nice. You have a card class and a card big variation class on it. If you wrote it like that, you're going to have problems because the variation class you're kind of assuming in your mind is going to win over the base class. But in this case it wouldn't because the image there, the second one is going to win over some of those styles above it.

So instead be like, I don't know my variations, I'm just going to write as scoped instead so that the proximity of them is going to win over the base class. And then you don't have to worry, like all your variation classes, you don't have to worry about the order in which that they're loaded. It's a little bit cool, it's a little bit better CSS because of it. I don't think it's changing any lives in here, but it's pretty good. This is my favorite one though, for sure. DOM blasters. This might change everything. It might not. Let's find out.

There's some HTML, there's two divs in there, and there's a paragraph inside each one of those divs. One of these divs I'm going to slag in an angle brackets style tag.

And inside that angle brackets style tag I'm going to use at scope.

And no parentheses or anything. Not the double syntax of donuts, not even the single syntax of scoping one selector. Just nothing at all. Just at scope that will still have a scope. The scope is its parent element in the dom. SOPE is the div above hiho.

Here we go. And what I'm doing is applying some border to it, that div, and then selecting the paragraph within it and making it red. If we render that the first paragraph is untouched.

And the second one has all that red crap going on. So we've achieved some kind of like weird level of what's going on there is pretty interesting to me. I think it's actually pretty rad. So it's like selector scoping, but there's no selectors involved.

It just selects the parent element. So it's a way that you can pop some styles in the DOM that just to apply to one chunk of the DOM tree, you just rip that style in there and it works.

Wow. I guess that seems surprising to me. And it's not like the Shadow DOM and that you can still query selector through it and stuff. It's not quite like the Shadow dom, but it's like the Shadow dom and it's like you can just plop some CSS in there and it's just applying to this tree of stuff without even touching the Shadow dom.

I think there's probably people that have reached for the Shadow DOM for this kind of ability that you don't have to anymore. So it's like zero tooling way to do component level scoping at least, you know, pretty rad. There's some unknown, like, is that like, maybe we should just do that all the time is the thought that occurred to me.

Like, if you really care about scoping and you're working with components in any framework or anything, maybe the way to do this is to stop using so much tooling and just throw style tags where you need them in the Dom. The concern is that you have whatever, 10,000 elements on a page and they each have a style tag. What are the concerns about that? Is that bad?

Is the browser slow about it? Is there memory problems or DOM problems or whatever? I don't know that I nailed it, but I did put like 10,000 cards on a page and had it just use regular css. And then I had one where every One of the 10,000 cards had its own hundred lines of CSS that style it just to test it. And like, unfortunately, like, it seems like it's good testing, but I think browsers these days are like just so smart at crap like that that there was like no none difference. Almost like first of all, gzip or Brotli or whatever got in there and just.

It was like magic, dude. 18 kilobytes for 150,000 lines of HTML. That's some stuff that was pretty cool that that's what comes over the network. Of course, when it like unflitter fucks or however that works, that turned into big, a lot of stuff, but like the rendering speed of it, the performance, even the memory usage of the same one. One of them is little baby one with just regular CSS. Even though there's 10,000 cards and one of them was like much, much bigger, there seemed to be no difference. Okay, bad test. Because it's not. They weren't 10,000, like different elements with interactive styles and all this stuff. But it was kind of promising to me that I'm like, hey, maybe, maybe it is zero tooling action.

More likely what I think is cool about this and I'm going to use it is the, like, I'm just in WordPress and I just want to do like a little art direction on my post or whatever. Or it doesn't. It could be any cms, but the idea is like I just, I want this paragraph to be styled specially, but I want to just write CSS to do it so I can use media queries and hover states. I don't want to write inline styles necessarily.

I can just rip a style block in the middle of a blog post and have that and not think of a selector, not worry that that selector is going to touch anything else. All that kind of stuff is pretty cool. So that's as close to like selector scoping. That reason we'd reach for CSS modules or maybe even CSS and JS or something.

There used to be this old school style scoped thing I thought was pretty cool. In the dom, we got it back. It's basically back. If you saw that and you liked it and missed it, that we have that now, it's pretty much the same thing. All right, that's enough about scope. It was kind of too much about scope to begin with, so let's just do the little recap thing. First of all, CSS is naturally scoped.

Like your brains are already all messed up with Scope. If you're good at css, you're already good at Scope, you might never think about SCOPE again. You're probably amazing at it. So cool. Be just your good, good job everybody.

And also the second you're above like just solo warrior coder working on a website by yourself kind of thing. As soon as there's like two people, I'd say, like that's the line. You have any kind of team at all or the website gets a little bit big, it's probably scoping time.

Especially if you actually had a problem with scoping. A little light layer of tooling is probably appropriate at that point. And now native CSS scoping exists and CSS is slightly better for it. You know, it's okay if you're not extremely excited about it.

That's fair, I'd say. But it's cool. It's fine. Good job, Miriam.

It's more than I've ever done. I would tell people I met at a party, I'd be like, I affected how all websites are rendered in the world. What have you done? You know?

And that those style block scoping, I think there's future there. I think hopefully that like somebody in here is like going to look into that a little bit more. I think that's like underused. Like there's something there. Like there's something to that that could be used a lot more than it already is. Thanks so much to PPK and the Gang and CSS Day. Thanks for having me. Obviously, it's mandatory to Go Pro on CodePen and thanks for having me. Cheer computers.

Thank you. Chris, please join me. Oh, yeah, we do that with the lovely flowers.

Sure. They still look good, right? The flowers. I like them. I bet there's a hot pink one in here.

Don't worry about it.

Chris. Yes. Yeah, I should fix my kerning.

On the. On the. On the chart that you showed. We have a bunch of questions from the audience.

So Dolan asked, you mentioned a lot of methods to scoping. Which one do you recommend as your go to method and why? I think you gave a hint about it at the end. Yeah, just use different classes.

Chris's microphone is not working. No, it's because I screwed it up. We have somebody coming to the rescue.

Sorry I messed up your thing. The preferred method of scoping is just to use a different class name, period.

Subu asked is adscope inside of the dom, the DOM Blaster? Is it technically a new version of internal style inline style or.

I mean the only. It's different than inline styles because you get to write actual css. Like you can't write an inline style for a hover state.

Right. I'm pretty sure you cannot. So now you can. And that's one difference. It's just regular old css, I guess. Is that the question? Let's move on. Yeah, that was the question. And there is an open CSS working group issue somewhere where people want to put selectors into style attributes, which sound so complicated, but there is an open issue for that. Hands off.

Cool. Cool. You grunted.

Hardcore scoping. Can you do that again? I liked it. Hardcore scoping.

Could you do full talk that way?

I'll get you next time, Gadget. Maybe we shouldn't we were in a former church, maybe we shouldn't do that at the end you mentioned it was in your overview site, the purple one.

I think you mentioned something like maybe we can get into this build step less type of components in the future. Is this something that you would like to do eventually get to or do you see. Well, isn't that fun when the web platform comes in and it's like, you know that thing that you used to use a tool for, now you don't need to use a tool for it again. That's like my favorite kind of platform development. And that was what's on my mind. When you looked at the, the style tag thing you're like, normally we have to use tooling to get that kind of scoping and now maybe we don't.

Can we lean into that a little harder or is the tools doing so much more that we should just keep using the tool? And it just seems like early days to know that. But maybe do you have a thought of like what are the things that are blocking you to getting there?

I mean it would be knowing what the full performance story is like. Like what if a webpage didn't even have a link tag to any CSS at all?

Every single piece of CSS that styled say fairly complicated website was a style block inside of each component. Like a real world website. Is that tenable? Like how does Chrome do, how does Firefox do, how does Safari do? Are there differences in the browser story? Is it like faster? Because CSS itself is like a blocking resource and maybe because it's not a blocking resource, maybe if streaming HTML is a thing, maybe it is somehow better even.

I don't know. That's the interesting story about all this is like what if we just didn't have the sheets part of CSS related to that? There is an add sheet proposal somewhere that is doing rounds. Microsoft is proposing it where you can define like a block with some styles that don't apply and then you can IM import those styles.

Maybe that will solve maybe the problem and maybe the next year you can come talk about it. Yeah, yeah. I mean I could imagine a world in which you write a. I think they're called CSS module scripts. Right. So instead of actually including the inline block of styles that it's still just an import statement but it's scoped anyway because you.

I don't know. That seems far fetched. I don't understand how like I really don't actually know if that's a good idea or Not. It just occurs to me donut scoping back in the day also might have seemed far fetched.

But like as you mentioned, like blog about it and then wait 10 years and then we have it. Yeah, I saw you mentioned at rule the atrual function to check what is that real? Was I right?

You know, stuff. Is that real? That thing exists in a working group resolution, I think. I think it also exists in the spec. But yeah, it's one of those ones where like, okay, we decided we're doing it and then nobody does it. It's behind the flag in Chrome right now, but there's some open issues with that.

Is there advice that you can give to people because you now mentioned AD rule. One of the things which I interpret as a hint of you want this.

Is there advice that you can give to our audience right here. Like, hey, if you want a certain feature to happen, what should they do?

I know what you mean, but I'm not super sure you're closer to that stuff than I am. Like, what should they do? Should they like thumbs up the GitHub issue or. Yeah, one thing I don't know.

Yeah, I mean be heard I guess. I mean blogging goes a long way, so maybe we'll do that. Do the blogging. Yeah. And then if you add an RSS feed to it, which you should, then I will read it.

So win. Win.

Oh, this is crazy. Like this is a live doc, so there's more questions trickling in and layout is shifting around. Like we have cls. I think it's web sockets or how does it work? I don't know. Technology hooray.

Maxim is asking how do we feel about Bam in 2025?

The City of Bend, where I live. B E M. Sorry.

Bem. Yes. Block elements modifier thing. That is weird. I was never super into bam. I get it. It was the idea of flattening scope. So it's related to my talk. I guess the idea was let's not write so much nested css. Let's have even the internal elements just become a class selector so that the specificity was flattened out, that everything just had that class level specificity.

So overriding things became easier. I don't hate that as a concept.

That's kind of smart, I guess. But it was really strict. I feel like there was wasn't even like, wasn't it Yandex behind it like this Russian search engine and anytime I mentioned it in a blog post they would email me immediately and be like, you should link to the official Yandex documentation for bem. And I'd be like, I will link to what I please to link to.

You know, it just was weird. It was like something about bem, like, turned me off from, like, a society perspective. I think they're reading your RSS feed and then that's how they get the info.

Yeah, maybe. But whatever, you can bam it up. It's fine.

It was just a convention. It was like double underscores or double dashes. Cool. That's it. Like, if that's how you want to roll, roll like that.

Maybe I'll use single dashes, which tailwind uses. Which is a nice segue to the next question.

Like, Rick. Rick is asking, like, okay, what's your. This is a spicy question that we saved for the last. Right. What is your honest opinion about tailwind and the way it scopes? I don't know, it's just not.

I always do that, like, let's do the sandwich technique where you say something nice and then you say something mean and then you say something nice again.

No, that's not what I'm going to do. But it has cool performance characteristics.

I always thought it was cool that, like, it, like, reads all the classes that you used and then produces the smallest possible CSS style sheet that accomplishes all the classes that you're going to use. And it ends up. It's probably smaller CSS than I would write, or we would mostly write. That's cool. That's a good performance characteristic of the thing. And if you are, like, spiritually can look at tailwind classes all up in your HTML and be like, that is working for me. What do I care? Good job. I always just didn't like it. And I like that it's like, I already know css, but it's an abstraction over CSS that constantly has to keep up with the language of css. And something about that bugs me, too. And, like, how do I express media queries? Well, obviously they have a syntax for it, but then it's like I'm writing a bunch of styles and then I'm writing them again with, like, MD colon on it and stuff. It just. There's something about it that just. It just bugs me syntactically.

But I don't, like, care that it exists. I'm just not into it, you know? Thanks. And with that, please give a warm hand for Chris.

Chris Coyier presents:

SCOPE in CSS

Illustration of a green bottle with a label reading "SCOPE in CSS".

How to CSS

Illustration of a cartoon character from the "For Dummies" book series, wearing a yellow shirt labeled "For Dummies", pointing towards the slide title.

How to CSS

  1. Write some scope
  2. Write some style
Illustration of a cartoon character wearing a yellow "for dummies" t-shirt, pointing upward.

Every selector is scope.

Cool — you've got some styles.

Where and when do you want to apply them?

Cool — you've got some styles.

Where and when do you want to apply them?


				.date {
					color: #222;
					font-size: x-large;
					margin: 0 .4em;
				}
				
Screenshot of a code editor displaying a CSS code example for styling a date element, alongside a stylized "CSS Day" event logo.

Scope


				.date {
					color: #222;
					font-size: x-large;
					margin: 0 .4em;
				}
				
Screenshot showing a CSS code snippet for a ".date" class alongside part of a website interface with a "CSS DAY" logo. An annotation arrow visually points from the word "Scope" to the code.

Scope


				.date {
					color: #222;
					font-size: x-large;
					margin: 0 .4em;
				}
				@media only screen and (min-width: 48em) {
					.date {
						margin: 0;
					}
				}
				
Screenshot of CSS code with multiple "Scope" callouts, illustrating how scoped CSS and media queries affect rule application.

CodePen

Screenshot of the CodePen homepage, showing the platform's interface for building, testing, and discovering front-end code, with sample HTML, SCSS, and JS code snippets displayed.

				.card {
					background: gray;
					color: #eee;
					border-radius: 10px;
					padding: 1rem;
				}
				
Card-style UI element featuring a graphic and the text "Learn & Discover" with a description about CodePen Challenges and a button labeled "Join this Week's Challenge".

				.card {
					background: gray;
					color: #eee;
					border-radius: 10px;
					padding: 1rem;
				}
				
Screenshot of a web application interface showing card-like UI components for "Learn & Discover" and "This Week's Challenge: Multiple Choice" on CodePen.

				.card {
					background: gray;
					color: #eee;
					border-radius: 10px;
					padding: 1rem;
				}
				.challenge-card {
					background: lightgray;
					color: #eee;
					padding: 1.5rem;
				}
				
Screenshot of a code editor showing CSS for card and challenge-card components, alongside a user interface with informational card widgets about "Learn & Discover" and a "Multiple Choice" challenge.

If that’s all the scoping you ever do in CSS, that’s… fine.

Really, actually, very fine. 👍

The only reason to move on to more advanced scoping is when you’re actually experiencing problems.

One problem could be that you forgot that you used card as a class already, redeclared it for a new use, and got weird results. Or that you work on a team and someone else does that and is confused, doesn’t realize it, or messes with the other CSS out of that confusion.

One problem could be that you forgot that you used card as a class already, redeclared it for a new use, and got weird results.

Or that you work on a team and someone else does that and is confused, doesn’t realize it, or messes with the other CSS out of that confusion.


				.card {
					...
				}
				

				.card {
					...
				}
				
Two user profile card mockups are shown, each with a different person’s photo and identical .card CSS styling code. To the right, there are UI widgets simulating dashboard cards: one labeled "Learn & Discover" with descriptive text about CodePen Challenges, and another labeled "Multiple Choice" promoting a weekly challenge, each styled as interactive cards.
Two CSS properties walk into a bar.

A barstool in a completely different bar falls over.

— Thomas Fuchs, Twitter, 28 Jul 2014

Screenshot of a tweet by Thomas Fuchs featuring a joke about CSS properties and unintended consequences.

Two CSS properties walk into a bar.
A barstool in a completely different bar falls over.

— Thomas Fuchs (@thomasfuchs)

Writing CSS can have unintended consequences.

Screenshot of a tweet displayed on a slide, followed by a humorous caption about CSS.

Writing CSS can have unintended consequences.

Screenshot of a tweet by Thomas Fuchs with a CSS joke, annotated with a "CSS Brain" label and a code snippet "text-wrap: pretty;".
There are tools to take the idea of scoping further.
be like water yo.
— John Allsopp
Portrait-style photo of a person with a beard and red reflective peace sign sunglasses.
scope the snot out of everything.
— me in this talk
Large nervous emoji illustration on the right side of the slide
There is web platform stuff. We have @scope now in CSS and I promise we'll talk about that — later.

There are tools that process HTML/CSS/JavaScript in order to help with scoping.
Some of them are explicitly about selector scoping and for some it’s an implied benefit.

CSS Modules

CSS MODULES

🎂 10 year birthday!

Screenshot of the CSS Modules GitHub contributors page, including graphs of contributions over time.

CSS modules implies a build process for the CSS and is exclusively for JavaScript-produced HTML.


				.card {
					background: gray;
					color: #eee;
					border-radius: 10px;
					padding: 1rem;
				}
				
Screenshot of a code editor showing a card.module.css file with a .card class definition.

CSS modules implies a build process for the CSS and is exclusively for JavaScript-produced HTML.

  • card.module.css
    
    				.card {
    					background: gray;
    					color: #eee;
    					border-radius: 10px;
    					padding: 1rem;
    				}
    				
  • Produced CSS
    
    				.card-Adf98xd {
    					background: gray;
    					color: #eee;
    					border-radius: 10px;
    					padding: 1rem;
    				}
    				
Two code blocks side by side. The left shows a CSS module file with a generic class name, while the right shows the produced CSS where the class name is scoped with a unique suffix. An arrow points from the first code block to the second, indicating transformation.

CSS modules implies a build process for the CSS and is exclusively for JavaScript-produced HTML.

  • card.module.css
    
    				.card {
    					background: gray;
    					color: #eee;
    					border-radius: 10px;
    					padding: 1rem;
    				}
    				
  • Produced CSS
    
    				.card-Adf98xd {
    					background: gray;
    					color: #eee;
    					border-radius: 10px;
    					padding: 1rem;
    				}
    				
Two side-by-side code boxes: the left shows a mild pepper icon labeled "MILD" next to CSS code from "card.module.css" using a simple class; the right shows the produced CSS after a build process, with the class name hashed (".card-Adf98xd"). An arrow points from the original file to the produced CSS, illustrating the transformation.

Component.js

import styles from "./card.module.css";
		
				element.innerHTML = `
					<div class="${ styles.card }">
				`;

What is imported

export default const styles = {
					"card": "card-Adf98xd"
				}

Component.js

import styles from "./card.module.css";
		
				element.innerHTML = `
					<div class="` + styles.card + `">
				`;
Arrow illustration connecting an object definition example to its use in a JavaScript file, showing how a mapped style name is imported and applied via styles.card.

What is imported

export default const styles = {
					"card": "card-Adf98xd"
				}

Component.js

import styles from "./card.module.css";
		
				element.innerHTML = `
					<div class='` + styles.card + `'>
				`;

Produced HTML

<div class="card-Adf98xd">
				

Produced HTML

<div class="card-Adf98xd">

Produced CSS


				.card-Adf98xd {
					background: gray;
					color: #eee;
					border-radius: 10px;
					padding: 1rem;
				}
				
Illustration of a three-legged yellow stool centered below two code blocks labeled "Produced HTML" and "Produced CSS".

CSS Modules is just a spec. You won’t see it in your package.json file. Other tech just builds it in.

  • Next.js
  • Astro
  • Vue SFCs
  • Gatsby
  • SvelteKit
  • Nuxt.js
  • webpack css-loader
  • Parcel
  • Vite
  • create-react-app
  • Bun
  • Lightning CSS
  • Rspack
  • PostCSS
  • Solid

Import

  • Co-locate the component styles

Use

VS Code editor screenshot showing a React component with a highlighted import statement for a SCSS module and the usage of the imported styles in the className property. The file tree is visible, illustrating the SCSS and TSX files located in the same folder.

Orthogonal Problem

Scoping CSS is a related concept of unused CSS.
Performance tools can bitch at you about unused CSS but it’s hard to accurately detect site-wide and teams can be scared to touch global CSS because of unknown consequences.

Diagram showing a large, diagonal arrow pointing upward, suggesting growth or escalation.

Orthogonal Problem

Scoping CSS is a related concept of unused CSS. Performance tools can bitch at you about unused CSS but it’s hard to accurately detect site-wide and teams can be scared to touch global CSS because of unknown consequences.

Screenshot of a code editor (VS Code) showing a login.scss file with CSS code and a Git-related icon overlayed.

Unused CSS is less of a concern with scoped and co-located styles.

Toss the component — or don’t use it — the styles aren’t loaded.

Illustration of a trash can with its lid floating above, representing discarding unused CSS or components.

CSS-in-JS is scoping, too.

CSS in React

MILD CSS in React

  • Styled Components
  • Emotion
  • JSS
  • styled-jsx
  • Vanilla Extract
  • Stitches
  • Linaria
  • CSS Modules
  • Styletron
  • Fela
  • Goober
  • StyleX
  • Radium
  • Aphrodite
Image of a slide with a large logo showing a chili pepper labeled "MILD" beside the text "CSS in React". Below, there are three columns listing various CSS-in-JS and CSS module libraries for React.

PostCSS to transform the following:

<style scoped>
				.example {
					color: red;
				}
				</style>
		
				<template>
					<div class="example">hi</div>
				</template>
				

Into the following:

<style>
				.example[data-v-f3f3eg9] {
					color: red;
				}
				</style>
		
				<template>
					<div class="example" data-v-f3f3eg9>hi</div>
				</template>
				

Keep the class and scope the styles.

Scoped styles

Screenshot of the Svelte documentation page about scoped styles.

The web platform doesn’t really help us with this kind of selector scoping.

It goes from very lightweight to very extreme.

The web platform doesn't really help us with this kind of selector scoping.

It goes from very lightweight to very extreme.

Hardcore SCOPING

<iframe>

<iFrame>

Photograph of a Steve Jobs holding a small white rectangle, with the caption ">iFrame<" superimposed, referencing the HTML iframe element.
<!DOCTYPE html>
				<html lang="en">
				  <head>
				    <meta charset="UTF-8">
				    <meta name="viewport" content="width=device-width, initial-scale=1.0">
				    <title>My New Pen</title>
				    <link rel="stylesheet" href="./style.css">
				  </head>
				  <body>
				    <main>
				      <iframe src="https://www.cssday.nl/"></iframe>
				    </main>
		
				    <script src="./script.js"></script>
				  </body>
				</html>
				

Shadow DOM

Illustration of the character Shadow the Hedgehog standing with arms crossed next to the words "Shadow DOM".
<svg style="display:none" xmlns="http://www.w3.org/2000/svg">
					<defs>
						<symbol id="user-icon" viewBox="0 0 32 32">
							<path d="M25.333 9.335c0 5.153-4.179 9.333-9.333 9.333s-9.333-4.18-9.333-9.333c0-5.156 4.18-9.335 9.333-9.335zM23.203 18.988c-2.008 1.516-4.499 2.427-7.203 2.427-2.707 0-5.199-0.913-7.209-2.429-5.156 3.231-3.467 10.675 8.797 13.092z"></path>
						</symbol>
					</defs>
				</svg>
		
				<div class="icon">
					<svg width="1em" height="1em">
						<use xlink:href="#user-icon"></use>
					</svg>
				</div>
				
Screenshot of a code editor showing SVG markup for a user icon symbol, along with browser DevTools displaying the resulting rendered icon and its HTML structure.

Web Components allow you to make your own Shadow DOM.

They don’t need to use Shadow DOM, and honestly I often prefer not to, but it is a powerful scoping tool unique to the platform.

class FancyButton extends HTMLElement {
					connectedCallback() {
						this.attachShadow({ mode: "open" });
		
						const style = document.createElement("style");
						style.textContent = `
							button {
								background: #1E88E5;
								color: white;
								padding: 2rem 4rem;
								border: 0;
								font-size: 1.5rem;
								margin-block-end: 2rem;
							}
						`;
						this.shadowRoot.appendChild(style);
		
						this.shadowRoot.appendChild(this.firstElementChild);
					}
				}
		
				window.customElements.define("fancy-button", FancyButton);
<fancy-button>
					<button>Web Component Button</button>
				</fancy-button>
				
class FancyButton extends HTMLElement {
					connectedCallback() {
						this.attachShadow({ mode: "open" });
		
						const style = document.createElement("style");
						style.textContent = `
							button {
								background: #1E8E5E;
								color: white;
								padding: 2rem 4rem;
								border: 0;
								font-size: 1.5rem;
								margin-block-end: 2rem;
							}
						`;
						this.shadowRoot.appendChild(style);
						this.shadowRoot.appendChild(this.firstElementChild);
					}
				}
		
				window.customElements.define("fancy-button", FancyButton);
				
Split-screen code example: On the left, HTML usage of a custom <fancy-button> web component containing a button. On the right, the corresponding JavaScript code defines the FancyButton class as a custom element with Shadow DOM encapsulation and attached styles.
<fancy-button>
					<button>Web Component Button</button>
				</fancy-button>
				
class FancyButton extends HTMLElement {
				  connectedCallback() {
				    this.attachShadow({ mode: "open" });
		
				    const style = document.createElement("style");
				    style.textContent = `
				      button {
				        background: #1E88E5;
				        color: white;
				        padding: 2rem 4rem;
				        border: 0;
				        font-size: 1.5rem;
				        margin-block-end: 2rem;
				      }
				    `;
				    this.shadowRoot.appendChild(style);
				    this.shadowRoot.appendChild(this.firstElementChild);
				  }
				}
		
				window.customElements.define("fancy-button", FancyButton);
				
Illustration showing a large blue button labeled "Web Component Button" as rendered output from the code example.
<fancy-button>
					<button>Web Component Button</button>
				</fancy-button>
				
<button>Not Web Component Button</button>
				
class FancyButton extends HTMLElement {
					connectedCallback() {
						this.attachShadow({ mode: "open" });
		
						const style = document.createElement("style");
						style.textContent = `
							button {
								background: #1E88E5;
								color: white;
								padding: 2rem 4rem;
								border: 0;
								font-size: 1.5rem;
								margin-block-end: 2rem;
							}
						`;
						this.shadowRoot.appendChild(style);
						this.shadowRoot.appendChild(this.firstElementChild);
					}
				}
		
				window.customElements.define("fancy-button", FancyButton);
Visual example in the center of the slide showing a styled button labeled "Web Component Button" to illustrate how a custom element renders differently compared to a standard button.

Web Components are pretty sweet for design systems. Theoretically? No, actually.

Web Components are pretty sweet for design systems. Theoretically? No, actually.

  • Google Material Design
  • Adobe Spectrum
  • Microsoft FAST
  • IBM Carbon
  • Salesforce Lightning
  • Shoelace / Web Awesome

UTILITY CLASS SCOPING

Styling via classes that do one thing each.

Without ever leaving your HTML.

Screenshot of a code editor showing HTML with Tailwind CSS utility classes, and a preview card displaying an album cover and information for "Class Warfare" by "The Anti-Patterns".

With Tailwind, you’re mostly not writing CSS. CSS is a byproduct of HTML classes.

Utility styling classes applied directly to HTML is inherently tightly scoped. You can’t accidentally style something else.

<div class="flex flex-col items-center gap-6 p-7 md:flex-row md:gap-8 rounded-2xl">
				  <div>
				    <img class="size-48 shadow-xl rounded-md" alt="" src="/img/cover.png" />
				  </div>
				  <div class="flex items-center md:items-start">
				    <span class="text-2xl font-medium">Class Warfare</span>
				    <span class="font-medium text-sky-500">The Anti-Patterns</span>
				    <span class="flex gap-2 font-medium text-gray-600 dark:text-gray-400">
				      <span>No. 4</span>
				      <span>.</span>
				      <span>2025</span>
				    </span>
				  </div>
				</div>
				
Screenshot of a code editor displaying HTML code for a layout with an image, headings, and the text "Class Warfare", "The Anti-Patterns", "No. 4", and "2025". There is also a colorful punk band-themed image labeled "The Anti-Patterns" visible within the editor preview.

ok ok ok

Vanilla CSS
@scope

Credit where credit is due

I'm just playing around, using what I can, and reading the same documentation and articles as y'all. So high-five to all the people who work on MDN blog elsewhere about all this stuff.

Logos of an RSS feed, a terminal or developer tool, MDN Web Docs, and one additional stylized logo are displayed in a row.

Browser compatibility

Interop 2025

Browser compatibility table screenshot showing support for @scope across different browsers, and a screenshot of the web.dev website listing focus areas for Interop 2025, including @scope and other web features.

Allow me to introduce @scope with a stupid usage scenario that doesn't help anything.

Then we’ll learn later that it’s not entirely stupid.

<div class="card">
					<h3>Title</h3>
					<p>Description</p>
					<a 
						href="/product/a" 
						class="button">
						View Product A
					</a>
				</div>
				

				.card {
					h3 {}
					p {}
					.button {}
				}
				
<div class="card">
					<h3>Title</h3>
					<p>Description</p>
					<a
						href="/product/a"
						class="button">
						View Product A
					</a>
				</div>
				

				.card {
					h3 {}
					p {}
					.button {}
				}
				

				@scope (.card) {
					:scope {}
					h3 {}
					p {}
					.button {}
				}
				

				@supports (at-rule(@scope)) {
					@scope (.card) {
						:scope {}
						h3 {}
						p {}
						.button {}
					}
				}
				

				@supports (at-rule(@scope)) {
					@scope (.card) {
						:scope {}
						h3 {}
						p {}
						.button {}
					}
				}
				if (window.CSSScopeRule) {
					document.documentElement.classList.add("scope-supported");
				}
				

The basic thing that is not useful with @scope

  1. Just scoping a single selector

Three niche things that are useful about @scope

  1. Donut Scope
  2. DOM Blasters
  3. Proximity

Scope donuts

Screenshot of a blog post by Nicole Sullivan titled "Scope donuts" on stubbornella.org. Overlaid is a screenshot from the same article showing a browser window with annotations labeling sections "donut" and "hole", illustrating scoped tabs. A large "2011" appears in a black circle referencing the year of the post, and text "GOAT" with an arrow pointing to Nicole Sullivan's name.

Undergraduate Disproves 40-Year-Old Conjecture, Invents New Kind of Hash Table

Screenshot of a Wired article about a new kind of hash table invented by an undergraduate, featuring an illustration of an open drawer with compartments.

Underlines in body copy is good.

Screenshot of an article on Wired.com with green arrows pointing to underlined links in the body copy, emphasizing proper use of underlines for accessibility.

Don't need the underline in other areas.

Screenshot of a WIRED article webpage with red arrows pointing to navigation links and content areas; a large red circle highlights the statement about not needing link underlines in those areas.

				/* links are underlined
				in user-agent stylesheet */
				.site-nav {
					a {
						text-decoration: none;
					}
				}
				.popular-articles {
					h2 a {
						text-decoration: none;
					}
				}
				

				a {
					text-decoration: none;
				}
				.content {
					a {
						text-decoration: revert;
					}
				}
				

Remove underline from links everywhere... until you get to any element with a class of content, then stop doing that.


				@scope (body) to (.content) {
					a {
						text-decoration: none;
					}
				}
				

Donut


				@scope (body) to (.content) {
					a {
						text-decoration: none;
					}
				}
				
Diagram illustrating a browser window with a central white box labeled <article class="content"> and two outlined boxes below labeled <aside class="comment content">, all surrounded by a pink background, representing the area affected by a scoped CSS selector.

DOnut

Ooooooooooooo lexical scoping

Illustration of Homer Simpson reaching up to grab a large pink donut-shaped letter "O" in the word "Donut". The visual emphasizes the "O" and the phrase "lexical scoping" related to donuts.

Proximity

Diagram illustrating the concept of proximity with several circles connected by curved lines.
Light Mode / Dark Mode has become the ultimate classic demo for this, and I’m not above it, so let’s do it. We’ll do another one too.
Icon illustration of a light switch positioned to the left of the text.
<!-- or theme-dark -->
				<main class="theme-light">
					<p>
						Lorem ipsum dolor sit amet consectetur adipisicing elit. <a href="#blah">Vero, ullam ad.</a> Quo deleniti mollitia iste consequuntur, itaque nesciunt. Quae officiis architecto iusto libero molestiae temporibus reprehenderit nobis quaerat. Corrupti, nam?
					</p>
				</main>
				
Screenshot of code showing an HTML structure for theming using a theme-light class, including a placeholder text paragraph with a link.

				.theme-dark {
					background: black;
					color: white;
					a {
						color: oklch(0.9 0.15 212);
					}
				}
				.theme-light {
					background: white;
					color: black;
					a {
						color: oklch(0.6 0.15 212);
					}
				}
				
Screenshot of CSS code demonstrating theme-dark and theme-light class styles for background, text, and link colors.

				.theme-dark {
					background: black;
					color: white;
					a {
						color: oklch(0.9 0.15 212);
					}
				}
				.theme-light {
					background: white;
					color: black;
					a {
						color: oklch(0.6 0.15 212);
					}
				}
				

lighter is annotated near 0.9 in oklch(0.9 0.15 212).

Screenshot of CSS code demonstrating color adjustment for dark and light themes, with an annotation indicating the value that controls a "lighter" color for link text in dark mode.

				.theme-dark {
					background: black;
					color: white;
					a {
						color: oklch(0.9 0.15 212);
					}
				}
				.theme-light {
					background: white;
					color: black;
					a {
						color: oklch(0.6 0.15 212);
					}
				}
				
  • lighter: oklch(0.9 0.15 212) for links in dark mode
  • darker: oklch(0.6 0.15 212) for links in light mode
Two side-by-side screenshots: one shows a block of text on a black background with a bright blue link, and the other shows the same text on a white background with a darker blue link. The images visually compare link colors on dark versus light themes.
<main class="theme-dark">
					<p>
						Lorem ipsum dolor sit amet consectetur adipisicing elit. <a href="#blah">Vero, ullam ad.</a> Quo deleniti mollitia iste consequuntur, itaque nesciunt. Quae officiis architecto iusto libero molestiae temporibus reprehenderit nobis quaerat. Corrupti, nam?
					</p>
					<footer class="theme-light">
						<a href="">Link</a>
						<a href="">Link</a>
						<a href="">Link</a>
					</footer>
				</main>
				
Screenshot of a code editor displaying an HTML code example with nested theme classes in main and footer elements.
<main class="theme-dark">
					<p>
						Lorem ipsum dolor sit amet consectetur adipisicing elit. <a href="#blah">Vero, ullam ad.</a> Quo deleniti mollitia iste consequuntur, itaque nesciunt. Quae officiis architecto iusto libero molestiae temporibus reprehenderit nobis quaerat. Corrupti, nam?
					</p>
					<footer class="theme-light">
						<a href="">Link</a>
						<a href="">Link</a>
						<a href="">Link</a>
					</footer>
				</main>

theme within theme (annotation pointing to the <footer class="theme-light"> element)

Diagram showing a code example of a main element with a dark theme containing a footer with a light theme, illustrating "theme within theme" with a highlighted annotation and arrow.

				.theme-dark {
					background: black;
					color: white;
					a {
						color: oklch(0.9 0.15 212);
					}
				}
				.theme-light {
					background: white;
					color: black;
					a {
						color: oklch(0.6 0.15 212);
					}
				}
				
Screenshot of a CSS code block with a dark-themed content preview showing a paragraph, a hyperlink, and three underlined links inside a white box. A thumbs-up emoji is shown, indicating correct or successful output.

				.theme-dark {
					background: black;
					color: white;
					a {
						color: oklch(0.9 0.15 212);
					}
				}
				.theme-light {
					background: white;
					color: black;
					a {
						color: oklch(0.6 0.15 212);
					}
				}
				
Source order matters.
Annotated code sample with two UI mockups: one black themed card with correct link colors marked as correct, and one white themed card with incorrect link colors marked as incorrect. An arrow highlights "Source order matters" to show how CSS source order affects theming results.

				@scope (.theme-dark) {
					background: black;
					color: white;
					a {
						color: oklch(0.9 0.15 212);
					}
				}
				@scope (.theme-light) {
					background: white;
					color: black;
					a {
						color: oklch(0.6 0.15 212);
					}
				}
				
Diagram showing a progression of CSS factors: Origin and Importance, Context, Element Attached Styles, Layers, Specificity, Scope Proximity (highlighted), and Order Appearance, illustrating their order of influence in CSS styling. Chrome for Developers logo is present.

For the links...

<main class="theme-dark"> This class has lower proximity.
					<p>
						Lorem ipsum dolor sit amet consectetur adipisicing elit. <a href="#blah">Vero, ullam ad.</a> Quo deleniti mollitia iste consequuntur, itaque nesciunt. Quae officiis architecto iusto libero molestiae temporibus reprehenderit nobis quaerat. Corrupti, nam?
					</p>
					<footer class="theme-light"> This class has higher proximity.
						<a href="">Link</a>
						<a href="">Link</a>
						<a href="">Link</a>
					</footer>
				</main>
				
Screenshot of an HTML code snippet with visual annotations illustrating the concept of CSS proximity and how class scope affects link styling.

				@scope (.theme-dark) {
					background: black;
					color: white;
					a {
						color: oklch(0.9 0.15 212);
					}
				}
				@scope (.theme-light) {
					background: white;
					color: black;
					a {
						color: oklch(0.6 0.15 212);
					}
				}
				
Two visual examples are shown: one boxed layout with a dark theme (black background, white text, blue links) and a thumbs up, and a second with a light theme (white background, black text, blue links) and a thumbs up. Both contain example paragraph and link content, illustrating the applied CSS from the code blocks for dark and light modes.

ok class — what did we learn?

@scope can help reduce concerns about source order.

<div class="card">
					<img />
					<h2>I'm a card</h2>
				</div>
		
				<div class="card card-big">
					<img />
					<h2>I'm a card</h2>
				</div>
				
<div class="card">
					<img />
					<h2>I'm a card</h2>
				</div>
		
				<div class="card card-big">
					<img />
					<h2>I'm a card</h2>
				</div>
				

				.card-big {
					grid-column: span 2;
					display: flex;
					img {
						width: 50%;
						height: 100%;
					}
				}
				.card {
					img {
						width: 100%;
						aspect-ratio: 16 / 9;
						object-fit: cover;
					}
				}
				
<div class="card">
					<img />
					<h2>I'm a card</h2>
				</div>
		
				<div class="card card-big">
					<img />
					<h2>I'm a card</h2>
				</div>
				

				.card-big {
					grid-column: span 2;
					display: flex;
				}
				.card-big img {
					width: 50%;
					height: 100%;
				}
				.card img {
					width: 100%;
					aspect-ratio: 16 / 9;
					object-fit: cover;
				}
				
Grimacing face emoji placed between two code examples, indicating something problematic or undesirable in the code structure or outcome.
<div class="card">
					<img />
					<h2>I'm a card</h2>
				</div>
		
				<div class="card card-big">
					<img />
					<h2>I'm a card</h2>
				</div>
				

				@scope (.card-big) {
					:scope {
						grid-column: span 2;
						display: flex;
						img {
							width: 50%;
							height: 100%;
						}
					}
				}
				.card {
					img {
						width: 100%;
						aspect-ratio: 16 / 9;
						object-fit: cover;
					}
				}
				
Smiling emoji appears next to the second HTML card example, indicating a preferred or successful approach.

Keeps specificity equal

<div class="card">
					<img />
					<h2>I'm a card</h2>
				</div>
		
				<div class="card card-big">
					<img />
					<h2>I'm a card</h2>
				</div>
				

				@scope (.card-big) {
					:scope {
						grid-column: span 2;
						display: flex;
						img {
							width: 50%;
							height: 100%;
						}
					}
				}
				.card {
					img {
						width: 100%;
						aspect-ratio: 16 / 9;
						object-fit: cover;
					}
				}
				
Diagram showing the use of the CSS @scope rule with a red callout labeled "Keeps specificity equal" pointing to the scoped selector. Includes emoji for emphasis.

DOM Blasters

One-offs FTW

Illustration of a starry space background with pixelated alien invaders on each side of the slide title, evoking a retro video game theme.
<main>
		
					<div>
						<p>Dum de dum de dum.</p>
					</div>
		
					<div>
						<p>Hi ho here we go.</p>
					</div>
		
				</main>
				
VS Code editor showing HTML code with two divs each containing a paragraph.
<main>
					<div>
						<p>Dum de dum de dum.</p>
					</div>
		
					<div>
						<p>Hi ho here we go.</p>
						<style>
						@scope { /* Scope is the <div> above, as this is a direct child. */
							:scope { /* This selects the <div>, optional. */
								border: 1px solid red;
		
								/* I can use CSS nesting in here, ensuring *everything* is safely scoped */
								p {
									color: red;
								}
							}
						}
						</style>
					</div>
				</main>
				
<main>
				  <div>
				    <p>Dum de dum de dum.</p>
				  </div>
				  <div>
				    <p>Hi ho here we go.</p>
				    <style>
				      @scope { /* Scope is the <div> above, as thi
				        :scope { /* This selects the <div>, optio
				          border: 1px solid red;
				          /* I can use CSS nesting in here, ensur
				          p {
				            color: red;
				          }
				        }
				      }
				    </style>
				  </div>
				</main>
				
Screenshot of a code editor with HTML and CSS showing scoped styling alongside a browser window preview displaying the result: two paragraphs, with the second paragraph inside a red-bordered box and red text.

This...is...basically selector scoping?

Illustration of cartoon eyes looking to the side, suggesting surprise or scrutiny.

This... is... basically selector scoping?

Without the selectors? The parent, anyway.

Just put the <style> where you wanna scope.

Wow?

Kinda like Shadow DOM without using the Shadow DOM.

Illustration of a pair of cartoon eyes looking to the right.

But there are unknowns here for me.

Is it performant to have every single component on a site have a <style> tag plopped inside of it? Is there DOM weight or memory problems with that? Is it not taking advantage of the power of cache? Would HTML streaming help?

Normal CSS. Arbitrary. Proper amount.

Screenshot of a web page displaying a grid of identical "Premium Experience" cards with features and a "Get Started" button, demonstrating large-scale repetition of styled components.

1,000 Cards

Normal CSS Inline @scope
HTML 12,000 lines
3.0 kB transferred
472 kB resource
150,000 lines
18 kB transferred
3,176 kB resource
LCP
(20x CPU Slowdown
kept parity)
1 s 1 s
Lighthouse Performance 100 100
Memory 150 MB 150 MB

Low Effort Art Direction

My Cool Blog Post.

Let's all take a nap.


				<style>
				@scope {
					p {
						font-size: 70px;
						color: red;
						line-height: 1.1;
					}
				}
				</style>
				

And then another one.

Screenshot showing a blog post editor with a custom scoped CSS style on the left and a mobile preview of the rendered blog post on the right, demonstrating the applied styles.
This is as close to native CSS helping with selector scoping as we’ve got. It’s just as good as <style scoped>, and I’m tempted to say as good as we’ll ever get.

ok let's be done.

The Takeaways

  • CSS is naturally scoped. That might be all you need.
  • Selector scoping is pretty sweet on large sites with teams. Lots of existing tools help with selector scoping, or avoid the need for it.
  • Native @scope is nice to have, I guess. Donut scoping and proximity are a small upgrade to CSS.
  • <style> block @scope might be great. Tool-free component scoping. Can we make it performant?

Thank you!

Also, subscribe to CodePen PRO, obviously.

Logo of "CSS DAY" stylized in a blocky geometric font with a hand making the "rock on" gesture beside it.
  • CSS Selectors
  • CSS Property: font-size
  • Media Queries
  • CSS Property: border-radius
  • CSS Class Naming Conflicts
  • CSS Unintended Consequences
  • CSS Modules
  • CSS Modules Build Process
  • CSS-in-JS
  • React CSS-in-JS Libraries
  • Scoped Styles in Vue
  • Selector Scoping Limitations
  • HTML Element: iframe
  • Shadow DOM
  • Web Components
  • Utility Class Scoping
  • Tailwind CSS
  • Browser Compatibility for @scope
  • CSS Donut Scope
  • CSS Proximity
  • CSS Source Order Concerns
  • DOM Blasters
  • Style Tag Scoping
  • Native CSS Selector Scoping