>>HOST: I'm proud to introduce Brian Perry that we will see on the screen, scope and CSS with and without JavaScript. Hello!
>>BRIAN PERRY: Thank you, happy to present this at my local camp that is now in my home office.
>> There's the bit.ly there that has a link to slides and resources, and a session node and PDF. If you are a follow along person or you want to check it out, it is there for you.
Let's see here.
Okay.
Probably the first thing ‑‑ how is everybody doing in the last weeks, year, or who knows. In general, I'm excited to take this virtual and get together and share ideas and, you know, it just makes me really happy to be part of a community like this during such a crazy time. And also, you know, a big hat tip to all the other organizers that have done incredible things to make this happen this week. And I'm Brian Perry, a lead front end developer, I live in the Chicago suburbs, and I'm a lover of all things component‑driven, so building with the components in Druple, design systems, pattern lab, story book, and increasingly building with component‑based JavaScript frameworks, like React. A lover of Nintendo, so happy to chat in Slack about the cool things you are playing on Twitch. I'm very excited for the new aninal crossing game that comes out tomorrow. And I'm on the internet in a variety of places, I would love to internet with you. So hopefully there are time for questions, if you want to track me down on Slack or Twitter to continue the conversation, I would love it.
So as I mentioned, I work for a company called Bounteous, we are throughout the Chicago and North America area, but all at home right now. And Druple is a handful of things that wedo and I work with a great team of folks and learn so much from them on a day‑to‑day basis. We are often hiring, I'm not sure if we are right now, but get in touch in the future if you like to do great Druple work and if you like to have great Druple work done, get in touch as well. We will dive into what we are here to talk about. CSS has global scope. So the CSS that you write is in a global name space, those rules apply by default globally, a really great thing if you want those to apply globally. If you have a small amount of code that applies to the application, it is great. But it is not so great when that isn't what you want, I will let people catch up and read this. And anybody who has read any meaningful CSS has experienced this, a change they make has an unintended consequence and part of that is related to the fact that CSS by nature is global. And, at the same time, there are new approaches using ‑‑ of writing CSS within JavaScript and a lot of those solutions offer something called Component Scope, so that's the idea that the CSS you write is only going to apply to that component and won't leak elsewhere and affect other things on your page. And as you imagine, there are differences in opinion on that particular approach, and people argue about it on the internet because of course they do. And my take is that each approach has different advantages, it would be great to use them together. So I think we should have both, just like the meme says.
So that is my talk. I don't know, how are we doing on time? Are we ‑‑ I feel like that might have been a little short.
>> Um, might be a little short.
>> Okay, all right. Maybe it is the virtual part of this. I guess I can maybe just, like, start from the beginning and dive in a little bit deeper. Let's do that. So this is Scope in CSS with and without JavaScript.
And CSS has global scope! So let's look at what that actually means. This is a code pen that looks at inheritance in CSS. So I will jump over here, and I'm assuming that everybody sees my browser window? If not, somebody yell. And the simple code pen has inherited properties in CSS, we have an H1 and paragraph tag here, we are creating a rule on the html element that sets the font family to the One True Font, Comic Sans. And the font family rule in this case is inherited by children, that allows comic sans to be applied to H1 and the paragraph. That is great if we want the rules to apply globally. If that wasn't the case, we would have a situation where we would have to write ‑‑ that works.
You know, we write individual rules, we have to target the paragraph, we have the paragraph within H1, sorry, with the Comic Sans and we have the H1 to apply in both cases. And it would be cumbersome, we have to write complex selectors, but a lot of duplicate code to be able to achieve the same effect if inheritance wasn't a thing. So that is potentially one of the super powers of CSS in this case.
And we can talk about how different CSS rules are combined and apply, there's the cascade, in CSS, part of this is the idea that style sheets have difficult origins, author, user, and system and how they are applied is the cascade. So this image is the definitive guide to CSS styling order, which is easy and simple to consume and always remember. And being sarcastic there, this is from a CSS tricks post, this image, and a lot of good information there, but it definitely does get at the fact that it can be complicated to understand how your CSS will be inherited, how it will apply, how the cascade is going to be combined. So the best thing that I have found as a way to explain the cascade is the W3C spec, still. So going through that at a high level. So you will find all declarations that apply to the element and property for the particular media type. And they are listed according to importance and origin, there's the declaration, user normal, author normal, and important. If you stick an important on it, it will win on top of that, and user declarations will come out on top of that. Anything with the same origin and importance will be sorted by user and specificity, and on top of that, it is the rules that are specified. And that is powerful and having multiple origins gives interesting control. We can write in the CSS that we think a page should look like this and it is represented like this, and at the end of the day, the user says I appreciate you want it to look like this, but to consume this, the font needs to be bigger and we can dictate that needs to be the case, which is great.
And then specificity. So a fun way I found to look at specificity is from Star Wars, so CSS specificity wars in this case. And so for the Storm Trooper, that's the element specific, and class attribute or pseudo class is slightly more specific than that. And the ID selector is more specific and then on top of that, the in line style will win out. And the universal selector or things that are inherited have no specificity.
So there is a lot more to specificity, and then we will go into right now. But there's a lot of different ways that ‑‑ different selectors can be combined together, you can give them point values to figure out what the specificity will be. Again, this CSS specificity wars is a great way to explore that.
And also, you can use the Death Star of important at the end of the day if you need to.
Those are the ways that they can be combined together. And CSS in JS can provide component scope and other advantages. So we are talking about why somebody would write within their JavaScript. We talked about the idea of component scope, so that's the idea that the styles you are writing are going to aplay to the component and will not leak out and affect other things. And then there is also the idea that you are co‑locating your styles with components, everything is packaged up together. It can be nice from a developer experience potentially to have the CSS right there with your logic, and it is potentially an advantage for components that are distributed or otherwise composeable, because you know that everything that you need to render that thing is all in one place.
And then using bundlers, like Webpack, there are potential advantages there, because it can do static analysis on your code and determine what CSS is going to be used and what CSS will never be used, and can actually eliminate that stuff from the bundle. And then, because the styles are going to leak out, it will take the pressure off from a naming convention perspective, you don't have to use class naming to control specificity as much, or control the cascade.
And then there's the idea that using some of the approaches, you can have the props passed into a component. So something that ‑‑ the component knows about itself to affect styling, rather than having to juggle class names and update things in the DOM like you might have to do with a jQuery approach.
And then also there are definitely some cases where it might be fear and lack of knowledge in CSS that is leading people to this approach, too. But not all cases.
So why wouldn't one use CSS and JS? We have to know how CSS works, even stuff that is hard. It comes with, in many cases, I believe not all, there are new approaches there. But it comes with a bundled size or performance cost, there's a runtime that ends up part of the bundle to allow you to do this. And there is a barrier of entry, so somebody who is really comfortable writing CSS may not be comfortable writing JavaScript and this gets in the way of being able to make their awesome CSS. It is, at the end of the day, an additional layer of abstractions. So some of the approaches here use a JavaScript object‑like syntax, rather than writing regular CSS. That can pose problems with syntax highlighting, though there are solutions around that. And in general, knowing that everything is scoped within this component can potentially get you in the mindset where you focus on too much the individual component and introduce potential for inconsistency or opportunities for reuse.
So the question is, which approach is right?
And, you know, again, with our mean friend here from the taco commercial, why can't we have both? Why can't we have that level of control over global rules and component scope rules? I think that would be amazing if CSS allowed that.
But that is my talk. Looking at my watch, though, I think we are still pretty short. I did not know how much this virtual approach was going to affect my ability to proceed. Maybe it is just the past week, I don't understand how to perceive time anymore.
Okay, so I guess we will just start from the beginning again and dive a little bit deeper.
So this is scope in CSS with and without JavaScript. And CSS has global scope. So we talked about the cascade, we talked about specificity and inheritance. And, while that is powerful, it can be tricky in some cases and understanding how everything comes together. We should get our act together. And in comes CSS methodologies is a way to help with this.
So this is the idea of a particular approach to organizing or naming your CSS.
So popular methodologies are object‑oriented CSS, very popular in the Druple community is BEN, Block Element Modifier, slack, scaler architecture for CSS, used in core, I believe, as far as the file structure. This can give you something that feels like component scope at the end of the day. Like a CSS by its nature is global, it is not really bad.
So I wouldn't be surprised if a handful of people that are watching are already familiar with BEM. To look at it at a high level for those of you not familiar with it, I found a post, BEM for everyone else that uses the concept of an emoji to describe what BEM is and how it works, and I created a code pen to illustrate that. We will take a quick look over there.
Make sure there are no crazy comments in the chat. Just laughs and goofs. So here is the code pen, we have a div here, with a class of face. So that's the block, the parent‑level container here.
And it gives a yellow circle, the face itself. If you wanted to add elements, we can create a div, give it a class, and the convention here for BEM is it is the block face and two underscores and the name of the element, so the element is like a sub component inside of the face. And then at that point, we have eyes. And then we will give the emoji a mouth. We can do face, underscore underscore mouth, and yep.
We have a mouth. And now there's the M in BEM, the concept of a modifier, we have a face with eye and a mouth, they are in the default state. So a modifier lets us change the presentation for those things. So the convention for that, you will still have your default class of face eyes in this case, and if we wanted to open our eyes, we also go face, eyes, and then it is two dashes and then the name of the modifier.
So open, face, eyes, open, will open our emoji's eyes and then face, eyes, face, mouth, open, will open our emoji's mouth so he is nice and shocked. There's a number of ways these can be combined together. At the block level, we have a modifier so we can do face and we have a red emoji with this open eyes and mouth.
And that's the basic idea and the basic structure.
So I think BEM is great and I know a number of others in the Druple community also agree, or use it and stay silent about their opinions about it.
But I love that it gives a nice, understandable structure in our mark‑up and our files and it makes it really easy to scaffold out a component so you can, based on the BEM structure of that thing, you can write out some markup containing dives and then we can have the same classes in SaaS partial and color it from there. And it gives nice component‑focused classes that are less likely to conflict with other things, due to naming conventions. It feels like component scope, at the end of the day, it actually isn't.
But as I mentioned, it is not scoped to the component, it is not enforced at the tooling level, like the CSS and JS options would be. If you are Linting the SaaS, you can force naming conventions. And it requires discipline across the team and everybody needs to agree they are writing using BEM, everybody needs to do it, nobody can cheat, and naming can be challenging. There's a general framework of how you will name the classes, when things start to nest and components get complicated, there have been times when I thought a lot about the right way to name something using BEM conventions. CSS and JS can provide component scope. Let's look at what that means. So first, we will look at the CSS and JS playground. If you don't know the options, this is a great place to start. We will look at this example here, the styled components, a library used with React and also you can pick from a whole bunch of other options if you want to try other libraries, syntaxes, stuff like that. We will look at the form here. So it is a little squished on my screen. And so first in our component for the form, we are importing style from styled components. And if you click on the button, that allows us to click a constant for the button component, it is a style.button, the button element. And there's the back tick here, and what that is, it is a tag template literal in JavaScript and that allows us to write the CSS code and the tag template literal allows us to use things like JavaScript variables within that string, which you will see an example of in a second. You are creating a constant, with styles, you have SaaS‑like nesting and that gives the submit button that looks like this, or the default button state. And then I talked about previously the idea of with this approach being able to respond to the props they are passing to a component. So this is a simple example of that here. For our submit button, we are looking at the disabled prop. If a button is disabled, it is going to use one background color. If it is not, it uses a different background color so you don't have to change anything in the DOM, or give it a class name for that. It knows something about itself and can adjust its style. So, for example, if I fill out this form and put what is a phone number in there, you can see the submit button changes to that blueish color, which is pretty cool. If we inspect here, to give you a little bit of an idea of how the component scope actually happens, I mentioned there's a runtime for the CSS and JS solutions. What is happening, if you look at the mark‑up, it is giving these gobbledy gook unique class names for each of the instances of the styled components and that's what prevents anything from colliding, unless you are doing something crazy, there is no way you are going to use that same combination of class names somewhere else..
So that is an example with styled components. We will look at another area of the JavaScript ecosystem, a similar example in the JavaScript framework, view.
And view does some interesting stuff as well. View as a concept of single file components, there's other ways to manage styles in view, and one of the things that Makes view cool is the single file approach. You can see the component, we have the template, and then the JavaScript, and our styles, all in one place in this view file. And on the right hand side here, the app is a code sandbox with the default, view hello world here. And I added three instances of H3s. So the first is just an H3, unadorned, the default element. And then we have the global component, so it is an H3 and the heading prop that is passed in is rendered out and then there's the scoped heading we will see in a second. We are looking at the global one and we have the styles for that. Color is green, setting a border, and because this is treated as global, it is applying to the traditional H3 on the page. You will see it is green and has the border. If we look at the scope heading component, it is the same component with one difference. In the style section, we are saying style scoped. If you click the attribute, make the view styles scoped to the component. So we are changing them, making the border red, blue, different font sizes. And as you can guess with specificity in the cascade, we will predict what happens if we removed scope from the component. It takes a second to catch up. And now because of specificity here and the things we talked about, the rule now that it is global is applying to the previous H3s on the page, this is what is coming last in the mock‑up.
But it is pretty school to be able to have that level of control over how your CSS applied, when it is global versus when it is scoped to the component.
We have a decent idea of what slide's coming up next. Why can't we have both of those things, and that level of control over CSS, with the way I approach building components, it would be great.
So thanks again, and that's definitely the end of my talk, looking at the chat, I can tell that no one is on to the fact that this is just a giant bit. But we still have more time. So let's dive in one last time, and just dive in a little bit more. This is scope within and without CSS, CSS has global scope, cascade, specificity, inheritance, methodologies like BEM, but a question that I asked myself and you may be asking yourself, can regular CSS have component scope? Not really, because CSS is by it's nature global, there's a few other interesting things to talk about. One is atomic or functional CSS, and I saw this abbreviated as AMU CSS. And functional CSS is using single classes based on what they do, the visual function. It is immutable. So the CSS is not going to change, the classes are not going to change. This shifts the control of styling and the visual control into mark up from the CSS. So there's a couple of approaches in the atomic world, one is static, so basically you have a preexisting set of utulty classes, you have a little bit of control over some variables, color, the grid system. And for the most part, you have a bunch of utility classes in the mark up as you see fit. And in the mark up approach, in the mark up, CSS is generated based on what you are asking for in your mark up, kind of wild. And tail wind is a popular lubeerary here. So this gif from their home page illustrates that pretty well. You have the utility classes, you can write in the mark up text large, 500, W6 ‑‑ it is going to change the style, based on what you provide as the mark up. It is a utility‑based framework. So from my perspective, it is not for me. It is not how I want to write CSS, it takes a lot of my workflow, I can stop that for a second.
And I have heard more and more people using tail wind, liking it, and there are folks who love it. And the interesting thing for this conversation is the fact this is another approach out there that seems to be gaining some steam where developers are trying to bend or override the default behavior of CSS and some of it's global nature. It doesn't necessarily make ‑‑ you have component scope but it gives you so much more control within the mark up. So the fact that people are doing this and are interested in approaches like this are noteworthy, given the CSS landscape. And as we mentioned numerous times, CSS and JS can provide component scope. But the counter point that we looked at, can CSS and JS be more like regular CSS, as in could it be done with less JavaScript and can it handle things globally? Thinking about the global aspect of that, another approach is CSS modules and it is a little bit different from something like styled components we looked at, and it is not really a package, it is handled by a bundler and build process, Webpack, and it scopes your styles locally by default, as we see in a second, can handle things globally as well. And it plays nicely with SaaS, or Webpack SaaS in this case and other things that are part of the build process. And it also ships now with create React app in Gatsby, you can experiment with this and try it out. This is the first time that I was hands on with this approach and it has a super snarky logo with modules overflowing its container, haha. And just to look a little bit at how CSS modules works, we will look at a side project I did a while back, static site, about static sites in Gatsby. We will look at the card components, with nothing complicated going on. And this is a SaaS partial project card.module.CSS and in Gatsby, that extension lets Webpack know it should be treated as a CSS module. The styles are not super important, but the general structure is what we need to look at. So there's the parent class, the block of BEM, project card, inside there are elements with pretty generic class name, title, language, and description. So if you look at our React component, projectcard.JS, we can import projectcard.CSS and see styles, a styles object we can use in the JSX when we are rendering things. If you get to the div here, there's the class name prop and you can use styles.project card for the project card class and styles.language and so on.
And this component is similar to styled components and it gives the class name, handled by the runtime. It is more readable and somewhat configurable as well. But we have the component name and module and then our unique string appended to it as well.
And that is what scopes it locally and prevents it from leaking out.
So a friend is saying, why don't we have both, and jumping the gun in this case a little bit.
CSS modules can also handle global CSS, or not, I guess in this case.
So it is possible with the same component here, if we just have a projectcard.CSS SaaS partial, we can import that in the component and if we import like this, it is global. All of them are applied globally and in the render method, we can pass in the string of the class name and the end result is going to look the same and all of the styles are globally and no longer scoped to the component.
And this opens up interesting possibilities, cases where you have styles that want to be shared across templating engines. This is based on an ongoing project. I will talk about it at a high level. It is a progressively decoupled Druple project I'm working on, and we did find the situation where there's essentially an authenticated user portal and other sites traditionally Druple and there are global styles to apply in both cases, typography, space and scale, stuff like that. And so we are looking for a way that we could easily share that stuff and use it in both developer contexts. So Druple uses Twig and has its own styles and components, React has JSX and we are looking to handle the overlap. What we ended up doing is essentially having ‑‑ using CSS modules allowed us to let each of the pieces of the stack to use the SaaS partials. We are umporting everything in JavaScript, it is a mix of global and component scope. The global things are imported globally and other things at the component level and there is project‑specific CSS. And both build processes are using the SaaS partials, on the Druple side, we can do a traditional SaaS build. We are creating a file with the shared design system and also things that are specific to Druple, and we need to list out the SaaS partials we want to import from the main SaaS file.
So we can have our peanut butter and chocolate in this case and it is working well in the project so far. For people that have come up in a certain age, they might have the concept of the separation of concerns drilled into their head, I know it was mine, and JavaScript and CSS and html are separate things to isolate, and at the end of the day to work together. And taking a component approach, the separation of concerns changes a little bit, it is a different view. This is a different image I created, and at the component level there, if you look at the button, or the date picker or something, it is encapsulating the CSS, JavaScript, and html you need for that thing and to render that component. And it is still a separation, but it is a different view on that. And both can be valid, but the way that I have been building front‑end components, it is component level. This got the idea ‑‑.
And preprocessors are in CSS, custom properties is a big one, something that SaaS variables can do. And nesting is working its way into CSS as a whole. And in the Druple world, there's popular config projects, into core, JSON an example of that, the panels ecosystem, and even the new core admin and default themes are also starting out in contrib and working their way into core. The best example of that is in JavaScript itself. So writing ES6 or any flavor of modern JavaScript, we are using a transpiler like Babel so we can write using the new JavaScript methods and things like that, but it will still be transpiled in a way that it can be used on a particular target set of browsers.
In general, over time more of the things that you have to be transfiled are becoming supported until the browser, and what Babel can do evolves, keeping up with the newer things in JavaScript and over time, a lot of that stuff trickles down to native JavaScript as well. So could the same thing happen for scope in CSS?
So there actually was an attempt at this a while back, the html scope attribute was within the spec. As you can see from the screenshot, it was not widely adopted, very briefly in a time period of Firefox's life. And interestingly enough, it is the scoped attribute that view has adopted as their way to dictate the way CSS is in a book. And as people continue this approach and want this level of control, something like this can be revived in CSS at large.
And can something like web components, or the shadow DOM, be a solution for this? So the web component for things using shadow DOM, scoped by default, and there are ways to handle things globally as well. But it is still requires writing a decent amount of JavaScript to create the web component. So I don't know if that necessarily allows you to have the control of scope with less JavaScript, as they increase the adoption, this becomes the way that people find this component scope for CSS in the native language.
I can predict the future here, only Yoda knows the future of CSS. Again, I would love to be able to have that level of control with CSS all of the time, and dictate when things should be global and have certain things that are only within the scope of a component.
So pretty much wrapping up here, and also definitely want to plug the contribution day, the virtual contribution day on Saturday. So I will definitely be around if you want to chat about this stuff on Slack or the internet or whatever, but trying to work on issues with the (indiscernible) thing, exciting and coming along.
So definitely stay where you are, and join for the contribution day.
Thanks to everybody, and thanks to our friend, supporting me all the way along. I think we probably have a couple of minutes for questions, if you have questions, or we can continue the conversation virtually, too.
>> Um, so we have 15 more minutes in this room scheduled for questions, Brian. And we did have one early on, I have been monitoring it, I didn't want to interrupt your restarts for your talk. It was from Genille, how do you avoid redundant CSS code in the view approach, are there views for general approach CSS files?
>> Yeah, I don't use view much myself. So I don't know what the typical convention is there within view. Because you can have that control over scope, you can certainly have, it might be a component, or the app root component that imports CSS that is global. So I will look this up, because you have control over scope, it is definitely possible.
>> Awesome. Anyone else have a question, feel free to use the chat to communicate to us, or raise your hand. Or if someone wants to be brave and unmute themselves, yeah, the hand‑raising function. That is ‑‑ you are on the air, John.
>> Long time listener, first time caller.
>> Hey, John.
>> Ha, the one method you talked about with the funky CSS names, is that a trend that you see coming up more that the names are unique?
>> Yes, and it is pretty common within CSS and JS components. So motion and everything that takes that approach, and it varies a little bit. And so CSS modules, it is more readable and has the string that makes it unique.
So as CSS and JS gains adoption, that will become more of a thing, which I'm guessing can be problematic, maybe from an analytics perspective.
>> And personalization has lots of downstream effects. Are they consistently garbage, every time you compile, do they shake it out?
>> Yeah, that's a good question.
>> Sorry to put you on the spot.
>> Because it is a runtime bundled with the JavaScript, they are not consistently garbage. The one thing I would say, trying to think of ways around that, I believe it would be possible to provide your own class names, though they might not ‑‑ either a class name, or a prop in a component. So if you need something where you are trying to trigger based on mark up or logic, maybe it does not control styling. Kind of funky, but seems impossible.
I see another question ‑‑ (speaker far from mic) ‑‑ special considerations for how you author the partials? Not really. I cannot really think of too many cases, so the only thing I can think of that I have seen, there are cool things you can do with Webpack as far as exposing variables in your SaaS to JavaScript. So we have done that with break points, and you can make the break points from SaaS partials, things that are automatically JavaScript variables. So in another part of the build process, if you are not using Webpack, that's the only thing you can think of. In most cases, it is traditional SaaS or CSS.
>> All right. Anybody else have a question? Don't by shy. This is a chance to pick Brian's brain on the spot.
>> I am definitely around if questions come up, or FEMA want to chat and definitely thanks to everybody for being part of this virtual camp, it is super exciting.
>> It is, a lot of thank yous coming in. Feel free to unmute yourself and give applause here. We will take a second to do that.
[ Applause ].