Jonathan Snook is a designer and developer from Ottawa, Canada. Snook has a 15+ year career working freelance, for agencies, and for product companies such as Yahoo!, Shopify, and Xero. He writes about tips, tricks, and bookmarks on his blog at Snook.ca.
He has also written for A List Apart, 24ways, and .net magazine, and has co-authored two books, The Art and Science of CSS and Accelerated DOM Scripting. Snook has also authored and received world-wide acclaim for the self-published book, Scalable and Modular Architecture for CSS, sharing his experience and best practices on CSS architecture.
[00:00:01] I'm here to talk about the componentization of the web. So I'm going to start off with one thing that I think is important to mention, and that is that style is important. And if we go back to the very beginning days of the web, when we had black text on a gray background, we had headings, we had images, and that was pretty much all we had. And the thing is is that when we look at sort of the commercialization of the Internet, companies need a way of establishing a brand, of establishing identity. But also when we look at all the applications that have been built on this platform we need to establish hierarchy, we need to establish usability, and all that comes through style.
[00:00:59] So in the beginning we thought about pages, that reminds us of old documents, right? The documents are the way pages were structured. We have headings, we have paragraphs. But you know with that shift to applications, we required a shift in our thinking as well. So in the beginning we didn't have CSS, we didn't really have a great way to style our pages, we had font tags that we could embed everywhere which wasn't the best approach. If we had a way of centralizing our styles for an entire site, we might have a better way of doing that. And in about 2003 the Wired.com redesign came out and it was one of the first sites that actually showed that we could use CSS as a way of styling your pages, to control layout control, to create a good looking site. For all the work that I was doing at the time, I was really around a lot of blogs and blogs have a fairly simple structure. A lot of sites like Wired.com have a fairly simple page structure where we're talking about headers, footers, side bars and our main content area. And admittedly if you look at a lot of sites these days, we still have a very similar structure to this. And so we would write the CSS for this page. Like what's my header, what's my footer, what's my side bar? This is how all these things should look like. And then we would move onto another page and we would say, here are all the styles that are different. And when I look at CSS I kind of think of like transparencies. I don't know if you remember back in school, the overhead transparency where you had that little sheet of transparent currency that's put on this glass? But you could actually layer multiple transparencies one on top of the other. And from that we get this picture. The problem is that each selector that we write is essentially a new transparency that we're putting on. So when you say "these are my headers" and then I start to get a little bit more specific.
[00:03:25] So I have my headings but maybe when I have an inside article I'm going to style my headings in a different way. My comments I'm going a style a specific way, but I can be very specific under certain conditions and I continue to get more and more specific. And what happened is we kept layering sort of one transparency over another, adding more and more CSS to this one long file. Can you imagine after a year or two it feels like this tree. We can see this stuff at the end, but coding styles and what we did were completely different from the stuff at the beginning but everything just kind of layered onto this massive document. This scrolling document that you saw here was when I first started working at a company called Shopify and I came in. Mind you this was five years ago. So still what I would consider fairly recent. This was their CSS. It was one large document. So I think a lot of the stuff that I'll be talking about haven't really changed that much. Now if you could imagine that CSS document, those transparencies in there, and we have one specific thing on the page that is a confluence of all these selectors targeting that one particular element. And then I say, I need to remove this CSS, so I'm going to grab a selector, I'm going to grab a line and I'm going to remove it. And as a result of removing that I have inadvertently affected all of these other things because that transparency that I've pulled out, that selector that I pulled out of my CSS actually affected a lot of other stuff. And it's very difficult when you're looking at a stack of transparency's to know what's going to happen when I pull one out of the stack.
[00:05:15] So you know I haven't come from a page oriented way of developing. We started to see a change in how we approach CSS. So when I was working at Yahoo - this is actually my first time working on a product with a large team. Before that I was a freelancer and as a freelancer it was mostly just me working on a particular project. So I would get the project, I'd do the design, I'd do the CSS and ship it off and I'd never have to see it again. I'm assuming that code was fantastic. Of course it's fantastic. But here I was now trying to deal with like coding styles and whatnot for these other projects. And so I started looking around at what other people were doing and I came across OOCSS, which was focused on how do we look at componentizing our stuff. And I took that and layered on a bunch of other things. And since then BEM is another approach that has also become quite popular. And these are all really around the concept of making things a little bit more understandable. Instead of thinking of all these pages... Which you know when you're dealing with the complexity of the applications and sites that we work on these days, actually become really difficult. Because yeah when I make this CSS change I might not see a change on this page that I'm working on, I may have actually sffected things in three other places that I don't notice until we start getting those bug reports and then we have to do those release notes that say minor bug fixes.
[00:06:53] So what does it mean to be modular? Well, a module is "each of a set of standardized parts or independent units that can be used to construct a more complex structure." And to me the important part in this is independent. You don't want something that has all these dependencies across other things. You want to be able to take something, move it around and know that it's not going to break. You want to be able to put other things into the same place and not have things break. And a lot of people when they think of this they think of Lego, right? I've got this bucket of pieces that I can put together. And this is bucket of pieces is probably more complex than a lot of the projects that you're working on. In fact I've yet to see a project that really tends to deal with more than, say, 50 components because a component is something that your users have to understand how to use. And if you have a bucket of hundreds and thousands of pieces, that's a lot of complexity that your users need to understand. So I don't really like the Lego analogy. To me, Tetris I think is a much better way of putting it. With Tetris, you've got predefined components, right? Each component has a different shape and they can fit together in certain ways. And from that we can build the shapes that we want for our sites. The thing is is that there are a number of hurdles, when we're building stuff, that make it difficult for us to be truly independent.
[00:08:31] So within CSS there are sort of four main things that we have to deal with. One is inheritance. We have to deal with the cascade. We have to deal with the browser's own default dialing for different HTML elements. And then we have to figure out how do we put this stuff together in a way that makes sense. So inheritance. Now inheritance you know affects things like topography. Again going back to the early days having to wrap font tags around every single paragraph element or every single table cell was a very cumbersome approach. Wouldn't it be great if I could just specify that my entire page should use Helvetica? Or my entire page should use Gotham? Like being able to specify this for the entire page in one place would be a huge asset. And we got this, right? CSS allows us to specify things on a global level. And this is great. Everything, those styles, essentially inherit down to the elements that are embedded within them. Same thing with table styles. So for us this is a good thing. We don't want to get rid of this. If we had to specify, you know, these days we have a couple of hundred CSS properties that we can actually apply on a particular element, having to specify all 200 on every single element would be really cumbersome. We have to deal with the cascade.
[00:09:58] Now the cascade basically says "I've got four different selectors, four different rules that I've created for this particular element. But in this particular rule I said that the background color should be red. This one I said it should be blue. This one I said it should be green. Which one of those rules wins? And so there's a calculation that happens that help us determine this. The thing is that that calculation often gets us into trouble. That's when we start creating those little selectors. Where I was using a class selector here but I need to override that in this particular case, so I'm going to use two class selectors, or maybe I'm going to use an ID selector, or I'm going to start having to use bang important to say you know I need this to overrule all the other things. And we get into this sort of competition in order to style things properly.
[00:10:49] So some of the things that we can think about is 1) to avoid writing multiple rules for the same element. And that's what a lot of the modularization is, right? This independence. So let me create a rule that is very specific to a set of HTLM elements. I could use inline styles. So inline styles, basically if I'm templatizing my website - I've got a template for this component, a button, a modal dialog - then I can write those styles at essentially the lowest part of the component as opposed to further up in the tree, having to deal with possibly cascading rules, possibly having to deal with inheritance. And I can also create a structured layering system to prevent conflicts, which again is essentially what SMACCS and BDM do. They say OK we're going to create a component and we're going to layer on styles, and then I've got my - with BEM, which stands for Block Element Modifier - I've got my block which might be a button. But I don't just have one button style for my entire website. I might have primary buttons, I might have secondary buttons, I might have modal buttons that need to be smaller, or maybe I've got call to action that need to be bigger. And so I have all these different variations and I need a way of specifying what these variations are, but I'm still trying to group these things together and keep them separated from everything else.
[00:12:11] We have to deal with browser defaults. So if we look at things like a button element - and I can create a bunch of styles to say this button should be have a border, should have padding, should have color - but what if I take that button class and I decided to add it to a link? Well links have a certain style that the browser puts on them - things like underscores - so that text decoration I now have to remove when applying this to links. And this is the other thing that we have to deal with, understanding that different elements have an innate style. You know if I took this button and threw it onto a Heading I'd have to adjust the font size. I might have to adjust margin and padding. The way a lot of other people have tried to approach this is using CSS resets where they basically just try to reset everything on the page to be as plain and ugly as possible and then try to layer on their styles on top of that.
[00:13:13] And lastly trying to put modules together. So you know, how do we handle that? So for example I've got a button, fantastic button, lovely gradient, border, text, you know it's got all the stuff that I want in it. But I want to put two buttons side by side. Well you can see the spacing between those two buttons isn't really ideal. So I need some way of specifying that well, maybe if I have a button beside a button I'm going to set the margin on this to separate them out. And excellent, I've got what I want. But project evolves and I need a button beside an input. And you think oh okay that's great. But what if the designer comes back and says, well we should have these things touching each other. And you're like, okay well, if a button is beside a button that's great. If the input is beside the button, that's great. But maybe I could just say that anytime anything is beside something, put a margin. Will that work in all situations? No. We start creating all these different exceptions for what we're trying to do. And so for this exception I'm just getting frustrated because of all these different variations, all this CSS I've had to add for every single variation, and because of this we're only looking at one particular situation. It was buttons. But we had buttons beside buttons, button beside inputs, maybe we start dealing with what happens when a button is inside a module, and we start adding all the special rules that we don't want.
[00:14:40] So there's a couple approaches we can do here. We can separate layout from module or we can use micro layout classes - also known as functional CSS, or atomic CSS - where we start using the sort of single purpose classes to update styles. So for example with the sort of layout module approach where everything that needs to be wrapped with an HTML element that has its own class, to say okay well layout in line has a certain property that has margin padding around it that I can apply in all the situations that apply, which feels like a very verbose solution right. That would be a lot of work to have to add into a project. And so as an industry we've been seeing this trend towards these utility classes, where I can just add it into one particular place. And in this particular place it's going to say margin left and maybe I'll have small medium large. That gives me maybe 10, 20, and 30 pixels of margin that I can apply in all these different places. And then in this case where I only wanted it on the one button, boom I add it to the one button and I'm done.
[00:15:50] So I'm coming back to SMACSS and this sort of scalable modular architecture and what this really means. So you know when we look at a design - we're trying to solve these design issues from an architectural, from a coding perspective - and when we look at a design we might have these buttons, you know, and we might want to make it stand out. And we can make it stand out using position, using color, using size, using shape. These are all different ways that we want things to stand out. And this again is a good thing from a design perspective. We need things to have priority, we need to direct the users attention to do certain tasks. The problem that we run into is when we try to apply everything to everything, and we get a mishmash of situations that can be hard to see when you're just looking at one page that might only have one button. But if we can go through the process of actually auditing our designs, we can actually see where suddenly we have this mishmash of possibly dozens of different button styles. This is actually taken from a single website across multiple pages. All the different buttons styles that they've had. If you're just looking at a single page it can be really hard to see that. And again every single one of those has CSS that's been written for it. Extra code that needs to be maintained and delivered to the user. I mean if you think from a performance perspective how much extra code is coming down to the user for them to be able to use this website. And that's where a lot of those sort of modular approaches allow us to define patterns and see how we're creating a lot of complexity not only for us to maintain but also for the user to understand - what am I allowed to click on? What is a button? What is a form input? What are these things that I can interact with? And if they're literally changing from page to page it can be confusing and difficult for the user to understand. So visualizing related things makes it easier to see the disparities. Again having the sort of component based approaches allow us to group components together outside of our project and be able to see those changes.
[00:18:18] Now as I mentioned design has a cost. So as much as we can simplify our design, the easier it is for users, the less we have to maintain and ultimately a better experience it is for everybody. So the pros to using an approach like SMACSS, or BEM, or OOCSS, is that it reduces the intersections, right? If instead of having this huge CSS file - where we're constantly adding rules and trying to override everything that we have before - if we can simplify things into a component - like a button or a modal dialog where we know the constraints of that object - we can know that we can write a style that is a modal class, for example, which has a modal content, which has a modal footer, and a modal header, and then everything else is something else. A button in that isn't a modal button. It's just a button. So we don't have this sort of complexity of having to override or create different rules, and it also allows us to simplify our selectors. We don't have to create these sort of huge chains saying, "well if I have a button inside of a modal dialog that's inside of this particular page...", you know? Writing out those longer selectors become unnecessary and there's less that we need to worry about when it comes to the cascade, because we do have less applying to one particular element on the page.
[00:19:52] However these approaches are methodologies. There is no framework that you can download to run on your project. This is just simply an agreement that you've made with all the developers on your team to follow this approach, and that requires diligence. And I know because I am one and I'm a lazy developer. I will take shortcuts when I'm in a project. You need this launched by Friday, I don't have the time to build consensus with my team, I'm just going to ship and I'm going to throw in all this extra stuff that I can deal with later. I'm going to create that tech debt, I will take out that loan and try to pay it back later. And I've seen this happen again and again at Shopify. We had a team that was launching a new feature within two weeks. They needed to get this stuff out. And you know I kept going to them saying "Listen, the way you're approaching this is going to create a lot of burden on not only your team but other teams, and it is creating design inconsistencies with the rest of the project." Now at one point I had to kind of give up and say, "OK well you do what you need to do." They ship that product and then a month later we did a post-mortem, and in that post-mortem one of the things they said is that once we launched we found ourselves having to deal with a lot more bugs than we expected. And the reason is because we had already solved a lot of these problems elsewhere on the site. So having that consensus, trying to figure out how do we solve these problems across the product, allows us to create much more reliable components when they're used in a lot of different places.
[00:21:36] And the other thing is that there's no tooling, so there's no way to establish that consistency. I mean a lot of projects now have linting tools built in so that, you know what, I might be a tabs guy but we've agreed to spaces and I am literally unable to commit my code to the project unless I convert all my tabs to spaces. And those types of things are important in creating consistency and consensus within a team. Now that is one particular approach. There have been since then other approaches that have come out - Atomic CSS, or as it's sometimes known as, Functional CSSA. There's a number of libraries out there like Tachyons on that take this approach, and what they attempt to do is take that sort of utility class approach and apply it to everything. Now from the Atomic CSS project they say "like inline styles, offers single-purpose units of style but applied via classes." So, for example, instead of having a modal class that defines the styles for all modals, you would instead have an HTML element where you would apply a class that says something like "the border of 1 pixels, a border radius of 5 pixels." Those would be two different classes that I would apply to that particular modal dialog and so I would build up essentially all these classes, have four, five, six, seven, different classes on a particular HTML element, thus creating a sort of a one-to-one relationship between class and the property that I want to apply. And the reason why they take this approach is because there is an upper limit to how many CSS properties and values you're going to apply on a project.
[00:23:33] So if we look at our topography, how many different font styles are you going to have? Well, I might have 8 different font sizes, maybe 2 different font styles - or font families, rather. And that might be the extent of my typography on my site. So I have 10 classes that I can apply elsewhere, anywhere on the site, to define the look and feel of my site from a typographic perspective. Margin. You know, I might only use 5 pixels, 10 pixels, 15, 20. Right? So I've got a limited set of values that I'm applying in that particular case. And as a result we end up with this sort of tool kit of properties and this upper bound of how big my CSS is going to be.
[00:27:27] And what they'll do is, as you're writing out "This is the class for this," it will actually create a compiled version of that class so you don't deal with clashes. You don't have to deal with the cascade. And a lot of that stuff can be very helpful in maintaining the styles within a particular project. I don't think they really necessarily deal with the bulk side of things, like how to minimize our CSS so it's the smallest possible.
[00:28:35] But on the flip side, handling CSS state management via JS can be a performance bottleneck. Although some of these tools handle a little bit differently - like style components will actually compile into CSS, so your hover states and whatnot will still work - whereas with CSS modules I know one time I haven't checked recently will actually attempt... you know a hover state isn't a hover state, it's actually a JS mouse over, mouse out event. And those types of things can create issues. But like everything else, the solutions don't understand the HTML and have no way of optimizing the properties, the values and everything that you're using. And you can also create side effects by using these tools in the project and then trying to include CSS from other projects. You start running into those cascade issues that you were trying to avoid to begin with.
[00:31:53] The Shadow DOM, however, is a very specific thing that can avoid the cascade and name spacing issues because what it does is, the CSS that is written inside there will not impact anything outside of it. And likewise, the sort of classes that you might create outside of it won't affect the stuff inside of it. But the problem is that Shadow DOM right now is only supported in a couple of browsers, like Firefox and Chrome, and while there are Pollyfills out there they don't work exactly the way they should. And we kind of have to wait until Shadow DOM is actually implemented by browsers before we can use them.
[00:32:33] And likewise, there is no performance tooling around them. This is to me an important thing. I look at HTTP2 as being a very useful thing that will hopefully allow us to componentize and cache individual components. But what about shared styles across multiple components? How do we optimize that? And that tooling isn't there. And likewise, cross-browser support right now is very spotty.