999: Writing Maintainable CSS

š **Source:** [999: Writing Maintainable CSS](https://share.snipd.com/episode/d5570267-8c8a-4817-a364-a56f2ef67683)
## Highlights
- **Style Leak Is The Fastest Sign Of Rot**
- CSS is manageable when styles don't leak and components can be removed and still look correct.
- Wes Bos explains a component should render on a blank page and still appear intact, exposing style leaks as a key rot signal.
Transcript:
Wes Bos
A very high level, CSS does not leak into other parts of the website. This is a very clear one, is if you are writing some CSS and those styles are accidentally leaked to other parts of the website, or those styles are so broad that you would not be able to Take part of a website away. We'll use the word component in this. You should be able to take a component out of your website and render it on a white page, and it should still look mostly the same or entirely the same, right? And if that component only looks that way inside of a specific part of your website you have a bit of a rat's nest of css yeah man which is something that i i feel like you know with the current
Scott Tolinski
Landscape of how ai likes to write css and things like that it it's definitely a code smell that i think a lot of people will have in their projects.
Wes Bos
Yeah, I feel like it's less of an issue these days as people, most people are using something in their CSS. Instead of just having one big style.css file and dumping a whole bunch of rules in there, I feel like it's a little bit more maintainable. Most people have some sort of idea of components and hopefully some way to manage that CSS. But even with those things in place, it can still get out of hand. Yeah.
Scott Tolinski
And I think a bigger concern these days is having every single component as a tight unit that if you do adjust is a global anything, then that tight unit will remain in the old style of things, Right? It's like a lack of the cascade is a huge problem.
Wes Bos
The tight units are a problem. Tight units. Another thing, what makes CSS maintainable is, it easy to maintain updates edits this includes being able to find the actual thing because there are certainly lots of css approaches Out there that are so stringent and so hard to use that people end up not being able to do what they want right and you baked into... This is kind of the thing with design systems as well. With larger companies, you need... The larger the company, the more places that it touches, you need a more strict design system so that you can enforce these things across the thing. And that always comes with a trade-off of like, I'm not actually able to... A good design system will let you do whatever it is that you want. But there certainly is this other spot where if it's too stringent, you're not able to actually implement the design that ([TimeĀ 0:01:50](https://share.snipd.com/snip/c3d9530b-3aa0-4cf2-8230-4ec28e27a761))
- **Build Flex First Components**
- Embrace flexible CSS so components adapt instead of reimplementing styles per breakpoint.
- Wes Bos recommends container queries, fluid values and updating variables at breakpoints rather than repeating selectors across many media queries.
Transcript:
Wes Bos
Now, here's one thought that I have with a lot of CSS that people have is that your CSS needs to embrace the flex. And what I mean by this is writing good CSS means that your CSS will adapt to breakpoints, container sizes, different types of content, wrapping without too much extra code. Often what I'll see is people are simply just re-implementing the CSS for every single breakpoint. Or they have to duplicate their HTML because it's so different from one to another. Where writing media queries and container queries and all that stuff, that is fine. But your media queries should not have to write every single selector and reimplement absolutely everything. They should be flexible enough that when things change, when a component is in a space that has more width or height, or when a breakpoint is much smaller than larger, that the contents Will sort of just flex to them. You don't even have to write MIDI queries. Or if you are writing MIDI queries, you are writing container queries. A lot of that work is simply just updating variables. All right, at this breakpoint, I'm going down to three columns and I'm changing the font size to be something slightly different, right? Rather than having to just like write every single selector again, because I hate debugging that CSS where it's just like, oh, I want to edit this thing. Now I have to go through 11 different media queries, find all of the selectors. ([TimeĀ 0:07:12](https://share.snipd.com/snip/2d68396d-7e2a-4ffd-882f-df2b03229932))
- **Use Clamp-Based Fluid Typography**
- Use fluid type with clamp and ratios to make typography automatically scale across viewports or containers.
- Scott Tolinski describes his fluid-type generator that outputs clamp-based CSS variables and optionally uses container queries for consistent scale.
Transcript:
Scott Tolinski
So this is uh my this is a fluid hyphen type dot tolin dot ski this is a fluid type generator that i i built that everything is based off of math ratios and clamp. And if we look at this, you have this massive clamp function, where you have a 16 pixel based REM based clamping function that basically says, here's what the font size is preferred to Be at any given point. And then it's going to clamp down to a minimum for mobile. In this case, it's 14 pixels for mobile at the viewport size of 400 and the maximum of 20 pixels at desktop size. And that's for the base level font, like your your paragraph font, then at any given point, it's going to, if it can be its maximum based on the viewport size, in this case, it will be its Maximum. And at the minimum viewport size, it will be its minimum. But in between there, what it does is it picks the point in the middle of all of that, making it just totally fluid. And it's funny, I actually have this be container query based as well, too. So you can have this be container query based or viewport based based on how you how you choose just I have it based on a toggle. And then the way I also have it because this is all just the base level font size, you can choose the font scale as well. So like, what is that ratio between the two of these? Is that ratio is bigger than your font sizes at the larger end of the spectrum will be massive. Oh, that's cool. And if it's smaller, then that ratio ends up becoming smaller. And then you can tweak the baseline. Like if you want your smallest font size to be 16, you can have that be 16 instead of whatever. So check out this fluid type calculator, because at the end of the day, what it spits out for you is just man, a handful of lines of CSS that calculates all of this fluid type fluid font based On CSS variables. This is what I tend to use for all of my font sizing. ([TimeĀ 0:09:52](https://share.snipd.com/snip/fd9ec887-5443-4373-9510-3b8b01c388be))
- **Lock Core Values Into Variables**
- Convert repeated literal values into CSS variables for colors, fonts, shadows, and sizes.
- Scott Tolinski enforces variables and even forbids non-variable color values in his tooling to prevent drift and inconsistency.
Transcript:
Scott Tolinski
Things that make my CSS unmaintainable. I think one of the bigger things that we've talked a little bit about being systems and reusable and stuff. Yeah. But it's using values instead of variables. I know we'll talk about using variables in a bit here, but there are some things that just straight up should be variables in my mind all the time. Fonts, font sizes, colors, shadows, those types of things. When you define them one-off, especially with AI agents, can have next thing you know 40 different shadow types in your site and that's like a guaranteed way to make things not only unmaintainable But also non-cohesive in the way that it looks yeah so again we're talking i'm system guy right so i i really love variables for everything. And I forbid them certain properties to have individual values set on them that aren't a variable.
Wes Bos
Honestly, it's a great approach. Because as soon as you introduce eight different types of gray borders into your website, then you want to update it. Or want to add dark mode you know or you want to restyle your thing you're just hunting through the website for all these different use cases one kind of cool site is if you go on projectwallace.com And put put your site in they will show you all of the colors that are used on a website and it's kind of funny to like pipe in a i think you go to yeah you go to the design tokens and it'll show You all the colors that are used on a website if you type in several different websites you're going to hit upon some sites that like oh i see that they've used the blue but the blue has deviated Because somebody used a color picker here someone used rgba here versus somebody else used like HSL and it's slightly different. And then it's just, it's just deviated over time. And I'm very happy looking at syntax.fm where there is one yellow, two yellows.
Scott Tolinski
There's a variable system for a bunch of different yellows. We just don't use them. Yeah.
Wes Bos
Oh yeah. But I mean like the actual used yellow that we use for syntax is the same across the board and that way we actually have theming in the syntax site we can just change one variable and the
Scott Tolinski
Whole site will update yeah and in that same regard about changing one variable and having the site update i think uh one thing that uh is people using non-relative things like we have, Especially with colors nowadays, we have relative colors. Colors can be derived from other colors. Those types of systems, I think, often end up being the most maintainable because even if you're using an opacity, right? If you have like a transparent blue, people will define the blue and then they'll define the parent blue. Instead of having the blue and then deriving the transparent blue from that blue, you can get things out of sync that way.
Wes Bos
Let's say you have a purple, and then you want a slightly brighter purple to put as a border on a card. And then you want a slightly darker one for like a box shadow. Are you then sticking those in variables as well? Or are you just computer? Okay, good. So you're not just that's another thing AI loves to do is just compute them in place. Because I had this rule. Yeah, do not use any colors that are not in variables. And then what it did is like, okay, it just took the variable and then used relative colors. I'm using it, but I'm just computing what I want.
Scott Tolinski
Yeah, yeah, right. Yeah. I have, we talked about this in the last episode, Wes, about non-deterministic tools or deterministic tools. Styluent blocks me from that. Yeah. So it says, if it's a color value and it's not a custom property as the value, then error out. Yeah. Yeah. ([TimeĀ 0:14:11](https://share.snipd.com/snip/48583697-b9be-422a-a3dc-0d7ec08d8ba3))
- **Pick A System And Stick To It**
- Choosing any single CSS methodology matters less than picking one and sticking to it across the codebase.
- Wes Bos notes mixing approaches (utilities, components, atomic) leads to divergence and rot even if each is valid alone.
Transcript:
Wes Bos
Like when, what is not important is which system you pick. What is important is that you pick a system and you stick with it as close as you can. Because if you have a system and then you maybe use a different system here and then you mix and match, and then all of a sudden they just start to deviate from each other, that's where you Can run into trouble. So let's run through some of the more common approaches out there. So first one here is utility CSS. Most likely everybody is using Tailwind for this type of thing, where you have lots of classes that are associated with the different properties, different padding values. They're scoped a little bit smaller, meaning that you don't have access to the full gamut of values, which is often good because then you have like six different kinds of shadows or four Different kinds of spacing. But then, of course, you can use computed properties to actually inject your own version in there as well. And quite honestly, when I use Tailwind myself, I find myself using the computed values quite a bit because I find myself wanting a little slice in between one of these values or setting Up my own utility class, extending it a little bit. With a utility CSS approach, the reusability factor moves from select to like components that are reusable because like if you look at it there's a bazillion classes on on an element And then you find yourself going oh well like now i have two things on this page that need to look the same way am i just copy pasting it or sometimes people put the classes in like a variable And then they use that. That's an okay way to do it. But generally, the approach to this is instead of creating a selector somewhere with your CSS, you're simply just making a component that has all of those reusable components on. It's not my favorite approach. I certainly use it in, I don't know, I have three or four websites that use it. Lots of people absolutely love this approach.
Scott Tolinski
Yeah, I think people like it because it solves scoping for them, because they're using component flows, they're scoping their CSS, they're not going to worry about classes and leaking And those types of things. They like it because there's a system. But again, yeah, I find it's just like a shorthand, like a CSS shorthand. Yeah. And to me personally, yeah, that's why I don't personally use it. But I get why people like it. It makes it easier to have a system in place. You have Atomic CSS as being a separate thing in here as to utility CSS. I've always seen those as the same thing, but I don't have any strong feelings there. Do you see them as being different?
Wes Bos
Honestly, I don't either. Atomic CSS is the same idea, right? You have these small classes that simply do one thing. It changes the border color. It changes the width of the element. Uno CSS is very big in this regard. And it differs from Tailwind in that Tailwind is somewhat opinionated with what you can do with it, which is good because then it stops people from making a mess with everything, right? It scopes down what you can change on your selectors. With Atomic CSS, you can essentially build your own Tailwind. If you don't like the way that tailwind does something or if you don't like the fact that it doesn't have um the ability to do a certain pixel value you can you can go and build your own little Um thing with uno css and uh it's pretty cool it uses lightning css it's from antfu who's the creator of absolutely everything. And yeah, it's pretty cool. I'm looking at this and thinking, hmm, that'd be kind of cool to build my own. But I don't think I ever will. I prefer, we'll get to what I prefer, but I like a utility CSS as a sprinkle on top for common use cases for things that I want to do. You know, changing the font size, maybe margins here or there. But I don't like it for absolutely everything. I rather have a selector that I can reuse throughout my my code base. ([TimeĀ 0:18:23](https://share.snipd.com/snip/7ff48cd1-4972-409f-9631-473ae7d9b761))
- **Stylex Deduplicates At Build Time**
- Stylex (Facebook) deduplicates identical declarations at build time into tiny generated classes, so identical rules are shipped once.
- Wes Bos explains Stylex compacts repeated inline-style objects into single small classes across a large codebase.
Transcript:
Wes Bos
And then they have a build step that sort of takes that out. And the benefit to that is that you never write the same line of CSS twice. You never ship the same line of CSS twice. ([TimeĀ 0:24:11](https://share.snipd.com/snip/7baac7bb-cdf2-4cde-8ae1-0cd18a245fa0))
- **Scope Styles To The Component**
- Prefer component-scoped CSS for ergonomic authoring and to avoid naming gymnastics.
- Scott Tolinski praises Svelte/Vue scoped styles where you can write element selectors like div or strong inside the component without inventing global class names.
Transcript:
Wes Bos
Is component scoped CSS.
Scott Tolinski
This is how I prefer to work. Now, a Vue.js has component scoped CSS, Svelte has component scoped CSS. Back when I used CSS and JS like styled components, I wrote it in a way that was just scoped to my component. I adore this way of working, because you can target elements having class names and things like that. Your CSS becomes scoped to the component that you're authoring the CSS in. That way you can have a layer of base CSS that's global. You can have local CSS that doesn't leak anywhere. You don't have to come up with a thousand different classes for naming things. That's the best. It's the best. And it's only going to get better because we're getting native scoping in CSS. Therefore, you can not rely on your JavaScript framework to do that scoping. You can use the scope and then scope to a class or anything like that and then have that a div.
Wes Bos
Yes. Honestly, this is the one thing that I miss from when I'm not doing a Svelte project. And it's like, I just want to put a style tag in the same file as my component. And that's why people like Tailwind so much as well is because I go to the component that I'm working on and what it looks like, how it's styled, and often the actual like logic behind how It works is often in the same file. And it's beautiful. I absolutely love it. And like you can simply just write very bare selector like div or strong, and you don't have to like come up with a crazy name for it and i love that approach to this the one thing i don't like About this component scope css is when you have a component and you have some css in there and then you nest a component inside that's maybe two or three levels down and you say okay when Like let's say example, I have like a tag and it's inside of like a show ([TimeĀ 0:25:39](https://share.snipd.com/snip/1be5af9e-2569-43d6-af56-8899eb2bdfe7))
- **CSS Modules Trade Familiarity For Separation**
- CSS Modules give deterministic scoping by importing class names but separate files can feel fragmented compared to single-file components.
- Scott Tolinski uses CSS Modules happily but misses colocated styles inside component files.
Transcript:
Wes Bos
The way that this works is that you write your CSS in a separate.css file, and then you import the classes from that CSS file in your JavaScript. So maybe I'll have a footer, and then I will import the footer styles from that. And then what that turns into in your bundling step is like a class name that's associated with those scope styles. And then you can apply that class to your footer. And then if you have other elements, the one thing I don't like about it is if you have other elements, like let's say you have like a footer header, then you have to also import the footer Header styles from that one. You can't just simply just like use nesting. I get it because you can get in trouble with writing very deeply nested CSS and they much prefer you to say, no, let me just write this top level scope CSS that is not dependent on any of its Parents. But sometimes I just want to select the H3 inside of my footer without having to do all these jumping around, which is why I like the Svelte or the scope CSS approach so much more. I use CSS modules on my own personal website. I moved it over, I don't know, it'll be a year or two ago. And I've been, I've been like a 91% happy with it. And I think the only thing I don't like about it is that I have separate files. ([TimeĀ 0:29:11](https://share.snipd.com/snip/6893c264-c333-4f98-90b0-23ded40c41fc))
- **Ship A Useful Global Base**
- Start projects with a solid global base: reset/normalize, typography defaults, variables and color-scheme.
- Wes Bos and Scott Tolinski recommend Josh Comeauās reset and global variables so plain HTML elements look cohesive without classes.
Transcript:
Wes Bos
A good global CSS. We talk so much about scope your styles, make them tight, not repeatable. But then a lot of people who do that, they find themselves writing the same CSS over and over again, where in reality, you should have a good that includes some sort of normalize or reset. I really like the Josh Komu reset. I've been finding myself, I just copy paste that one every single time I started a project. And then that global base will have all of your defaults for your typography, has a lot of your colors and your variables that are being used. Often you should set your color scheme in there if you support light or dark or both. What other types of stuff is kind of globally done?
Scott Tolinski
Yeah, for me, I actually, Wes, I lean pretty hard on global CSS. So I've talked about this a number of times on our show. I lean so hard into global CSS that I wrote my own UI library graffiti, which has a lot of global CSS. So I do all my typography. But I also like at what point is your variable system? Your variable system feels like that is global CSS to me because that's usually included everywhere.
Wes Bos
I think so. If I'm defining a variable, I either define it globally because it needs to be throughout the entire thing. And those are generally like font sizes, colors, et cetera, or you do it at a component level, or you do, you do your component variables globally as defaults, and then your components Can simply just overwrite those variables.
Scott Tolinski
If you're asking me, a good global CSS should mean that with just elements and no classes, you should be able to have decent looking forms. Like your form should look like all of your form elements should look good. All of the base level elements should match your theme. If you were to just write html no classes it should look good okay now to me that's what a base level css should do and then everything else on top of there is like icing on the component cake There a ([TimeĀ 0:34:08](https://share.snipd.com/snip/bcc465fb-289a-4893-b5c4-7450352ee76d))
- **Use @property Inherits To Tame Variable Cascade**
- Use CSS variables dynamically and leverage @property inherits to control cascading of design tokens.
- Scott Tolinski highlights @property inherits false to stop variables like gap from unintentionally cascading into nested layouts.
Transcript:
Wes Bos
True. Like the gap one is a really good example, because if you have like a layout and then your entire site layout, like, oh, I want to change the gap on the layout, right? To 10 pixels. Without that, you just change the gap for everything else. And then you have to go back and set it back on all the other ones. So, and also for things like, like in CSS, there's many properties, like top, right, bottom, left. What are the things like padding, margin, borders? These things don't necessarily inherit. When you set a border on an element, all of its children's don't automatically inherit that value, right? Right. So you want to use this.
Scott Tolinski
And it allows you to build relative systems without having to worry about like cascading downwards effects. It's great. Yes. I put everything in variables, by the way, folks. I put everything, any type of thing. It's in variables. ([TimeĀ 0:40:39](https://share.snipd.com/snip/6b6b2b10-7e80-4356-9c43-5ea7258f82f4))
- **Order Styles With CSS Layers**
- Leverage CSS layers to order resets, defaults, animations and theme overrides instead of fighting specificity.
- Wes Bos notes layers make intentional cascade order explicit and are used by Tailwind 4 to avoid specificity hacks and !important.
Transcript:
Wes Bos
What this allows you to do is you can essentially take your CSS and put it into a specific layer. And this may be a reset layer, a defaults layer, an animations layer, and then you can control the order in which that CSS applied in the cascade. So if you have ever fought with the wrong CSS being applied over one or the other, it is so much easier to simply just stick them in a layer and explicitly tell the browser, apply them in This order. And Tailwind 4 uses CSS layers as well. So you're starting to see it quite a bit be applied to it. Especially also if you have like if you're writing some like customization right you may have a base theme and then you want to like add a bit of customization to overwrite some of those Values just make another layer tack that on the end and you know that that css is going to be applied over top of the existing one you don't have to start fighting with crazy specificity Selectors or like over you you like, oh, the dot heading didn't work. Let me just do H2 dot heading. Now that's more specific.
Scott Tolinski
Yep. Yeah, I think, you know, that's one of those things that people can get really into trouble with with this is like the moment they start applying overrides to the specificity tree to Gain some sort of control. It's the same classic issue that we've talked about going back to day one of CSS. If you throw an important on something, it is a sign that there's something wrong. Like an important is not something you should be using. And likewise, there are times when you start to write some really icky selectors and it's like, I don't know, I'm going to be doing this. It's a good sign that like a layer system layers could help you out in terms of like being able to actually gain control over that cascade, which I like CSS layers quite a bit. ([TimeĀ 0:41:39](https://share.snipd.com/snip/8d12f764-834e-4761-9c8e-aa6f4564be82))
- **Make CSS Rules Enforceable With Linters**
- Use linters and enforcement tools to make rules loud and automatic in PRs.
- Wes Bos recommends StyleLint or Biome CSS linting to block non-variable colors or other anti-patterns so tooling, not people, enforces style rules.
Transcript:
Scott Tolinski
StyleLint is a great project for this because you can set up your rules and those rules can become something that is loud. Hey, you just defined a color with a variable or with a value instead of a variable. Go make sure you don't do this. But it can also be extremely important when working in teams because we all know that everybody has a different idea of what CSS is. I've worked on teams where I'm just like, and not this team to be clear because Wes and CJ write good CSS, but I would just look at CSS and be like, I want to rewrite all of this. I don't like any of this, but now you don't have to be the bad guy. If you have a tool like this, it can be the bad guy for you like hey uh go fix this thing that you shouldn't have done this is bad yeah it gives you the error um biome also has a css linter in it
Wes Bos
Um there's an es lint plugin so like whatever your stack is there likely is already a plugin that will allow you to do these things they're not all exactly the same You may find that they Leave it to be desired. You want to switch over to one of them. But having a tool like this in your code base is kind of key to stop you from doing crazy stuff, especially when you have like dozens of people writing CSS in there. ([TimeĀ 0:46:19](https://share.snipd.com/snip/7f1f0f12-f9f2-4905-b930-6b7d0a8d9129))
- **Implement Variants By Overriding Tokens**
- Prefer changing a single variable for a variant instead of setting many CSS properties directly.
- Scott Tolinski explains variants should toggle a token used by borders, shadows and radii so an override is one variable change, not many properties.
Transcript:
Scott Tolinski
And I've said this a number of times in here, but I think oftentimes, even in variants and things like that, what we do is we default to going straight to CSS properties. Like one thing that we didn't touch on when we talked about variants and variables is that you could do something where a button is getting a background color of a variable. And then that variant could then change variable not the background color itself because that variable can then be used for other relative systems like you mentioned the border radius The box shadow those types of things and then therefore your variant code doesn't end up being a whole bunch of css property definitions it's like a one css custom property override
Wes Bos
And that's it that's that's so key because like sometimes i look at these things and it's like you just made variables for every single property that existed and now instead of setting Border uh 10 pic solid red i'm updating variables called border width, border color, border style. And it's like you just reinvented CSS and put them into variables. ([TimeĀ 0:47:58](https://share.snipd.com/snip/1db18c28-3da9-4b4e-957a-7120dc81eeed))