In this talk, Siddharth shares the challenges his team faced with optimising runtime css in js (styled-components) for performance. At GitHub, there are about 4000 React components that contain styles, Siddharth dives into the reasons for rethinking styling architecture, the challenges faced and lessons learned by migrating a big application.
Moving on From Runtime Css-In-Js at Scale
AI Generated Video Summary
1. Introduction to Styling Architecture
How I got there was, I was basically assigned this task. Which is, figure out the styling architecture for Primer React. What is Primer React? Primer React is the design system that powers GitHub. I'm Siddharth. I work on the design engineering team at GitHub. This was one big, it's funny that we call them epic, because it was kind of an epic task. And before GitHub, I used to work at all of these companies and for the last six years I've been basically working on design systems and component libraries. Fun fact, just like React, I also started my career exactly 10 years ago today, so thank you for that. I appreciate the balloon. Thank you.
All right, so, back to the moving slide. And why this was such a confusing task? Because when I was trying to figure out the styling architecture for Primer, it ended up being the styling architecture for a lot of React at GitHub. Any tiny change that I make would have cascading effects, get it? Cascading? Effects on all of GitHub. And that's a scary thing to do. So let me first talk about how did we land up in the space that we are.
2. Styling Architecture Evolution
You write your style like this. This is a button component. You put your colors, put your padding. You see a lot of hex codes, pixel values hard coded over here. This isn't as nice as you'd like it to be, but there was some tooling that existed.
The other thing that you got was variables. You could define these variables in a single place, which is huge for our design systems. And then in our components you could just use those variables. So when React came around in 2013, and then when we started using React, you would use it something like this. You have a button component and it uses the class name. And then you could use this button component in another type of page.
The nice thing that we just did, by defining a component in a different file, and then using it in a new file, is that we've created this relation. We've created a dependency tree between components. And that helps us create smarter bundles, eliminate dead code. You get a lot of tooling from that. But the same kind of tooling doesn't really exist for... Or didn't really exist for CSS.
The next interesting thing that gets to is even though you have variables in Sass, wow, I see your eyes, I'm sorry about that.
3. Dynamic Theming with Style Components
4. Theme Provider and Style Overrides
Some of you might already be using this style. You can wrap it with a theme provider. Adding additional colors can be challenging without a way to check if they are defined and applied. TypeScript helps catch errors and provides autocomplete. Customizing components with style components can be a lot of work and diverge from the original source. The SX prop or CSS prop allows for inline overrides, merging default styles with overrides. It provides type completion and offers features like nesting and linting. The success of this approach has led to thousands of presentational components on GitHub using the SX prop, causing some challenges.
Some of you might already be using this style. And you can wrap this with a theme provider that takes care of the rest. So something that's interesting is if I add this additional color, I kind of don't really have a way of knowing is this first, is this the right color, is this color defined? Second, is this color going to be applied? I kind of have to write this, render into the browser, see if it works. It probably is just black, so I right click inspect element, and that's when I get to know if it's the right color.
5. Optimizing Style Updates
6. Exploring Other Solutions and Native CSS Features
So I looked at other solutions first. So vanilla extract is a good example of this, where they ask you to write your styles in a CSS.TS extension. So you're writing the same object styles, but these can be compiled out, and you get the type safety, all of the nice things. I'm slightly short on time, so I'm going to skip the boring parts. This is boring.
7. CSS Migration and Intelligent Developer Tools
Tailwind does a really good job at it. Use the extension, they use a language server to understand what you're trying to do, look at your theme, and then suggest classes that actually match your own theme. That's very smart. Similarly, this is from Shopify's Polaris. So Polaris is Shopify's design system, and they have an extension which uses something similar to suggest CSS variables in CSS that are actually part of their system. So again, you can have very opinionated, you can see it's not just suggesting everything, it's suggesting shadow because it understands that you're trying to use it in box shadow. Which I think is very cool. So I tried to play around with this as well. It was surprisingly hard in the wrong ways and easy in the things that I thought were hard. And you can actually get very opinionated things, you can also get lint errors, where it can stop you from not using the variables that the design system team doesn't want, but actually use the ones that we do want you to use, even if the values are the same. So there's a lot of intelligence that you can bake into your developer tool. There is no good open source API, open source library to do this yet. So if any of you want to be popular, have that GitHub Star fame, this is a free idea for you. Finally, right, that should be it. Now we're using CSS, we have the tooling for it, this should definitely be enough. Almost. Remember I said we have 4,000 presentational components that use SX? So we kind of have to move all of them to CSS so that we can use this new tooling. And one way to do it would be, let's just do it one at a time, refactor it, let's ask teams to do it, deprioritize users, deprioritize bugs, and prioritize migration to CSS. Or we could use some tools. So remember this magic thing I showed earlier which you can do with a baby plugin and decided it would be hard to do it for so many different bundlers at the same time.
8. Developer Step and Naming Challenges
We could do it one at the developer step instead of the build step. TS Morph is a library that allows you to write transformers that can change your code based on the TypeScript AST. It's not an automated migration, but an automation-assisted migration. Should you drop CSS in JS? It depends on your specific circumstances and needs.
We could do it one at the developer step instead of the build step. So there's a very good library called TS Morph. It basically looks at the typescript AST and lets you write transformers that can change your code. So it's pretty smart in the types it gives you. So depending on the type of the variable, it could be a JSX opening element or a self-closing element, it can differentiate the two, you can look at the JSX attributes, and then you can get very smart with it.
So for example, the first one is pretty easy. If it's margin 10, then I know that I have to pick this up, put it in a CSS file, and put a class name here. But then because we're using an entire typescript compiler, we also have the option of following variables around. So for example, if in SX, you're passing base styles, you can actually trace these variables across multiple files and compile the value from there. Similarly, if you're calling a function, you can find the function definition and from the function definition, return a class instead of an object style. So you can get really clever, really wild with something like this. And that's how I'm hoping that we would migrate those 4,000.
Now, the one thing that computers are not so good at is naming things. Even though we have a lot of context in the file, you can use the file name, you can use a component name. A lot of the class names actually ended up looking like this, where there's a wrapper container stack and then a hash at the end of it. And that's the thing that computers are not good at. I guess they are, but I don't know enough AI to be good at it. So it's very, the code that is generated is not very maintainable in the long run. So the way I'm looking at it is, it's not an automated migration. It's an automation-assisted migration. And that's the way to do it. You run your command, it gives you some code, you review it like a developer would, and then if you like it, you might suggest some class name changes, and then you ship it. I will be raising a seed round because it says automation-assisted, and that qualifies as AI. You can meet me in the Q&A after with your goods.
Q&A on CSS Tools and Libraries
That's my talk. We have a few questions coming in on the slide. The first one was what are your thoughts on panda CSS? Never heard of it. No opinion. Sorry. What do you think of tailwind CSS? It's a very promising idea for us. There are certain aspects in component land where if you have a component library and you want to add overrides, it's very hard to do it with just class names. Because the order in which you give class names to an HTML element doesn't really matter. And that's where we started shying away from it. Because we were really on the path of we don't want to runtime solution.
That's kind of it. I'm going to do that again because I spent way too much time on this slide. And that's all. That's my talk.
All right. So we have a few questions coming in on the slide. People see you as an expert opinion on CSS and the different CSS tools and libraries. So I've got a bunch of different ones. So we're just going to go and you're going to give me some of your thoughts on them.
The first one was what are your thoughts on panda CSS? Never heard of it. No opinion. Sorry.
So maybe later on in the speaker Q&A they can come and show you and we can find out. Yeah, I'd love to. And then we've got another one. What do you think of tailwind CSS? This one you expected. Yeah. Yeah. Definitely. So we explored as part of all of like the exploration for this project, I explored tailwind as well. And less tailwind specifically, but more the idea of a utility style CSS which also has very good tooling. And it's a very promising idea for us. There are certain aspects in component land where if you have a component library and you want to add overrides, it's very hard to do it with just class names. Because the order in which you give class names to an HTML element doesn't really matter. What matters is the order that they show up in the CSS. And if you distribute it all across a big application, you can't really control the order. So you need some runtime CSS to make sure you're deduplicating these class names to make that happen. And that's where we started shying away from it. Because we were really on the path of we don't want to runtime solution.
CSS Tools and Libraries
Because we were really on the path of we don't want to runtime solution. I showed it to multiple developers. Some developers said I love it, and some developers said I hate it. This is not a tech conversation anymore. The cameramen are having a little CSS problem. Another question that has come up is what are your thoughts about CSS in JS major libraries like MUI, struggling within service side rendering? Should we come back to CSS modules? Or will libraries evolve? That's a very good question. There is a path for libraries to evolve and play really well with service side rendering, suspense. For libraries like MUI, I'm really curious of what they're thinking. If I could rewrite prime from scratch, I would probably go CSS first because we have a lot of really, really good CSS developers. Picking a stack based on the team members and their strengths is important. Critical CSS is quite a solved problem with CSS in JS. The idea of each component file having a CSS file tied to it is invaluable. The dependency tree is something that's going to stay for a while. I often get exposed to new developer tools that I really want.
Because we were really on the path of we don't want to runtime solution. The other thing was I showed it to multiple developers. And some developers said I love it, and some developers said I hate it. This is not a tech conversation anymore. So we did not go that way.
Awesome. I think the cameramen are having a little CSS problem. They're like, put the spot there. It's like a grid. Center and a div. But we get there in the end.
Now that makes sense, that makes sense. Now this next question, hindsight, is 2020 and if you could rewrite prime from scratch, which stack would you use? If I was starting today, with the team I have today, I would probably go CSS first because we have a lot of really, really good CSS developers that are really smart designers as well. So I would go there, and I feel like we're at a point where it wouldn't be so much of a tech decision, like because I compared all of these, made all the pros and cons chart and it kind of just came down to, this one looks prettier, so I would say skillset is how I decided today.
Nice. One thing I also love about watching developers give presentations is I often get exposed to new developer tools that I really really get jealous of and really want.
TypeScript Extension and Namespacing CSS
Someone asked about the TypeScript extension used in the slides, but it was just Keynote. When it comes to namespacing CSS, it depends on the context. Library components like Primer button are not namespaced, while application components with specific names like wrapper, stack, and container are namespaced to avoid conflicts. This allows us to have a conflict-free dependency tree using CSS modules.
And someone's asked, what's the TypeScript extension that you're using? The TypeScript extension for? I think it was in your slides, but your slides in the code editor in your slides were using the TypeScript. Oh, nothing. That's just Keynote. Good design. Good design. That's just like, I typed and I drew the rectangle to get the TypeScript thing. Yeah. Styled it so well. All right, we'll do this one as the last one.
What about namespacing CSS, something that styled components are delivering already? Yeah, I think it's interesting because it kind of depends. If it's a... We're kind of partitioning this half an hour implementation where the library components, we're not namespacing them. So you might get something like a Primer button. And that stays the same across the stack. But something like an application component where things like wrapper, stack, container are actual legit names, those were namespacing because we don't want any conflicts. So the same dependency tree that we get, we also get the ability to hash these and have them conflict-free with that. So something like CSS modules. Thank you, Sid. Everyone give him a round of applause.