About this talk

A rundown of PostCSS’s diverse plugins. Helping you cherrypick the right ones for your next project. Followed by an introduction to stylelint, a modern CSS linter powered by PostCSS.

Transcript

[00:00] [music] Host: [00:00] I'm a freelance, one-time dev, I mainly write CSS, though. In this past year I wrote a CSS linter based on PostCSS. I'm going to be talking about that in the second half of the presentation. [00:23] I'm going to start with an introduction to PostCSS. PostCSS is a JavaScript framework for building CSS tools. It's being used to build lots of stuff, from linters, minifiers, beautifiers. It's what works behind the scenes in CSS modules. It's what's happening behind the scenes it hashes your selectors. It can be used to build preprocessors like Less and Sass. [00:47] It's, of course, being used to build post processing tools, the most famous of which is Autoprefixer which everyone is using. It's used behind the scenes that's the key point. One thing to bear in mind is when we talk about PostCSS, we're referring to both the tool itself and the ecosystem. [01:10] Just like with Grunt and Gulp, when we refer to those things, we're talking about the tool itself and the big ecosystems of plugins around it. That's the case with PostCSS as well. What is this framework? It's a three-part thing. The first thing it does is that it passes your CSS. [01:29] You give your source files, whether that's CSS, LESS, or SCSS, and then it converts it into an AST, so a tree of nodes. It's quite similar to DOM, which is quite easy to work with. It then provides a means to go through the AST to perform transformations or analysis. [01:51] If it does transformations, it then provides the third step which is to stringify out the changes to that tree. If it does analysis, it provides a means to attach warnings on the nodes within it. This gives you an idea what the tree looks like, a bit of simple CSS. [02:08] Up here, we got a simple rule with a selector, a single decoration inside of it with color property and the value red. It converts into this tree here which is the root node which contains a singular selector node. Inside that, we've got a declaration node with the property color and the value red. [02:26] That's postCSS. That's all it does. It's just the passer. It's the means to traverse through and to analyze it. It optionally gives you the ability to stringify that result out. That's all it is. PostCSS killer feature is it's easy to use API. It makes traversing, and doing analysis, and making transformations on the tree incredibly easy. [02:52] It has a number of functions to the traversal and quite similar to DOM here, we got previous and next, and that kind of stuff. Also, rules and functions like walk rules or walk declarations. They will loop through all those constructs in your CSS file and then you can do a transformation on those. You can then use like insert...You take a node, you can insert it before, remove it, clone it, replace the values of it. [03:16] The last part is the API for just attaching a warning. That's it. It's so easy to create tools. It's like being an explosion in them. I'll just give you an example what one those plugins might look like. You might have read a couple of months ago, the W3 guys on their mailing list, they talked about how, if they could go back in time, they would change a few with the property names and keywords. [03:46] One of those they wanted to change was, if they could go back, they want to change border radius to corner radius because it's more about shaping the box and going back. This is a plugin to go back and fix that mistake. It's taking the corner radius and it's just converting it to this. This is all done in just like five lines of JavaScript. [04:08] Importing it, this is how you register the plugin, and then here we are. We're taking the roots. We're walking through the declarations, filtering on corner radius. We take the declaration node and we rename the property to border radius. That's the plugin. This is what's like democratizing the access to writing CSS toolings. [04:27] Before, there was a big overhead, a lot of learning. Maybe it was written in Ruby. Maybe it was written in C++, I guess. Is it for LibSass? Now, it's JavaScript and very, very simple JavaScript. This explosion in tooling is probably 500 plugins now, my last count. They're roughly split into two categories. First category is transformations. [04:54] OK, cool. The useful things and then the fun things. I'll go for the useful things first. I already mentioned CS and modules and Autoprefixer. The most used minifier is one called cssnano. It's a modular minifier which is basic collection of PostCSS plugins. That, along with CSS modules, is bundled into Webpack CSS Loader so another illustration about how postCSS is used behind the scenes in a lot of modern tooling. [05:23] Beautifiers like style format which you can use and edited as you're writing. It's correcting your white space to whatever style you want it to be. Then the interesting stuff which probably represents the vast majority of these 500 plugins is language extensions. What allows you to create a preprocessor like LESS or Sass, that itself is split into two camps. [05:49] Those who are trying to mimic the features of SCSS like mixins and for-loops, and they're grouped under the umbrella of PreCSS is the name for it. There's another camp which is the CSSnext camp and they're looking kind of going out on a Babel route, whether looking at feature specs and then it works out ways to transpire that into code that can be installed by browsers now. But because it's so easy to do, there's no need to consolidate effort. [06:19] It's OK that this guy is over here who wants to rebuild SCSS in it, and it's OK, this guy is doing something new over here. It's also OK that people are creating fun things. I suspect it's an opportunity to learn how the API works. [06:35] There's a whole set of plugins allow you to write SCSS in your native language. There's one for Swedish and Spanish. There's one for PostCSS-spiffing which allows you to write in Queen's English like "colour" with u, "grey" with and e and then please converts to color gray important, or this one I just came across PostCSS emoji style, display, flex, flex-direction, row, justify-content, flex-end. [07:03] [laughter] Host: [07:03] It's amazing. This growth of a simple API and simple tool that starts with very simple tooling and then scales up from this stuff all the way up to creating the equivalent of SCSS. [07:16] With this explosion tooling and with the majority being around transformations there is an opportunity here to craft your own preprocessor, to move away from LESS and SCSS. There's a couple of compelling reasons why you might want to think about doing that. The first one is simplification of your toolchain. Just from the raise your hands before, everyone's already using PostCSS in their tool chain. [07:44] What's happening in the moment is, if you're using it in another preprocessor, you're taking your source code, you're parsing it, you're doing transformations in LESS or Sass or Stylus and then you stringifying that and then you're passing into PostCSS which then parses it, does its transformations and then stringifies it again. [07:57] As you're already doing the PostCSS transformation step, why not just inject your other transformations and then save a parse on the stringify step? It also simplifies the tooling around. The more compelling reason for me is that you only add the features you need. If you find yourself in this situation, say, I would use Vanilla CSS if it did x, y and z. [08:28] I would use Vanilla CSS if I could do nesting. It's just something very specific to my project and handled my prefixes automatically, then you can do that. The advantage of this is that with existing monolithic preprocessors, you bring the kitchen sink with it. It's like, "Hey, on our project, we can use SCSS," but is there a document over here that says, "But not these features." [08:51] We can use SCSS but we're not going to use conditionals and for-loops. We're just going to use it for nesting and that relies on communication and also develop the discipline. This is enforced by tooling. If you try to use a conditional in this, your build is going to fail. Transformations represents probably about 80 percent of the plugins out there. [09:09] The other set of tools being developed on PostCSS are analytics. I just highlighted three here that are these. The first one is, "Do I use?" We can do that as you give it your target browsers, then look through your CSS code and then it warns you if you're using a feature not supported by that target. [09:29] In this instance here, I'm using pointer events. I'm saying, "I need 10," and I'll get a warning saying, "Pointer events not supported." You can either fail your build on that or maybe it's just a warning to say I know what I'm doing here. I'm going to put a fallback in, but at least you get the warning from it. [09:46] Colorguard. this one allows you to protect from lots of the same colors appearing in your cobase like five shades almost the same red. When you define your first shade of red, when you come to write the next one, and maybe you've got color picked into a document or something, and it drops in this slightly off red, it's going to warn you saying, "Hey, you already got a color here which is almost like that." [10:08] Then BEM linter. This is actually authored by my co-author, one of my co-author on Stylelint. It's a quite amazingly powerful tool if you write BEM code. What it allows you to do, is to specify what flavor of BEM you're using. If it's like standard BEM, or if you're using SUIT, and then you define inside your CSS file, like this is the button component. [10:31] If you write anything that isn't a component name, a modifier, or descendant, it's going to warn you that's an invalid component. It enforces your chosen architecture. The last part of that slide was, Stylelint is...I just want to mention that it's becoming a bit of an umbrella for these analytical linting tools. [10:51] The, "Do I use the colorguard, and BEM linter?" All available within Stylelint as plugins? There's lots of things being shared across those now being grouped into Stylelint. Who's CSS linting? OK, cool. What's everybody using? Is it SCSS lint? The Ruby based one. SASS lint at the back? Man: [11:17] I use Stylelint. Host: [11:18] You use Stylelint. OK. Good. [11:20] [laughter] Host: [11:20] [laughs] Anyone else using Stylelint? OK. Good. [11:22] Just a quick introduction to Stylelint. Unlike previous preprocessors, we took a different design decision. Previous ones tend to based on like, "What was the current best practice?" Or a rule saying, "Don't do this, because it's deemed the right way of doing it." [11:45] What we've done, is we've taken a way of targeting the constructs of language. Things like, at-rules, rules select as declarations, and then optionally targeting the punctuation within those, like the colon within the declaration, and then specifying a way to allow, disallow, or apply limitations to those things. [12:05] It makes no assumptions by how, or what you want to enforce in your code, in your style guide. It just allows you the ability to go in there, and target very specific things, and say, "This is the rule we wish to apply to that." Like PostCSS, everything is off by default. It's very configurable, and explicitly configurable. [12:22] With PostCSS, if you were to give it some CSS, and define no plugins, you'd get the exact same CSS out the other side. Same with Stylelint. If you take Stylelint, give it no configuration, it's not going to do anything. Support for popular editors who got like, Atom, VScode, and the WebStorm ID guys just added it yesterday I think to their suite of IDs. [12:44] Good adoption. Get [inaudible] using it to lint their primer framework. The [inaudible] GitHub, and then the Facebook guys use it internally. They've replaced their [inaudible] based linter with Stylelint, and a load of custom plugins, which goes into the last point of it being extendable. [13:02] Built in Stylelint is about 150 rules, but there's a plugin system based PostCSS's plugin system, [inaudible] very easy to write those rules. Just to illustrate that this is an example plugin that disallows the important property at the end of you declarations. It's very similar to the PostCSS one. [13:24] In fact, Stylelint plugins are just PostCSS plugins with a few extra tweaks on top. Again, we're doing the same thing here. We're taking the route, we're looking through all the declarations inside of PostCSS's tree, they have an important property, we're just checking on that. [13:40] If it hasn't got one we return early, otherwise, we use Stylelint's report method, which just wraps that one I showed you that before, PostCSS's report, and that it. That's how you can very easily do analysis on your...Very specific plugins to your needs on your CSS code in JavaScript. [13:56] What can linter do? I can talk about four main areas. The first two aren't very interesting. I think the next two are quite unique to Stylelint, so quickly go through the first one, which is code style, so enforcing whitespace, that kind of stuff. Most linters do that. The slight difference in Stylelint, is it allows the accuracy to target anything here. [14:21] In our components in our CSS on the side here where we've got like an inconsistency, no space after the colon, it's got notice that inconsistency and warn about it. This is the corresponding config to enable that. The way these are named is, it targets the construct followed by the bit of punctuation, and then descriptive for the name. [14:41] We're targeting declaration, the colon punctuation with it, and we're specifying whether there should be a space after or not, with always. The next one, again, not as interesting as after two, is the color here. It's only two Hex values, and so that's an invalid CSS code, which will get flagged when you get to browser. [15:02] Having notification of that while you're editing saves time. The next two, the first one...it's a bit of a question actually. There's something happening in the first three lines of code there. It's valid CSS, but it's not going to give the outcome you expect. Can anyone see what it is? Man: [15:22] [inaudible] . Host: [15:22] Exactly. Because the declaration block is set to display inline the margin-top property is ignored. There's lots of little gotchas like that in CSS. There's a number of rules in Stylelint to protect for those. Another example is, if you define the top of your declaration box, a longhand property, like margin-top, and at the bottom, you define a shorthand margin. [15:47] The margin is going to override and trump the one above it. These little time savers are going to, you know, when we scratch our head about those. Like, "Why isn't this margin working?" [laughs] It'll protect you. Then the most powerful feature of Stylelint I think, is its ability to target these constructs and to specify what you're going to allow in your codebase or not. [16:10] The example I've got here is, there's two representations of color. I've got Hex value, and I got an RGB andI'm going to show you in CSS, there's probably about 10 ways to represent colors from like HSLs, the HWBs, and there's a few more coming in, like Level 4 specs. [16:26] You might want to, just for consistency, say, "Choose one representation," and say "That's all we allow in our codebase." [16:32] We can do that with a combination [inaudible] , function-blacklist with disallowing RGB, and only allowing the HEX. The other one connected to that...So that's about consistency and code. The other one is about enforcing your chosen architecture. [16:50] The fact that this is called my component on CSS implies we're using something like, BEM [inaudible] component, or maybe CSS modules. I'm using like a div selector there, which is probably going to be globally polluting, and so we have the option to kind of enforce the architecture by disabling these type selectors. [17:07] I want to take that one stage further, and just show how you can apply that to enforce the CSS mythologies as CSS modules which I'm working on a project at the moment, so I just ripped this out. This config split into two halves, the first one is about restricting the language features down to make your code consistent. [17:27] We're using the more restrictive whitelist here, rather than the previous blacklist one. We start with like, "Allow nothing in CSS," and when you come to write your first component you're like, "Actually, I'm going to use [inaudible] , so I'm going to allow the media one." [17:39] That means, when someone comes to do @import it's going to say, "You can't do that," and you have a discussion on your team whether you want to use @import, or whether there's some other bundling method you're using. Same with units like, percentage in pixels. Then if you use VH, you've put that in, you have a discussion, you poll request whether you want to do that. [17:58] Function-whitelist again, start restrictive just like the...I'm using colors [inaudible] . Then that corresponds with the color named and color hex as a way of saying this is a unique color representation I want to use. [18:11] With property-no-unknown, you don't have to be that restrictive. You don't have to use whitelist, there's the blacklist, but there's also something in between which is these property-no-unknowns and that says, "You can use any property you want as long as it is known within the spec. On the superficial level, this checks for spelling mistakes and that kind of stuff. [18:31] It's also nice that you are just using spec based stuff. Because I'm using CSS modules, it has the composers thing which isn't part of the spec. It's just part of the methodology. We can have these abilities that optionally override those and that highlights the configurability like, you got the methodology that adds new properties, new @ rules. You can always say, "Allow these things as well." [18:52] Then this kind of co-style thing, I want a spot opinion whether you should be...how you should be ordering your properties. Again, it makes no assumption about how you want to do that within your team. But this one here specifies composers at the top and then put your rest of your properties alphabetically underneath. [19:08] Now, this chunk here, this is about supporting your architecture. The first one is, in CSS modules, the convention tends to be "use camelCase" and this one here says class-pattern camelCase selectors. [19:22] The next one is a powerful rule that allows you to specify the max-specificity of your selectors. This doubles as a nesting depth rule because instead of saying, "Right, I'm gonna set my nesting depth to five things," this actually unravels all your selectors of the nested and then calculates the exact specificity of it and you can set a maximum on that. [19:43] In CSS modules, they will allow you to use ID selectors so we have zero ID selectors. We got two classes there which is the class itself and, I believe, CSS pseudo classes also calculated in specificities. So, two. The last one is the elements specificity, setting that to one for these pseudo elements. You think that might allow selector...type selectors. We override that down below. [20:13] Then also disallow attribute selectors, disallow combinators and disallow universal. Boom. That's like CSS modules only write in a code like that. If you deviate from it, you're going to be warned. Final slides, just a summary of what, I think, that brings and how it's a continuation of the trends, I think, has been happening in CSS for a while. [20:42] A couple of years ago, we used to write very complex CSS systems where the CSS guy on the team was the only guy who really understood the system, just like the backend guy and the JavaScript guy will be doing their module stuff over here and then you have the CSS guy developing a really complex system. [20:59] Then, we've moved away from that with images of component-based architectures with max, the BEMs, the CSS modules and then composition and react-apps, that kind of stuff. Now, it feels like we got an opportunity to also simplify the language itself. We had these massive CSS specs and then on top of that, we've laid on the kitchen sink of monolithic preprocessors. [21:22] Then we got an opportunity now to say, "Well actually, I'm gonna start with Vanilla CSS. I'm going to start with a subset of Vanilla CSS. I'm gonna force that with tooling and then I'm gonna opt in the extensions I need on that project." I think that leads to a kind of a better developer experience because by being...using, this end up with consistent code. [21:43] It tends to be simpler and it tends to be more comprehensible to, not the CSS dev on the team, the JavaScript guys, the backend guys can go in there to work on their component and see simple CSS that is both simplistic in the language-like features it uses and simplistic in its components.