On CSS Architectures, Frameworks and Tooling

(bright electronic music) - Hi.

I'm here to talk to you about some CSS.

So I call this talk The Cascade is Dead, mostly because I wanted to have something really controversial and make sure that people were mad at me when they saw the talk.

I did manage to get people mad at me when I tweeted this. And also there's, I mean you've seen this thing recently, people are using predictive text to make funny tweets on Twitter.

So I thought, hey, let's try that.

What is the cascade, if you type it into your phone, what do you get? (people laughing) Got stuck in a loop there.

I could use some Merlot about right now.

Yeah, Sunday's bad for me, too.

Hey, it's Jina.

He writes specs.

All right, so...

But you also can find some really strong opinions about the cascade on the Internet, and if you search for the cascade on Twitter, you'll find people saying very strongly worded tweets about what their CSS is good or bad.

And fundamentally, I feel like every time that I hear a talk about CSS, if it's about architecture, basically, what people are talking about is how do we manage this thing called the cascade? And this talk is no exception.

I'll be talking a lot about how I think we should manage the cascade.

So how did I end up here? So Mark tweeted out this wonderful blog post one day, and I decided to react on Twitter about cool, interesting things about CSS-in-JS, and what I like about it and some of the things I don't. And John saw me tweet-storming and asked me if I would come and share some of those thoughts, and I'll be real honest with you, this talk is not about that, but I'm gonna talk, drop a few of those thoughts in as I go here. But in that blog post, Mark makes some really interesting points about what CSS-in-JS is just kind of bringing to the table and how it's changing the game for doing front-end development right now.

So he talks about how scope styles are basically, you don't have to worry too much about it, if you're using the right class name or if there's conflicts and things like that, that there's ways of getting CSS delivered for your critical initial page load, some crazy new smart optimizations that you can do with route splitting and stuff like that.

He talks about how package management that you get with JavaScript, you get with this because it's in your JavaScript.

He also talks about the ability to do non-browser styling, which is an interesting use case for sure.

It's definitely a React thing in particular, I think. But like a few things that I've taken away from looking at CSS-in-JS and how it's affecting things is I really think that people like it because there is no meaningful cascade to writing your styles, and also this notion of like in an anonymous rule set, you don't have to come up with a name for the, as an intermediate abstraction between just you have this component and you have these styles, and you wanna connect the two, but now you have to create this name that means the two things, and it feels a little redundant, or it feels fake, and so just doing something anonymously and just saying here's some styles echoes over pretty well with a lot of devs and I get that because when we are programming in whatever, JavaScript, we can just pass an anonymous function.

We don't have to give it a name.

You can just do some stuff.

So that's a pretty useful feature.

And also the ability to determine styles for an element is really straightforward, like you're looking at an element, what style is it? Oh, it's right there.

So you don't go on these goose hunts to figure out, okay, which 10 selectors are mapping to my thing? And if you go in and you delete some code, styles go away. It's pretty great.

So but before I, I wanna back up a little bit. This talk might be a little bit less polished than I would like.

This is actually my first time giving a conference talk in two years.

I spoke in France and that was about two years ago, and I'm, I just had decided to take a break after that. So this is my first time and I wanted to come and share with you guys, so I really appreciate this opportunity.

So how did I end up even in the situation where I was ranting about CSS-in-JS, and what I think we should be doing maybe instead, and that's because my job is to think about CSS at work. I work at LinkedIn.

And about a year and change ago, the engineers of our main website came to me that the site they're about to launch, and they said, "Hey, our CSS build is a little slow." Well, Jesus Christ, there's 3.2 megabytes of CSS, yeah. What the hell? So I hadn't been working with them on their particular application, but at that point, I jumped in and I started helping, and they had selectors with crazy specificities, like five levels deep.

It was a disaster.

So the first thing that I did was just spend some time trying to stem, stop the bleeding, I guess, is the word. So, after about four months of working with them, we got our CSS down to a measly 1.5 megabytes, and that was a huge win, and they also managed to get enough architecture and process in place that as they were building new product, they weren't making things worse.

And at that point, I was like, okay, maybe I can back up and start thinking about this a bit more holistically in terms of technology, what can we do.

And so I had to take a bunch of constraints of a large corporation into account when thinking about our particular technological solutions to CSS. So we have a design system at LinkedIn.

We have a whole team of designers and a design system-specific team.

We have a design ops team.

It's actually the team that I'm on.

And we really wanted to come at this from a performance-first perspective.

We're really making a big bet on performance for our website right now, and just wanted to think about what does it take to make a high-performance style sheet framework. We also are huge users of Ember, so we needed to have a solution that works with that, which there is no CSS-in-JS solution for Ember. We could make one, I guess.

But also we wanted to have a solution that could empower our engineers to scale down to delivering websites to India.

We needed to be able to scale our human processes up to hundreds of engineers working on just one application. And we wanted to support server-side rendering for getting things up to the user super fast. We wanted to do all that critical inline styling that you get with CSS-in-JS, the code splitting, of course. But I personally didn't wanna make the trade-off of bad experience or performance, so I would love to give a great developer experience for engineers.

And also, specifically, I wanted to make sure that our design systems team had tools to address a lot of their longstanding problems that I'd been seeing them work with.

So, here's the bit where I talk about some things I don't like about CSS-in-JS. So I am kind of worried about CSS-in-JS because I feel like when we start putting our styles inside of components and stuff like that, it can feel a little scary maybe to people who are approaching the development world from the design side, and that might make them a bit more trepidatious about wanting to open those files up and make changes.

And one of the really cool experiences that I've seen from my work in Sass is seeing people who were designers and had literally no experience with programming start to like, "Oh, I can do some CSS," and then they're writing, but it's a Sass file and doesn't feel scary to them, and so then they make a function, and next thing you know, they're a programmer. They just slowly kind of evolved and followed this gradual progression and then they're returning values and creating systems, and so, I really love the ability to, if someone wants to go down that path, I'm not saying all designers have to go down that path, but if they want to take that path, I like having an accessible, seamless transition for that. But, also I don't like CSS-in-JS because I feel like it really naturally leads to JS-in-CSS. And I'm not a huge fan of that.

I feel like now you are required to know some JavaScript to do some CSS, and for the same reasons I mentioned, I also am pretty certain that that makes things less performant than it could be.

Which is theoretically worse than performance bid here because in reality, it might be totally fast enough for your site, it might be faster than a comparable CSS solution that isn't well thought out, so I'm not saying that you're gonna have a slow website if you're using CSS-in-JS, but I am fairly certain that for any given solution that's putting JavaScript inside your CSS that's rendering at runtime, I can create a CSS-only solution that's faster. So one of the other things we really were looking at, in terms of how could we speed up our CSS and make it small, is Atomic CSS, and the other functional or declaration-oriented CSS trademarks, and they really do produce high performance garbage. I am not a fan.

The styles are just terse and there's no ability to create abstractions per se, and I get that you're making good performance, but I feel like that particular trade-off of making performance over developer experience falls too close to the performance side.

But yeah, if you need something fast that seems good, like I understand you had to do something to make it work. Also the BEM naming system, who puts dashes and underscores in the same identifier? It's horrible.

So those are all my opinions.

I bring them to the table when I design things. And so we wanted to create a system where we were building CSS in a natural way that felt like it was targeted at our components. To have something that's, what I wanna say, just a better BEM, with names that make sense and don't require a bunch of duplication.

And then we wanted to figure out a way to optimise our shared declarations into shared rules. And after doing that optimization, what we wanted to do was a build-time transformation of our code, to make sure that we were taking maximal benefit of our optimizer. And so we didn't want to, CSS modules will basically ship a JavaScript object that says, "Here's your name, "and here's the name it should use instead." We wanted to skip that step.

And then we wanted to be able to do code splitting, and I'm not sure how many of you know this, but the identifiers that you're putting in your project make a huge difference to the overall gzip performance of your website.

So some of the measurements that I've seen are pretty impressive, so...

If you just take a random website with just using BEM or something like that, and then if you basically compress your identifiers, your class names and your IDs and things like that down to something pretty tiny, and then you can you save 50% of your over the wire costs, just by changing your identifiers.

There was a blog post recently, I have a link to it I can send out later on Twitter today, where they, just doing that was a huge performance win, and then on top of it, we make things worse actually as in a lot of the CSS-in-JS solutions that I've seen because they add a hash to their IDs to add uniqueness, as their scoping solution, but that hash is randomness. It's entropy that we're adding to our code, and for, if you know much about compression algorithms, entropy basically makes things bigger.

Can't be compressed as much.

So more than, it's like CSS-in-JS solutions that are preventing scoping, but they're actively bloating the size of our website when we ship our code.

Now who here said, "Bullshit, you can't merge selectors "and combine rule sets," when I said that.

Anyone? All right, well, you really shouldn't do that. And everybody would come to us since, well no, I worked in Sass for eight, nine years now. Can't you just put these two things together? It'd be so much better.

No.

It's very easy to break, oops.

More sneak peeks than I wanted.

So, this is a bit of a sneak peek about, more demos later, but I wanted to talk about the cascade and why it's dangerous to just willy-nilly reorder declarations.

So here what we have are some selectors and what we'd like to do is this simple operation of hey, this is colour red and this is colour red. Can't we just, make this a little bigger.

Can everybody see that? So we would like to merge those two declarations into a single declaration, not unlike you see here. And so this is what the optimizer has done. Here I see two common declarations, let put those together. And then, the apples and the strawberry elements can get rewritten and end up with a class name of a. Seems good.

Except what would you do if you had a frozen strawberry? See 'cause once you have a frozen strawberry, you can't merge this selector into this one because, of course, you would change the way the cascade resolves, you would end up with a blue strawberry instead of a red one and no one wants that. So you'll note here on the right hand side that as soon as I added that class to this element, the optimizer is like, "Oh, shit, sorry, "can't do that for you." And in fact, in our log message here, it'll say, Couldn't merge .strawberry { colour: red } at that line, with .apple because it conflicts with .frozen on that particular element.

So it fills you in on why it couldn't do a particular optimization. Okay, so this general idea of reordering declarations is basically, you have to understand where things are going to happen, and so, because there might be an element where the two classes are on the same element, then you have to be very careful.

If you have a CSS-only solution, you can't reorder things 'cause you don't know what's happening in your documents. So the solution to that, of course, is let's find out what's happening in our documents. So, we made a CSS framework called CSS Blocks. It's a blazing fast CSS for your design system and your app components.

And we made an optimizer that I'm calling OptiCSS. It's an analysis-driven optimizer for CSS and Markup. As far as I'm aware, there isn't anything like this in our industry.

Google has a thing that they put their website through for the homepage.

Maybe it's something like this, but they haven't released it.

So how did we do? Well like I said, where the website for LinkedIn.com is a little big right now.

Embarrassingly big, if you ask me.

1.6 megabytes, last I checked.

And after gzip, about 140kb.

It's a little, so we did a bunch of prototyping. We've made a prototyped version of the homepage for CSS Blocks, and we were able to just by compiling to vanilla BEM, and getting all the code splitting and whatnot there, we can get the website down to 60k, uncompressed.

When we toss in minimization, it gets better, of course. If we toss in the optimizer, though, you see it gets even better than the optimization, or than minification, and we put the two together, you get a 33% savings, or sorry, 66% savings on just the styles that we make for the homepage, but when you take that into account, that we are now able to code split our application, and only create the styles for the homepage, and so they're delivering all of LinkedIn.com's styles right off the bat, we're able to bring our CSS down to 5% after compression to the size that we're shipping to browsers right now.

It's a huge win.

Now of course, you get a, like CSS-in-JS, you could do the code splitting thing there, too. You get a big improvement off of what we have going right now.

So might seem a little too good to be true, like CSS and styles are complicated.

There's dynamic things happening and then of course, when those dynamic things happen, is there a huge runtime cost? What's going on there? What does it do to our template sizes? Does it, are we just squeezing the size of our CSS into our templates and making those bigger? Is it hard to use? Is it hard to adopt? The answer to the last question is absolutely, yes. I hope it's not hard to use.

So at this point, I'm gonna dive into the syntax and some of the features of this CSS framework. Then I'm gonna come back to the optimizer and show you some more cool stuff that it can do. So what is this thing called a block? Obviously, it's really important, 'cause I've called it CSS Blocks.

So this is not a new term, you've heard it before. In BEM, the B is blocks, and I'm using that same definition of the word block here.

Basically, a block file is a set of related styles of things that are meant to work together, and also they have interaction states.

In particular, there's four different concepts, or two concepts with kind of a specialisation of the root element.

So there's classes, which are CSS classes, and there's states, which are different modes or interaction states that an element that is assigned a class might be in.

And these are pretty standard concepts, I think, in CSS. This is like SMACSS or other things but kind of from the same general architecture. The block's root element is a little bit special, so it's called out here.

In particular, there is a special class called .root, and that class, you will assign it to the top level element of a particular component.

And that has to, all the other block elements need to be inside there.

From a conceptual perspective, way I think about it is classes are nouns and states are adjectives, so depending on if you want to, if you have a thing, or if you have a thing that is in some particular state or doing something, then you would use a, you would add a state.

So a really interesting distinction I'd like to draw here is...

A CSS file has a bunch of selectors in it, and a block file has a bunch of selectors in it. And it just looks like standard CSS, but you're actually defining an interface to a design concept when you create a block, and so you've got these selectors and they're mentioning these other bits, they're mentioning states, they're mentioning classes, and we can basically look at your selectors and say, "Oh, okay, yeah, you've got these classes "in your block, and you have these states, "and these states for those classes," and so when you're done with that, you basically, you have an, in an object-oriented sense, you have a class, in the object-oriented sense of the word, in that your classes are not unlike maybe methods on a class.

And so when you really start to think about the block itself as that abstract notion of the nouns and the adjectives associated with a design, then it becomes interesting to talk about the block in terms of those particular concepts and not in terms of the selectors that are operating on them.

You can really think of the selectors as being an implementation detail of private implementation of a particular interface, and so in that sense, actually, we allow you now to extend the block in an object-oriented sense of the term, you are inheriting all the classes and all of the states for those classes and all the states for that block into your block when you extend it.

You can also implement an interface, which is to say, hey, there's this sort of block here. It has those classes and states.

I would like to, I don't want any of selectors, I'm good, but I want to implement the exact same interface to make sure that if it changes, I get an error, so I know I have to add maybe new selectors for my implementation.

All right.

So let's look at some code.

This is an example of what a block might look like. The upper left hand corner here is what the CSS might be. This is just fairly straightforward form and... And then on the bottom, there's the markup that you would write.

And so I don't know how familiar you are with the concept of XML namespaces in HTML and CSS, but the syntax here of the bar is, I'm not inventing this, this is an actual CSS syntax for an XML namespace. So we use the state namespace to indicate attributes on an element that are in a particular namespace, so that we don't have to worry about colliding with the HTML namespace.

We don't have to use data-, crap like that. And then, of course, there's your class names and this all just looks like attribute selectors and class selectors and styles.

States, in particular, they can have a Boolean state, which is on or off, and they can have a state that has multiple exclusive values from each other. And then in your markup, this is hypothetical markup. Obviously, you're gonna have a React template or a Handlebars template or whatever else, so the syntax might vary by your template language, but in vanilla HTML, it would look basically like this. So we put the state attributes.

Now, in HTML, it's a colon because in, they couldn't agree on what their identifier should be, so HTML uses a colon for XML namespaces.

You've probably seen that with svg:.

All right, so that's what the code looks like. Hopefully, it doesn't seem too crazy.

All right, so we basically already settled that. In particular, what I call out is, when you're creating a state for a particular class, it's basically this compound selector of the class itself, and then the state attached to it. So on top of these concepts, we add a bunch of rules. The rules are basically BEM's rules for styling. We're now, as opposed to BEM being, yeah, you should probably follow these guidelines, we're gonna give you an error.

You gotta follow the rules.

So basically, the key selector for any selector needs to target one of these styles from a block, or it can target the pseudo-element of one of those files. And you can have any number of states and pseudo-classes on any particular class.

Obviously, you can put @-rules of any sort you want in there, and in general, combinators are basically forbidden in these files with the exception of combinating with states on the block route, which is basically a BEM convention. And we just like no !important.

You just can't.

It's not okay.

And actually, it's completely unnecessary, and I'm gonna explain why.

Remember, the cascade is dead, so you don't, if the cascade is dead, you don't need !important. The basic idea, I think, around these rules, is I think the goal is to help minimise coupling, and actually had an epiphany nine months ago or something, while I was giving a talk on BEM, that how many of you are familiar with the Law of Demeter, or maybe the principle of least knowledge? One (mumbles) people.

Okay, a few people.

So it's an object-oriented design principle. If you follow these rules of what arguments your functions should take and things like that, then you will encourage your code to have a very low amount of coupling, which, over time, will add to the maintainability of your software. And it's kind of annoying 'cause basically, it says you can't talk to, you can't use the dot of a variable, that you have to pass in both bits to a function instead of taking function in and then accessing its properties.

But it's a level of coupling that you're basically making assumptions about the internal structure of the thing when you do that, so by taking everything in as an argument in your C++ or whatever, you will have more easy, more maintainable code.

So actually I think BEM is just, we just naturally arrived at a similar principle for CSS by saying, "Let's stop coupling all these things." Stop combinating your classes with each other. Let's just assign this one specific place where we'll attach all that behaviour and everything else will be decoupled and it will actually be good for us. And so it might be hard to move to that architecture, but I think it, over time, does produce more maintainable code.

There's a few rules in terms of how you can use these things in your markup.

So you can't put classes from a block outside of the document subtree of an element, that once you put the root class there.

And you can't put two classes on an element of the, from the same block.

You can put them from different blocks, 'cause it doesn't really makes sense.

You're saying this thing is two different nouns, it doesn't, you can assign adjectives to a noun but you can't really say a thing is two different nouns. It's weird.

And so there's no, I don't actually have a theoretical reason for why you shouldn't do it.

I can still optimise it, I can still, but it just rubs me wrong, like I feel like you're, you've started to mix up concepts at that point and I really want to encourage the developers to have their concepts nice and straight in their own head. And so every time I've seen someone say, "I need to put two classes on here from the same block," I look at their code, I'm like, "You've not properly factored this." I'm still waiting for someone to give me a decent use case where I couldn't say, "Actually, I just think your concepts "are muddy here." So in a block file, of course, we have some special syntax, and I'm not gonna go super deep into all of this stuff. There's a lot of technical content in this. At some point, we're gonna have a bunch of documentation online and you can dig deep, but just give a overview, some of the things you can do, you can create a reference inside of a block to another block.

Seems good.

It's like an import statement.

Doesn't bring your styles in or anything like that. It just kinda gives you access to the names that it would have in there.

For the root selector, if you create a selector with .root, and it's just that with no combinators or any other things, then you can add a few extra properties.

There's an implements property which says this block is going to implement these other blocks' interfaces and please give me an error if I don't implement all of that.

You can do that inheritance thing that I talked about, and you're gonna sign a, basically, a preferred name to a block just for debugging purposes.

If you have a state for the root level of your block, you can declare it as global.

This is actually for, there's a lot of common patterns, I think, where your whole application will be inside of a state.

You wanna show the loading screen and a bunch of other bits of your site need to all collaborate, or you displayed a modal, and the whole app needs to be in the modal mode now, and so you would wanna create the sentence selector as kind of across a bunch of different places, and so we need a way to express that concept, and so, by declaring that you've got this global state for a particular block, now we will let you combinate with that.

You can also declare a class is the root of another block, and what that allows you to do is, there's a common pattern of components that have a nested structure and you would want to, the substructures are complex enough to become their own blocks, but it's really redundant to have to say the block and the class all the time together, so you just can link them at this point, and you not just link them to a particular root element, you can also assign a particular state of that root element. And there's a debug statement, so you can add a comment if you're like, what are the possible classes for this block? You could just output that to your CSS output or you can have it, debug it out to your console. Are you guys ready to kill the cascade? Okay, so I'm really excited about this idea. (beep) (people laughing) So we have these components.

We're building our CSS in little bits.

There is no general, overall ordering to your website, and this is the fundamental problem of the cascade, isn't it? We can't create a single top-to-bottom ordering of things, and so we end up getting into these weird situations where you have to break selectors apart or specificity wars and things like this.

So I just reject the notion that there is a global cascade for an application, and now we have to figure out what we're gonna do about that.

And because we're doing all of this analysis and we know for every class and every style that you're putting on to an element, you know what other styles are on that element, we can now start to get really smart about what's going on in terms of the cascade.

And so, if there are two different blocks that are setting the same properties, say, width, to a different value, we will give you an error. Let's say, "Hey, that block thing says "should be a hundred pixels.

"This block says it's 300, you figure it out. "We don't know.

"We're not gonna trust that your document order "and whatever else knows that magically "what the solution is.

"You need to tell us." And so that's what resolve does, so basically, you can create an override resolution where you say that for this property, whoops, for this property, then we want to resolve against the links class that's inside of that block, and because it comes first in the CSS order here, that means that we're kind of assigning the value of the links border to that property, but then our property comes immediately afterwards, and of course, that overrides it.

So this is an override resolution.

In terms of what that produces when we create our CSS, what we do is we create a selector that says specifically what is gonna get, what the combination of these particular selectors would be, but of course, we're resolving against this unique, this concept of a state or a class, and like I just said, that's not a selector. It's actually an, it's an object more than it's a set of selectors, and in fact, there are maybe several different selectors inside of your block, all targeting and talking about how to style that particular element. And so this doesn't just create one selector. It actually goes and finds all the selectors in the other block, and figures out how to resolve it against this particular selector.

And so I actually have query.

It's a selectors of selectors.

(mumbles) It's really cool.

Very meta.

The other type of resolution is a yield resolution, where you decide that if you are conflicting with a particular other block, that it should win, and so you can specify that it comes after you, and then of course, its value would win now because you've assigned it to the border after in its files. Same bit here, except now, instead of assigning border-bottom to none, we're assigning border-bottom to the shorthand. Seem good? We have 15 minutes, oh my god, I'm...

Do we have any questions so far? Shout it out if you've got anything.

Because you're smarter than me.

Right, so I'd like, I guess that we're going through it, we figured it out, we give these errors, and then we, you can tell us how it resolves and we'll take care of it.

So this is how we killed the cascade.

We now have this explicit resolution concept called resolve. There's also a slightly other construct.

You can actually resolve against multiple classes from a third class because you don't always have control over two classes that might conflict.

But I'm not gonna dig into that too much here. But also, because we're now starting to approach this, we're trying to approach this at a very property-specific level, we don't have to resolve things on a particular, not even for the whole selector.

We can say this property wins and this other property loses, even against the same set of styles.

And so we have really fine-grain control over how that conflict resolves.

You can also constrain what possible resolutions can be created, so when you're defining a value, you can say it is really important for the icons to be display: grid.

Constrain to my value and no one else can override it, and if you try to create an override resolution for that particular property, it will just say no. You can also constrain with ranges and specific enumerated values.

I think there's a lot more we can do with constraints but this is what we've got so far.

I mentioned inheritance.

I'm gonna kinda blow through this.

I really want to get to my demo.

So you can inherit these styles.

You basically pick up a whole bunch of stuff, but inheritance doesn't change your output per se. It, all your blocks basically always compile the same. Inheritance is a linkage between these nouns and these adjectives across different blocks and so now you've just got two classes or three classes that your particular element might mean. And so that's what turns into, in your markup. So there's your extends declaration. (mumbles) And so the way we implement inheritance is actually there's an intermediate phase of the compilation, where we just toss in, basically, resolution statements, where we're like, "Hey, this block has these other, "is related to these other blocks.

"We detected conflicts between properties, "but because it's an inheritance model, "we know it needs to override, "so we just take care of it for you." We have pre-process integration.

I don't know if you guys have heard of this thing called Sass.

So I thought we just support that.

We also, we actually integrate with any CSS pre-processor. Like if you just find the extension of the file and you can configure it how you wanna compile your files, and then it'll become CSS and then we can process it with PostCSS or something else like that, and then give vanilla CSS to CSS Blocks and it'll take it from there.

There's a lot of the blocks in text, so I really try to make it so that it was very usable with vanilla CSS, but a lot of the syntax really does clean up nicely with Sass.

So currently, we've implemented implementations of template integration for CSS Blocks with Handlebars and Glimmer and JSX, specifically with Preact, but definitely wanna get a full-fledged Ember implementation, Vue, and then maybe some other JSX implementations using slightly different syntaxes.

So this is what the syntax is like when you're writing in Handlebars.

Basically, that we create an association, there's a style sheet associated with every component, and you create a block reference inside of that, and then you can just use blocks inside of your styles. This looks like you're writing CSS except we have these namespace prefix classes, and that's how we get the scoping rate.

JSX doesn't really have the convention concepts that you see with Glimmer and Ember, so because of that, basically, we have to resort to an import-based option, not unlike CSS Modules.

So you can import styles and then what you get is an object back that has attributes and methods on it. The attributes are classes and the methods are states. So this is the font class inside of a, it's actually the block itself is for fonts, and then it has a font size declaration, and you can just pass in at what size you want your font to be.

This doesn't call any functions.

This is all statically run and everything, and it turns into a class name.

It's just the syntax.

And of course, you need past styles around. You've got, you're calling some function, you need to say, "Hey, here's some styles.

"Please look like this." And so this is where the inheritance concept really comes into play, 'cause we don't let you pass a single class around because we have all these requirements around static analysis, and so what we allow you to do is to pass entire blocks across component boundaries, and then what we can do is we do all these static analysis of that particular block, and the aspects of it that you're using, and then we can transfer that analysis to the other blocks that we detect that are getting passed in.

Dynamism, this is the really tricky bit.

So you've got your conditional expressions in your application that are deciding if you're gonna set some particular style from CSS Blocks or whatever, and those CSS Blocks, as I mentioned, with inheritance and whatnot, can result in multiple CSS classes being assigned, which then go into an optimizer, which will do horrible things to your CSS, so you don't have to, and out comes all these myriad, tiny CSS class names, and what we do is we create the Boolean expressions that basically say when these other classes are set or not set on your element, turn these things on and off, according to these Boolean expressions.

And that lets the optimizer data drive the rewrite process and create a generic logic around rewriting. So in Handlebars, we produce this.

It's gobbledygook.

Super meaningless in terms of output.

You're not gonna make sense of it really.

But these are opcodes, basically, that say what's going on in terms of the decision process, and here's the expression that you needed to run, and here's the class names we created, and it comes out on the other side.

So we handle dynamic stuff.

And actually, in many cases, the templates are smaller even with all that.

Just because of the compression around identifiers. Okay, OptiCSS, it's a markup analysis driven optimizer for CSS.

It handles generic CSS.

Nothing about the optimizer that we've built knows anything about CSS Blocks.

It's completely independent of that.

We think that CSS Blocks is a really good CSS trademark for building design systems, and it has the ability to be statically analysed.

It simplifies CSS to a level where we can make sense of it. But CSS Blocks isn't required to use the optimizer, and in fact, it's my hope that we can find other CSS frameworks to take advantage of the objects optimizer for their particular project, and I think that some of the CSS-in-JS solutions actually are more optimizable than even what CSS Blocks is. That's why we've been talking to Max Stoiber and a few other people from the CSS-in-JS community about what we can do to collaborate on this optimizer. Demo time.

Oh my god, seven minutes.

Give or take, right? You didn't want questions, did you? Five, four, three, two, one.

Let's make this full screen.

Okay, so a really complicated thing about CSS when you're optimising it are the various runtime aspects of your selectors.

You might have media queries and you might have combinators and things like that, and so we have to not break the cascade.

The number one rule that we have for the optimizer is don't break the cascade.

And I know that I said that the cascade was dead but it's not.

It's a fucking zombie and it's coming back to kill us. So we have to not break the cascade, and so actually, the testing tool that I've created for the optimizer is super rad.

You basically take a document and resolve all this CSS against it, and then we do horrible things to your CSS, and then we resolve your cascade again, and you have to have the exact same resolution, and if you don't, it's a bug.

There is no tool that did that so I had to make that. Good times.

So this is basically showing you that, where did my mouse go? There it is, hi.

Let me turn this off.

Just, we're gonna do nothing to this CSS.

It's just, it's gone through prettier at this point. It's all these different.

So what we'd like to do is merge declarations. And so, these random (mumbles).

Right, so scoped.

So here we had blue, we had several declarations for blue inside of the scope selector, and so scope still exists. We didn't take that selector, do anything to it, but we have rewritten within that selector, a class, and created a declaration, specifically for colour blue, and then we've gone to all the elements that were blue, so basic here inside of scoped, and now you'll see that it has the class d instead. And here's our logs of how we did when we did all of our work. (mumbles) Super informative, you can dig into that stuff and see if you could, expected some optimization to happen or whatever, why it didn't.

And then of course, here inside of our media query, that basically, every one of these declarations is the same. You didn't need four of them, but maybe you called some mixin over and over again, and you made a bunch of trash inside of your CSS. It's just gone now.

So we've really cleaned that up.

Okay.

So you've got some forms.

I don't know about you, but you might have used something like them, and you were always told you had to create a class to style your things, but you're like, "I just wanna style the HTML attributes," and with CSS Blocks, we still make you do that. We make you create classes and stuff, but the optimizer actually doesn't have that limitation. And so we have the ability to analyse it.

If you provide an analysis to the optimizer that says here's what's going on with all the form attributes, it can basically figure it out.

So now we're analysing the form and...

There we go.

Basically, there's only two declarations in there, float: left and background-color: grey, so we've narrowed this down to two tiny selectors. But what we've done here to our form elements is we've kept the form attributes around.

We only analyse those things.

We're not taking any of the attributes off, but we're able to assign the classes, a and b, to those attributes according to how they match the elements.

If you've got IDs and they've got various different specificities and you don't like specificity, that seems bad, you can get rid of that stuff by optimising IDs. So we'll turn on ID analysis and, whoops.

So now we keep the IDs around here in this situation because you probably are using them in your JavaScript. It's a good idea to keep those, but we were able to move the particular styles associated with those IDs down to class names, and by doing that, we're able to make it more likely that those classes will be able to be merged with other classes.

And specifically, we don't actually do any of these operations unless we can merge it. We're paying attention to what's going on.

So progressive enhancement.

So progressive enhancement is where you've got multiple CSS properties being set and they can do, you have to consider all of them in terms of what they accomplish.

So what we would like to do is merge border-top-color with this one up here.

But the problem is there's an element because well it's always a conflict because it's always the same element for these two things. And so what this demo is basically showing you is that we didn't merge border-top-color because it would break, because we did merge this border-top to, we actually deconstruct it into its shorthand, so we have super intelligent handling of shorthand properties.

We know what every shorthand means.

We parse it into its particular longhands, and then we do all the merging and whatnot. 21 seconds.

But yeah, so basically, if you could imagine moving things around, so if you wanted to, you'd get this message, "Hey, that doesn't merge," but you're like, "Actually, it's fine, I just need to move this down here." And then it can merge it, so hopefully, you get read the logs and figure out how you could make more optimal CSS.

Yeah, and there's a shorthand example, but that's pretty much it.

I don't wanna take up too much of your lunch. I do believe that what we've created here is what I think is the first viable, CSS-based alternative to CSS-in-JS, but if you want to, I don't care, you can put your CSS inside of your templates, whatever, it's fine.

Just make sure it's analyzable.

So I owe, yeah, this is a recap of the things that Mark and I both thought about CSS-in-JS, and these are the bits that I think we checked with this solution. And I think I have an idea for even how we could do anonymous rule sets but I need to think about it more.

Once we unlock this world of static analysis, I actually think there's room for a tonne of new tools and new abilities to think about things, so I'm imagining a bunch of capabilities around giving reports to our design systems teams that say, "Here's how all these classes are being used. "We noticed that this class and this class "are always used at the same time.

"Maybe you should just merge them "inside of your design system, that it's not very useful," or, "People are doing a tonne of overrides "of the width property here.

"Maybe that needs to be factored." And I think once we start being able to, as the design systems teams look at how our applications downstream are using it, there's a tonne of interesting analytics that we can use to learn from actual real world usage, and feed that back into our process.

Thank you.

(audience applauding) Real quick shout-outs.

LinkedIn pays me money, I like them, and Adam Miller is my coworker who has been my partner-in-crime on this project, and he's amazing, you should follow him on Twitter. We have a fucking website that launched today, css-blocks.com.

Go and check it out, so you can sign up for updates 'cause there's not much information there.

And follow me on Twitter, okay.

(bright electronic music) $%^&Chris Eppstein– On CSS Architectures, Frameworks and Tooling.srt#$%^

(bright electronic music) - Hi.

I'm here to talk to you about some CSS.

So I call this talk The Cascade is Dead, mostly because I wanted to have something really controversial and make sure that people were mad at me when they saw the talk.

I did manage to get people mad at me when I tweeted this. And also there's, I mean you've seen this thing recently, people are using predictive text to make funny tweets on Twitter.

So I thought, hey, let's try that.

What is the cascade, if you type it into your phone, what do you get? (people laughing) Got stuck in a loop there.

I could use some Merlot about right now.

Yeah, Sunday's bad for me, too.

Hey, it's Jina.

He writes specs.

All right, so...

But you also can find some really strong opinions about the cascade on the Internet, and if you search for the cascade on Twitter, you'll find people saying very strongly worded tweets about what their CSS is good or bad.

And fundamentally, I feel like every time that I hear a talk about CSS, if it's about architecture, basically, what people are talking about is how do we manage this thing called the cascade? And this talk is no exception.

I'll be talking a lot about how I think we should manage the cascade.

So how did I end up here? So Mark tweeted out this wonderful blog post one day, and I decided to react on Twitter about cool, interesting things about CSS-in-JS, and what I like about it and some of the things I don't. And John saw me tweet-storming and asked me if I would come and share some of those thoughts, and I'll be real honest with you, this talk is not about that, but I'm gonna talk, drop a few of those thoughts in as I go here. But in that blog post, Mark makes some really interesting points about what CSS-in-JS is just kind of bringing to the table and how it's changing the game for doing front-end development right now.

So he talks about how scope styles are basically, you don't have to worry too much about it, if you're using the right class name or if there's conflicts and things like that, that there's ways of getting CSS delivered for your critical initial page load, some crazy new smart optimizations that you can do with route splitting and stuff like that.

He talks about how package management that you get with JavaScript, you get with this because it's in your JavaScript.

He also talks about the ability to do non-browser styling, which is an interesting use case for sure.

It's definitely a React thing in particular, I think. But like a few things that I've taken away from looking at CSS-in-JS and how it's affecting things is I really think that people like it because there is no meaningful cascade to writing your styles, and also this notion of like in an anonymous rule set, you don't have to come up with a name for the, as an intermediate abstraction between just you have this component and you have these styles, and you wanna connect the two, but now you have to create this name that means the two things, and it feels a little redundant, or it feels fake, and so just doing something anonymously and just saying here's some styles echoes over pretty well with a lot of devs and I get that because when we are programming in whatever, JavaScript, we can just pass an anonymous function.

We don't have to give it a name.

You can just do some stuff.

So that's a pretty useful feature.

And also the ability to determine styles for an element is really straightforward, like you're looking at an element, what style is it? Oh, it's right there.

So you don't go on these goose hunts to figure out, okay, which 10 selectors are mapping to my thing? And if you go in and you delete some code, styles go away. It's pretty great.

So but before I, I wanna back up a little bit. This talk might be a little bit less polished than I would like.

This is actually my first time giving a conference talk in two years.

I spoke in France and that was about two years ago, and I'm, I just had decided to take a break after that. So this is my first time and I wanted to come and share with you guys, so I really appreciate this opportunity.

So how did I end up even in the situation where I was ranting about CSS-in-JS, and what I think we should be doing maybe instead, and that's because my job is to think about CSS at work. I work at LinkedIn.

And about a year and change ago, the engineers of our main website came to me that the site they're about to launch, and they said, "Hey, our CSS build is a little slow." Well, Jesus Christ, there's 3.2 megabytes of CSS, yeah. What the hell? So I hadn't been working with them on their particular application, but at that point, I jumped in and I started helping, and they had selectors with crazy specificities, like five levels deep.

It was a disaster.

So the first thing that I did was just spend some time trying to stem, stop the bleeding, I guess, is the word. So, after about four months of working with them, we got our CSS down to a measly 1.5 megabytes, and that was a huge win, and they also managed to get enough architecture and process in place that as they were building new product, they weren't making things worse.

And at that point, I was like, okay, maybe I can back up and start thinking about this a bit more holistically in terms of technology, what can we do.

And so I had to take a bunch of constraints of a large corporation into account when thinking about our particular technological solutions to CSS. So we have a design system at LinkedIn.

We have a whole team of designers and a design system-specific team.

We have a design ops team.

It's actually the team that I'm on.

And we really wanted to come at this from a performance-first perspective.

We're really making a big bet on performance for our website right now, and just wanted to think about what does it take to make a high-performance style sheet framework. We also are huge users of Ember, so we needed to have a solution that works with that, which there is no CSS-in-JS solution for Ember. We could make one, I guess.

But also we wanted to have a solution that could empower our engineers to scale down to delivering websites to India.

We needed to be able to scale our human processes up to hundreds of engineers working on just one application. And we wanted to support server-side rendering for getting things up to the user super fast. We wanted to do all that critical inline styling that you get with CSS-in-JS, the code splitting, of course. But I personally didn't wanna make the trade-off of bad experience or performance, so I would love to give a great developer experience for engineers.

And also, specifically, I wanted to make sure that our design systems team had tools to address a lot of their longstanding problems that I'd been seeing them work with.

So, here's the bit where I talk about some things I don't like about CSS-in-JS. So I am kind of worried about CSS-in-JS because I feel like when we start putting our styles inside of components and stuff like that, it can feel a little scary maybe to people who are approaching the development world from the design side, and that might make them a bit more trepidatious about wanting to open those files up and make changes.

And one of the really cool experiences that I've seen from my work in Sass is seeing people who were designers and had literally no experience with programming start to like, "Oh, I can do some CSS," and then they're writing, but it's a Sass file and doesn't feel scary to them, and so then they make a function, and next thing you know, they're a programmer. They just slowly kind of evolved and followed this gradual progression and then they're returning values and creating systems, and so, I really love the ability to, if someone wants to go down that path, I'm not saying all designers have to go down that path, but if they want to take that path, I like having an accessible, seamless transition for that. But, also I don't like CSS-in-JS because I feel like it really naturally leads to JS-in-CSS. And I'm not a huge fan of that.

I feel like now you are required to know some JavaScript to do some CSS, and for the same reasons I mentioned, I also am pretty certain that that makes things less performant than it could be.

Which is theoretically worse than performance bid here because in reality, it might be totally fast enough for your site, it might be faster than a comparable CSS solution that isn't well thought out, so I'm not saying that you're gonna have a slow website if you're using CSS-in-JS, but I am fairly certain that for any given solution that's putting JavaScript inside your CSS that's rendering at runtime, I can create a CSS-only solution that's faster. So one of the other things we really were looking at, in terms of how could we speed up our CSS and make it small, is Atomic CSS, and the other functional or declaration-oriented CSS trademarks, and they really do produce high performance garbage. I am not a fan.

The styles are just terse and there's no ability to create abstractions per se, and I get that you're making good performance, but I feel like that particular trade-off of making performance over developer experience falls too close to the performance side.

But yeah, if you need something fast that seems good, like I understand you had to do something to make it work. Also the BEM naming system, who puts dashes and underscores in the same identifier? It's horrible.

So those are all my opinions.

I bring them to the table when I design things. And so we wanted to create a system where we were building CSS in a natural way that felt like it was targeted at our components. To have something that's, what I wanna say, just a better BEM, with names that make sense and don't require a bunch of duplication.

And then we wanted to figure out a way to optimise our shared declarations into shared rules. And after doing that optimization, what we wanted to do was a build-time transformation of our code, to make sure that we were taking maximal benefit of our optimizer. And so we didn't want to, CSS modules will basically ship a JavaScript object that says, "Here's your name, "and here's the name it should use instead." We wanted to skip that step.

And then we wanted to be able to do code splitting, and I'm not sure how many of you know this, but the identifiers that you're putting in your project make a huge difference to the overall gzip performance of your website.

So some of the measurements that I've seen are pretty impressive, so...

If you just take a random website with just using BEM or something like that, and then if you basically compress your identifiers, your class names and your IDs and things like that down to something pretty tiny, and then you can you save 50% of your over the wire costs, just by changing your identifiers.

There was a blog post recently, I have a link to it I can send out later on Twitter today, where they, just doing that was a huge performance win, and then on top of it, we make things worse actually as in a lot of the CSS-in-JS solutions that I've seen because they add a hash to their IDs to add uniqueness, as their scoping solution, but that hash is randomness. It's entropy that we're adding to our code, and for, if you know much about compression algorithms, entropy basically makes things bigger.

Can't be compressed as much.

So more than, it's like CSS-in-JS solutions that are preventing scoping, but they're actively bloating the size of our website when we ship our code.

Now who here said, "Bullshit, you can't merge selectors "and combine rule sets," when I said that.

Anyone? All right, well, you really shouldn't do that. And everybody would come to us since, well no, I worked in Sass for eight, nine years now. Can't you just put these two things together? It'd be so much better.

No.

It's very easy to break, oops.

More sneak peeks than I wanted.

So, this is a bit of a sneak peek about, more demos later, but I wanted to talk about the cascade and why it's dangerous to just willy-nilly reorder declarations.

So here what we have are some selectors and what we'd like to do is this simple operation of hey, this is colour red and this is colour red. Can't we just, make this a little bigger.

Can everybody see that? So we would like to merge those two declarations into a single declaration, not unlike you see here. And so this is what the optimizer has done. Here I see two common declarations, let put those together. And then, the apples and the strawberry elements can get rewritten and end up with a class name of a. Seems good.

Except what would you do if you had a frozen strawberry? See 'cause once you have a frozen strawberry, you can't merge this selector into this one because, of course, you would change the way the cascade resolves, you would end up with a blue strawberry instead of a red one and no one wants that. So you'll note here on the right hand side that as soon as I added that class to this element, the optimizer is like, "Oh, shit, sorry, "can't do that for you." And in fact, in our log message here, it'll say, Couldn't merge .strawberry { colour: red } at that line, with .apple because it conflicts with .frozen on that particular element.

So it fills you in on why it couldn't do a particular optimization. Okay, so this general idea of reordering declarations is basically, you have to understand where things are going to happen, and so, because there might be an element where the two classes are on the same element, then you have to be very careful.

If you have a CSS-only solution, you can't reorder things 'cause you don't know what's happening in your documents. So the solution to that, of course, is let's find out what's happening in our documents. So, we made a CSS framework called CSS Blocks. It's a blazing fast CSS for your design system and your app components.

And we made an optimizer that I'm calling OptiCSS. It's an analysis-driven optimizer for CSS and Markup. As far as I'm aware, there isn't anything like this in our industry.

Google has a thing that they put their website through for the homepage.

Maybe it's something like this, but they haven't released it.

So how did we do? Well like I said, where the website for LinkedIn.com is a little big right now.

Embarrassingly big, if you ask me.

1.6 megabytes, last I checked.

And after gzip, about 140kb.

It's a little, so we did a bunch of prototyping. We've made a prototyped version of the homepage for CSS Blocks, and we were able to just by compiling to vanilla BEM, and getting all the code splitting and whatnot there, we can get the website down to 60k, uncompressed.

When we toss in minimization, it gets better, of course. If we toss in the optimizer, though, you see it gets even better than the optimization, or than minification, and we put the two together, you get a 33% savings, or sorry, 66% savings on just the styles that we make for the homepage, but when you take that into account, that we are now able to code split our application, and only create the styles for the homepage, and so they're delivering all of LinkedIn.com's styles right off the bat, we're able to bring our CSS down to 5% after compression to the size that we're shipping to browsers right now.

It's a huge win.

Now of course, you get a, like CSS-in-JS, you could do the code splitting thing there, too. You get a big improvement off of what we have going right now.

So might seem a little too good to be true, like CSS and styles are complicated.

There's dynamic things happening and then of course, when those dynamic things happen, is there a huge runtime cost? What's going on there? What does it do to our template sizes? Does it, are we just squeezing the size of our CSS into our templates and making those bigger? Is it hard to use? Is it hard to adopt? The answer to the last question is absolutely, yes. I hope it's not hard to use.

So at this point, I'm gonna dive into the syntax and some of the features of this CSS framework. Then I'm gonna come back to the optimizer and show you some more cool stuff that it can do. So what is this thing called a block? Obviously, it's really important, 'cause I've called it CSS Blocks.

So this is not a new term, you've heard it before. In BEM, the B is blocks, and I'm using that same definition of the word block here.

Basically, a block file is a set of related styles of things that are meant to work together, and also they have interaction states.

In particular, there's four different concepts, or two concepts with kind of a specialisation of the root element.

So there's classes, which are CSS classes, and there's states, which are different modes or interaction states that an element that is assigned a class might be in.

And these are pretty standard concepts, I think, in CSS. This is like SMACSS or other things but kind of from the same general architecture. The block's root element is a little bit special, so it's called out here.

In particular, there is a special class called .root, and that class, you will assign it to the top level element of a particular component.

And that has to, all the other block elements need to be inside there.

From a conceptual perspective, way I think about it is classes are nouns and states are adjectives, so depending on if you want to, if you have a thing, or if you have a thing that is in some particular state or doing something, then you would use a, you would add a state.

So a really interesting distinction I'd like to draw here is...

A CSS file has a bunch of selectors in it, and a block file has a bunch of selectors in it. And it just looks like standard CSS, but you're actually defining an interface to a design concept when you create a block, and so you've got these selectors and they're mentioning these other bits, they're mentioning states, they're mentioning classes, and we can basically look at your selectors and say, "Oh, okay, yeah, you've got these classes "in your block, and you have these states, "and these states for those classes," and so when you're done with that, you basically, you have an, in an object-oriented sense, you have a class, in the object-oriented sense of the word, in that your classes are not unlike maybe methods on a class.

And so when you really start to think about the block itself as that abstract notion of the nouns and the adjectives associated with a design, then it becomes interesting to talk about the block in terms of those particular concepts and not in terms of the selectors that are operating on them.

You can really think of the selectors as being an implementation detail of private implementation of a particular interface, and so in that sense, actually, we allow you now to extend the block in an object-oriented sense of the term, you are inheriting all the classes and all of the states for those classes and all the states for that block into your block when you extend it.

You can also implement an interface, which is to say, hey, there's this sort of block here. It has those classes and states.

I would like to, I don't want any of selectors, I'm good, but I want to implement the exact same interface to make sure that if it changes, I get an error, so I know I have to add maybe new selectors for my implementation.

All right.

So let's look at some code.

This is an example of what a block might look like. The upper left hand corner here is what the CSS might be. This is just fairly straightforward form and... And then on the bottom, there's the markup that you would write.

And so I don't know how familiar you are with the concept of XML namespaces in HTML and CSS, but the syntax here of the bar is, I'm not inventing this, this is an actual CSS syntax for an XML namespace. So we use the state namespace to indicate attributes on an element that are in a particular namespace, so that we don't have to worry about colliding with the HTML namespace.

We don't have to use data-, crap like that. And then, of course, there's your class names and this all just looks like attribute selectors and class selectors and styles.

States, in particular, they can have a Boolean state, which is on or off, and they can have a state that has multiple exclusive values from each other. And then in your markup, this is hypothetical markup. Obviously, you're gonna have a React template or a Handlebars template or whatever else, so the syntax might vary by your template language, but in vanilla HTML, it would look basically like this. So we put the state attributes.

Now, in HTML, it's a colon because in, they couldn't agree on what their identifier should be, so HTML uses a colon for XML namespaces.

You've probably seen that with svg:.

All right, so that's what the code looks like. Hopefully, it doesn't seem too crazy.

All right, so we basically already settled that. In particular, what I call out is, when you're creating a state for a particular class, it's basically this compound selector of the class itself, and then the state attached to it. So on top of these concepts, we add a bunch of rules. The rules are basically BEM's rules for styling. We're now, as opposed to BEM being, yeah, you should probably follow these guidelines, we're gonna give you an error.

You gotta follow the rules.

So basically, the key selector for any selector needs to target one of these styles from a block, or it can target the pseudo-element of one of those files. And you can have any number of states and pseudo-classes on any particular class.

Obviously, you can put @-rules of any sort you want in there, and in general, combinators are basically forbidden in these files with the exception of combinating with states on the block route, which is basically a BEM convention. And we just like no !important.

You just can't.

It's not okay.

And actually, it's completely unnecessary, and I'm gonna explain why.

Remember, the cascade is dead, so you don't, if the cascade is dead, you don't need !important. The basic idea, I think, around these rules, is I think the goal is to help minimise coupling, and actually had an epiphany nine months ago or something, while I was giving a talk on BEM, that how many of you are familiar with the Law of Demeter, or maybe the principle of least knowledge? One (mumbles) people.

Okay, a few people.

So it's an object-oriented design principle. If you follow these rules of what arguments your functions should take and things like that, then you will encourage your code to have a very low amount of coupling, which, over time, will add to the maintainability of your software. And it's kind of annoying 'cause basically, it says you can't talk to, you can't use the dot of a variable, that you have to pass in both bits to a function instead of taking function in and then accessing its properties.

But it's a level of coupling that you're basically making assumptions about the internal structure of the thing when you do that, so by taking everything in as an argument in your C++ or whatever, you will have more easy, more maintainable code.

So actually I think BEM is just, we just naturally arrived at a similar principle for CSS by saying, "Let's stop coupling all these things." Stop combinating your classes with each other. Let's just assign this one specific place where we'll attach all that behaviour and everything else will be decoupled and it will actually be good for us. And so it might be hard to move to that architecture, but I think it, over time, does produce more maintainable code.

There's a few rules in terms of how you can use these things in your markup.

So you can't put classes from a block outside of the document subtree of an element, that once you put the root class there.

And you can't put two classes on an element of the, from the same block.

You can put them from different blocks, 'cause it doesn't really makes sense.

You're saying this thing is two different nouns, it doesn't, you can assign adjectives to a noun but you can't really say a thing is two different nouns. It's weird.

And so there's no, I don't actually have a theoretical reason for why you shouldn't do it.

I can still optimise it, I can still, but it just rubs me wrong, like I feel like you're, you've started to mix up concepts at that point and I really want to encourage the developers to have their concepts nice and straight in their own head. And so every time I've seen someone say, "I need to put two classes on here from the same block," I look at their code, I'm like, "You've not properly factored this." I'm still waiting for someone to give me a decent use case where I couldn't say, "Actually, I just think your concepts "are muddy here." So in a block file, of course, we have some special syntax, and I'm not gonna go super deep into all of this stuff. There's a lot of technical content in this. At some point, we're gonna have a bunch of documentation online and you can dig deep, but just give a overview, some of the things you can do, you can create a reference inside of a block to another block.

Seems good.

It's like an import statement.

Doesn't bring your styles in or anything like that. It just kinda gives you access to the names that it would have in there.

For the root selector, if you create a selector with .root, and it's just that with no combinators or any other things, then you can add a few extra properties.

There's an implements property which says this block is going to implement these other blocks' interfaces and please give me an error if I don't implement all of that.

You can do that inheritance thing that I talked about, and you're gonna sign a, basically, a preferred name to a block just for debugging purposes.

If you have a state for the root level of your block, you can declare it as global.

This is actually for, there's a lot of common patterns, I think, where your whole application will be inside of a state.

You wanna show the loading screen and a bunch of other bits of your site need to all collaborate, or you displayed a modal, and the whole app needs to be in the modal mode now, and so you would wanna create the sentence selector as kind of across a bunch of different places, and so we need a way to express that concept, and so, by declaring that you've got this global state for a particular block, now we will let you combinate with that.

You can also declare a class is the root of another block, and what that allows you to do is, there's a common pattern of components that have a nested structure and you would want to, the substructures are complex enough to become their own blocks, but it's really redundant to have to say the block and the class all the time together, so you just can link them at this point, and you not just link them to a particular root element, you can also assign a particular state of that root element. And there's a debug statement, so you can add a comment if you're like, what are the possible classes for this block? You could just output that to your CSS output or you can have it, debug it out to your console. Are you guys ready to kill the cascade? Okay, so I'm really excited about this idea. (beep) (people laughing) So we have these components.

We're building our CSS in little bits.

There is no general, overall ordering to your website, and this is the fundamental problem of the cascade, isn't it? We can't create a single top-to-bottom ordering of things, and so we end up getting into these weird situations where you have to break selectors apart or specificity wars and things like this.

So I just reject the notion that there is a global cascade for an application, and now we have to figure out what we're gonna do about that.

And because we're doing all of this analysis and we know for every class and every style that you're putting on to an element, you know what other styles are on that element, we can now start to get really smart about what's going on in terms of the cascade.

And so, if there are two different blocks that are setting the same properties, say, width, to a different value, we will give you an error. Let's say, "Hey, that block thing says "should be a hundred pixels.

"This block says it's 300, you figure it out. "We don't know.

"We're not gonna trust that your document order "and whatever else knows that magically "what the solution is.

"You need to tell us." And so that's what resolve does, so basically, you can create an override resolution where you say that for this property, whoops, for this property, then we want to resolve against the links class that's inside of that block, and because it comes first in the CSS order here, that means that we're kind of assigning the value of the links border to that property, but then our property comes immediately afterwards, and of course, that overrides it.

So this is an override resolution.

In terms of what that produces when we create our CSS, what we do is we create a selector that says specifically what is gonna get, what the combination of these particular selectors would be, but of course, we're resolving against this unique, this concept of a state or a class, and like I just said, that's not a selector. It's actually an, it's an object more than it's a set of selectors, and in fact, there are maybe several different selectors inside of your block, all targeting and talking about how to style that particular element. And so this doesn't just create one selector. It actually goes and finds all the selectors in the other block, and figures out how to resolve it against this particular selector.

And so I actually have query.

It's a selectors of selectors.

(mumbles) It's really cool.

Very meta.

The other type of resolution is a yield resolution, where you decide that if you are conflicting with a particular other block, that it should win, and so you can specify that it comes after you, and then of course, its value would win now because you've assigned it to the border after in its files. Same bit here, except now, instead of assigning border-bottom to none, we're assigning border-bottom to the shorthand. Seem good? We have 15 minutes, oh my god, I'm...

Do we have any questions so far? Shout it out if you've got anything.

Because you're smarter than me.

Right, so I'd like, I guess that we're going through it, we figured it out, we give these errors, and then we, you can tell us how it resolves and we'll take care of it.

So this is how we killed the cascade.

We now have this explicit resolution concept called resolve. There's also a slightly other construct.

You can actually resolve against multiple classes from a third class because you don't always have control over two classes that might conflict.

But I'm not gonna dig into that too much here. But also, because we're now starting to approach this, we're trying to approach this at a very property-specific level, we don't have to resolve things on a particular, not even for the whole selector.

We can say this property wins and this other property loses, even against the same set of styles.

And so we have really fine-grain control over how that conflict resolves.

You can also constrain what possible resolutions can be created, so when you're defining a value, you can say it is really important for the icons to be display: grid.

Constrain to my value and no one else can override it, and if you try to create an override resolution for that particular property, it will just say no. You can also constrain with ranges and specific enumerated values.

I think there's a lot more we can do with constraints but this is what we've got so far.

I mentioned inheritance.

I'm gonna kinda blow through this.

I really want to get to my demo.

So you can inherit these styles.

You basically pick up a whole bunch of stuff, but inheritance doesn't change your output per se. It, all your blocks basically always compile the same. Inheritance is a linkage between these nouns and these adjectives across different blocks and so now you've just got two classes or three classes that your particular element might mean. And so that's what turns into, in your markup. So there's your extends declaration. (mumbles) And so the way we implement inheritance is actually there's an intermediate phase of the compilation, where we just toss in, basically, resolution statements, where we're like, "Hey, this block has these other, "is related to these other blocks.

"We detected conflicts between properties, "but because it's an inheritance model, "we know it needs to override, "so we just take care of it for you." We have pre-process integration.

I don't know if you guys have heard of this thing called Sass.

So I thought we just support that.

We also, we actually integrate with any CSS pre-processor. Like if you just find the extension of the file and you can configure it how you wanna compile your files, and then it'll become CSS and then we can process it with PostCSS or something else like that, and then give vanilla CSS to CSS Blocks and it'll take it from there.

There's a lot of the blocks in text, so I really try to make it so that it was very usable with vanilla CSS, but a lot of the syntax really does clean up nicely with Sass.

So currently, we've implemented implementations of template integration for CSS Blocks with Handlebars and Glimmer and JSX, specifically with Preact, but definitely wanna get a full-fledged Ember implementation, Vue, and then maybe some other JSX implementations using slightly different syntaxes.

So this is what the syntax is like when you're writing in Handlebars.

Basically, that we create an association, there's a style sheet associated with every component, and you create a block reference inside of that, and then you can just use blocks inside of your styles. This looks like you're writing CSS except we have these namespace prefix classes, and that's how we get the scoping rate.

JSX doesn't really have the convention concepts that you see with Glimmer and Ember, so because of that, basically, we have to resort to an import-based option, not unlike CSS Modules.

So you can import styles and then what you get is an object back that has attributes and methods on it. The attributes are classes and the methods are states. So this is the font class inside of a, it's actually the block itself is for fonts, and then it has a font size declaration, and you can just pass in at what size you want your font to be.

This doesn't call any functions.

This is all statically run and everything, and it turns into a class name.

It's just the syntax.

And of course, you need past styles around. You've got, you're calling some function, you need to say, "Hey, here's some styles.

"Please look like this." And so this is where the inheritance concept really comes into play, 'cause we don't let you pass a single class around because we have all these requirements around static analysis, and so what we allow you to do is to pass entire blocks across component boundaries, and then what we can do is we do all these static analysis of that particular block, and the aspects of it that you're using, and then we can transfer that analysis to the other blocks that we detect that are getting passed in.

Dynamism, this is the really tricky bit.

So you've got your conditional expressions in your application that are deciding if you're gonna set some particular style from CSS Blocks or whatever, and those CSS Blocks, as I mentioned, with inheritance and whatnot, can result in multiple CSS classes being assigned, which then go into an optimizer, which will do horrible things to your CSS, so you don't have to, and out comes all these myriad, tiny CSS class names, and what we do is we create the Boolean expressions that basically say when these other classes are set or not set on your element, turn these things on and off, according to these Boolean expressions.

And that lets the optimizer data drive the rewrite process and create a generic logic around rewriting. So in Handlebars, we produce this.

It's gobbledygook.

Super meaningless in terms of output.

You're not gonna make sense of it really.

But these are opcodes, basically, that say what's going on in terms of the decision process, and here's the expression that you needed to run, and here's the class names we created, and it comes out on the other side.

So we handle dynamic stuff.

And actually, in many cases, the templates are smaller even with all that.

Just because of the compression around identifiers. Okay, OptiCSS, it's a markup analysis driven optimizer for CSS.

It handles generic CSS.

Nothing about the optimizer that we've built knows anything about CSS Blocks.

It's completely independent of that.

We think that CSS Blocks is a really good CSS trademark for building design systems, and it has the ability to be statically analysed.

It simplifies CSS to a level where we can make sense of it. But CSS Blocks isn't required to use the optimizer, and in fact, it's my hope that we can find other CSS frameworks to take advantage of the objects optimizer for their particular project, and I think that some of the CSS-in-JS solutions actually are more optimizable than even what CSS Blocks is. That's why we've been talking to Max Stoiber and a few other people from the CSS-in-JS community about what we can do to collaborate on this optimizer. Demo time.

Oh my god, seven minutes.

Give or take, right? You didn't want questions, did you? Five, four, three, two, one.

Let's make this full screen.

Okay, so a really complicated thing about CSS when you're optimising it are the various runtime aspects of your selectors.

You might have media queries and you might have combinators and things like that, and so we have to not break the cascade.

The number one rule that we have for the optimizer is don't break the cascade.

And I know that I said that the cascade was dead but it's not.

It's a fucking zombie and it's coming back to kill us. So we have to not break the cascade, and so actually, the testing tool that I've created for the optimizer is super rad.

You basically take a document and resolve all this CSS against it, and then we do horrible things to your CSS, and then we resolve your cascade again, and you have to have the exact same resolution, and if you don't, it's a bug.

There is no tool that did that so I had to make that. Good times.

So this is basically showing you that, where did my mouse go? There it is, hi.

Let me turn this off.

Just, we're gonna do nothing to this CSS.

It's just, it's gone through prettier at this point. It's all these different.

So what we'd like to do is merge declarations. And so, these random (mumbles).

Right, so scoped.

So here we had blue, we had several declarations for blue inside of the scope selector, and so scope still exists. We didn't take that selector, do anything to it, but we have rewritten within that selector, a class, and created a declaration, specifically for colour blue, and then we've gone to all the elements that were blue, so basic here inside of scoped, and now you'll see that it has the class d instead. And here's our logs of how we did when we did all of our work. (mumbles) Super informative, you can dig into that stuff and see if you could, expected some optimization to happen or whatever, why it didn't.

And then of course, here inside of our media query, that basically, every one of these declarations is the same. You didn't need four of them, but maybe you called some mixin over and over again, and you made a bunch of trash inside of your CSS. It's just gone now.

So we've really cleaned that up.

Okay.

So you've got some forms.

I don't know about you, but you might have used something like them, and you were always told you had to create a class to style your things, but you're like, "I just wanna style the HTML attributes," and with CSS Blocks, we still make you do that. We make you create classes and stuff, but the optimizer actually doesn't have that limitation. And so we have the ability to analyse it.

If you provide an analysis to the optimizer that says here's what's going on with all the form attributes, it can basically figure it out.

So now we're analysing the form and...

There we go.

Basically, there's only two declarations in there, float: left and background-color: grey, so we've narrowed this down to two tiny selectors. But what we've done here to our form elements is we've kept the form attributes around.

We only analyse those things.

We're not taking any of the attributes off, but we're able to assign the classes, a and b, to those attributes according to how they match the elements.

If you've got IDs and they've got various different specificities and you don't like specificity, that seems bad, you can get rid of that stuff by optimising IDs. So we'll turn on ID analysis and, whoops.

So now we keep the IDs around here in this situation because you probably are using them in your JavaScript. It's a good idea to keep those, but we were able to move the particular styles associated with those IDs down to class names, and by doing that, we're able to make it more likely that those classes will be able to be merged with other classes.

And specifically, we don't actually do any of these operations unless we can merge it. We're paying attention to what's going on.

So progressive enhancement.

So progressive enhancement is where you've got multiple CSS properties being set and they can do, you have to consider all of them in terms of what they accomplish.

So what we would like to do is merge border-top-color with this one up here.

But the problem is there's an element because well it's always a conflict because it's always the same element for these two things. And so what this demo is basically showing you is that we didn't merge border-top-color because it would break, because we did merge this border-top to, we actually deconstruct it into its shorthand, so we have super intelligent handling of shorthand properties.

We know what every shorthand means.

We parse it into its particular longhands, and then we do all the merging and whatnot. 21 seconds.

But yeah, so basically, if you could imagine moving things around, so if you wanted to, you'd get this message, "Hey, that doesn't merge," but you're like, "Actually, it's fine, I just need to move this down here." And then it can merge it, so hopefully, you get read the logs and figure out how you could make more optimal CSS.

Yeah, and there's a shorthand example, but that's pretty much it.

I don't wanna take up too much of your lunch. I do believe that what we've created here is what I think is the first viable, CSS-based alternative to CSS-in-JS, but if you want to, I don't care, you can put your CSS inside of your templates, whatever, it's fine.

Just make sure it's analyzable.

So I owe, yeah, this is a recap of the things that Mark and I both thought about CSS-in-JS, and these are the bits that I think we checked with this solution. And I think I have an idea for even how we could do anonymous rule sets but I need to think about it more.

Once we unlock this world of static analysis, I actually think there's room for a tonne of new tools and new abilities to think about things, so I'm imagining a bunch of capabilities around giving reports to our design systems teams that say, "Here's how all these classes are being used. "We noticed that this class and this class "are always used at the same time.

"Maybe you should just merge them "inside of your design system, that it's not very useful," or, "People are doing a tonne of overrides "of the width property here.

"Maybe that needs to be factored." And I think once we start being able to, as the design systems teams look at how our applications downstream are using it, there's a tonne of interesting analytics that we can use to learn from actual real world usage, and feed that back into our process.

Thank you.

(audience applauding) Real quick shout-outs.

LinkedIn pays me money, I like them, and Adam Miller is my coworker who has been my partner-in-crime on this project, and he's amazing, you should follow him on Twitter. We have a fucking website that launched today, css-blocks.com.

Go and check it out, so you can sign up for updates 'cause there's not much information there.

And follow me on Twitter, okay.

(bright electronic music)