 Several years ago, researchers did a study on running shoes. They gave one group of runners shoes that had firm midsoles, not a lot of padding. They gave other runners shoes with thicker, softer padding. The thinking being that the cushioning would protect the runners would result in fewer running injuries. So they tracked these runners for the course of about six months. They got to the end of the study and found absolutely no difference. And they were puzzled by this. They thought, this surely should make sense. So they brought the runners in, put them on a treadmill, video recorded them. And what they found was the runners with little padding under their feet naturally landed really softly on their feet. And the other group, the runners with the soft squishy shoes stomped hard with every step. They were doing it subconsciously. They had no idea that's what they were doing. And they totally negated any benefit that the padding was supposed to offer them. I think when we approach CSS, we do kind of the same thing. CSS is an abstract language. And it offers a nice abstraction layer between us and the browser that can make a lot of good decisions for us. But we, without really fully understanding that abstraction layer, without really fully understanding what CSS is doing for us, try to push through the abstraction layer to get to the bare metal. We try to find something concrete. And we want stability. We want to know exactly what's going to happen in the browser, rather than trusting the system to do its thing. CSS allows us to write a rule set, a set of styles that the browser will later take to take unknown content and an unknown medium and put it together in the browser. And we don't have to know whether that's going to be a smartphone or a desktop computer. We don't have to know whether that heading is going to be two short words or a long, complete sentence is going to line wrap several times. I'm Keith Grant. I come from Atlanta. I work at Intercontinental Exchange. You've never heard of us, but we own the New York Stock Exchange. I work on the website team, where I'm kind of the resident CSS guy. I also do stuff and react and node. And I'm the author of the book, CSS in Depth of Manning. It will be in print in the spring, but it is available for early access now. You can get early chapters and get updates as I write. If you want to follow along with the slides, they're up on my website. There's a link at KeithGrant.com slash talks. I'm mostly going to be talking today about M's. Who here has tried working with M's? Who here did not have a good experience? In some hands. Who here kind of had an OK experience? Who here just still uses pixels and doesn't bother thinking about it? OK, I didn't admit that. It's OK to admit that. M's are frustrating. And the reason they're frustrating is because they're part of that abstraction layer. They're a really important tool that the browser provides, but if we don't really understand what they're trying to do, they can kind of get in the way and trip us up. So an M in CSS is equal to the font size of the current element. So the box on the left here is 1M high by 1M wide. And the box on the right is 1M high by 1M wide. I put the capital letter M in there because there's kind of a myth that it's related to the width of the capital letter M, which you can see is not really the case. It maybe used to be the case with old fonts centuries ago. But where M's really get tricky is when we start using them for font size. Because an M is defined in terms of font size of current element, we surely can't define font size in terms of itself. So the browser has to do something different. It has to base the M on the inherited font size. This means if a parent element has a font size of 16 pixels and you give a child the element of font size of 1.2M, the browser has to multiply them, resulting in 19.2 pixels for that child. This means font size is different. It's kind of a special snowflake when you use M's. If you give a bunch of properties 1.2M on the same element, assuming a 16 pixel inherited size, it will compute 1.2 times 16 pixels for the font size, giving you a font size of 19.2 pixels, which then in turn becomes the new local definition of an M. And that value is used to compute 1.2M for everything else, which comes out at 23.04 pixels. This is quirky. We can live with it. It's a little weird if you're not looking for it, but what really starts to trip us up is when we put M's inside of M's inside of M's. We nest our elements deeply. If we have a container with font size of 16 pixels, a child with font size of 1.2M, the browser multiplies it. We use M again for another font size inside that. The browser multiplies that and kind of the scratch our heads a little and do a little math. And if we keep nesting it, it just gets completely out of control fast. We have no idea what an M is anymore. We totally lose track of it. You can see a similar problem where if we, I don't know if you can read the red there. That's a UL selector. It's targeting a list. We give it a font size of 0.8M. And then if we nest the list within a list, well that selector targets each and every list and each one starts shrinking because it's 80% of its parent's font size. Of course, the solution to both these problems is the REM unit. The REM is similar to an M, but it's short for root M. It is, instead of being relative to the inherited font size, it's relative to the font size of the root element of the document, the HTML element. Now, if we use a REM for our font size, a list with a 0.8 REM font size, it's always the same because it's based on the same value. So if you're just getting your feet wet with M's and REM's, if you've been using pixels and you're thinking about making a switch, this is kind of my starting recommendation. Use REM for font size because it's predictable. Use pixels for border width because usually you just want a one pixel border, you want a nice fine line and that there's some, it can get weird if you use M's for that. And then use M for everything else. Well, with one exception, line height. I didn't put it up here, don't use any units for line height. They inherit funny. If you do, if you want to see an example of that, see me afterwards, but basically you're safe with M's. So REM for font size, pixels for border width, M for everything else. And I'll show you some examples of how to really capitalize on this. Now, you've probably heard this rule that you should use relative units for your font sizes. Relative units meaning M, REM, couple of the less common ones like CH or EX. And this is an accessibility rule. It's an important rule because if the user goes into the browser settings and they need a larger font size so they can read the font better, they can bump up the default font size. If you use pixels, you cancel out their option. You override it. But if you use M and REM, the browser will make it relative to their settings. Now, you could get into a debate about whether this still matters because browsers are moving away from the default font size and they're offering the zoom feature which will zoom font size in pixels. It will zoom other things on page like images. I would argue that browsers are doing that because we're not very good at following this rule. And I would also say that browsers such as Chrome still do offer the default font size. So I think we should do the responsible thing and follow this rule. But I'm not here to talk about accessibility. This rule might be what gets you to start dipping your toe in the water of M's and REM's. It's what got me to start toying with them. But if that's all you ever get out of them, if you just compute, okay, I want this many pixels and I'm gonna convert it to M's, then you are missing out on a lot of things that you can get out of M's. I argue and I will show you that if you favor the use of M's and REM's over pixels, and you do it consistently, you can have code that is more accurate, that is simpler and is more flexible. Which is a very good thing because it can mean your site responds appropriately. You are trusting the abstraction layer that CSS brings and allowing it to make some decisions for you that you wouldn't be able to do on your own. So this is an important formula. When you're working with M's, you've probably seen it. If you know the pixel values you want and you wanna convert to M's or to REM's, you take your desired font size and you divide it by the base font size. So M's, that's the inherited font size. REM's, that's the root font size. If you want a 14 pixel font and you are inheriting a 16 pixel font, 14 by 16 gets you 8.75. If you're on a Mac, I just hit Command Space and type it right there in the spotlight and it's nice and handy and quick. If you want an 18 pixel font size and you are inheriting 14 pixel font size, you can divide 18 by 14 and get 1.2857. This gets really tedious. And if you are doing this all of the time, you are gonna start to dislike M's and you're gonna really start thinking that pixels are a more inviting thing. You need to know this formula, you need to have it on hand, but if you were to find yourself doing it all the time, I think we, because I've been in this boat, are not leveraging M's properly. We have all these tools for calculating an exact pixel amount or exact M amount to represent an exact pixel amount. There's this website where you punch in a base font size and it gives you all these M values to get all these results. We have mix-ins and SAS and less that convert our pixels for us to REM. And I have to ask why? Why is it so important that we have an 18 pixel font size for a subheader? Why is it so important that we have precisely 24 pixels for that page title? Why must the footer text be eight or 12 pixels? And I think if you dig deep enough, the answer to that question comes from this book, The Elements of Typographic Style by Robert Brinkhurst. This was published in the early 90s and it is the Bible of modern typography. And he says, when you design with type, you should use a scale. And he gives this as an example. He says, you should start with small font sizes and it should grow increasingly larger as you go up. And he gave this illustration as an example. And I think we inadvertently, as an industry, took this example and said, perfect, there's our font sizes, 16, 18, 21, 24, those are magic numbers and we implement, I'd say 90% of our designs use these numbers as if these magic numbers are what Brinkhurst was talking about. He wasn't talking about these numbers, he was talking about the relationship between them. He was saying there should be some sort of mathematical scale from one font size to the next so that larger font sizes space out more and more, smaller font sizes are a little closer together and this is how we should approach typography on the web. This website, typescale.com, in the selector here, you can choose a different, what I'll call, scalar. So in this example it's 1.414 and you multiply that up to get the next, you multiply it by 1.414 again to get the next font size, you keep multiplying it up, you divide it to go down. And you can choose a different font size and this website kind of draws parallels with the musical scale. I don't know how much credence there is to it but it sounds cool to me. I think the point is that there's a number. What I'm getting at is this is how we approach design. This is the status quo of design in our world is we, whether it was Brinkhurst back in the day or us, our designer now, take a scalar and we compute a bunch of pixel font sizes and then we go over to our CSS and we reverse back out the exact same math to get back to an M value. We don't need this middle step. It's irrelevant what the pixel font size is if we just use a scalar and stick with it and let's watch what happens here. So we'll choose a scalar of 1.25 and we'll multiply it up a few times and multiply it down a few times. Now, whatever our current font size is, if we multiply by 1.25, it gives us to the next size. If we divide by 1.25, it gives us to the next size down or you can invert that. One divided by 1.25 gets you 0.8. Instead of 14 pixels, 18 pixels, 24 pixels, being your magic numbers, these are your new magic numbers. Pick a scalar for your design. Multiply it up a few times and multiply it down a few times. I guarantee you you'll commit these 1.25 and the 0.8 to memory within the first day on the project. It's just two numbers. The next few steps, it may take a little longer to commit to memory. Stick them on a post-it note. But if we do this, the browser does the work for us. We don't have to spend time dividing and multiplying things to figure out exact pixel values because it doesn't matter what the exact pixel value is. And the amazing thing here, when I talked about the problem of nested M's, don't nest a lot of M font sizes deeply, but on this scale, you can take any one of these numbers and multiply it by any other one of these numbers and you'll still be on the scale. If you stick with these numbers, you will always have a font size that looks good in your design. So don't micromanage it. Let the browser do the work. If we just use a scalar, call that the magic number. It will take care of a lot of these problems for us. Now, I have to acknowledge the real world here for a minute. If you're in charge of everything from conception, design, and implementation, this system is pretty nice. But in the real world, you gotta work with someone else who may not be familiar with this approach. You might be, it might be a contractor, it might be someone else on your team. And it does get tricky dealing with that because you will wind up having to do some conversion and figuring out exactly what's going on. I wish I had a magic bullet answer to this. But I do think one of two approaches makes sense. Either you can just suck it up and do the conversion yourself. This might make sense if you're working with someone short term on a contract. If you do the conversion, what I have found is that early in the project, there's a lot of this. You'll do the math for several days, maybe even a week. And you'll get your font sizes in place in CSS. And then you'll find that the workload of that math really drops off. You won't need to do as much conversion. Or if you're working with someone long term, someone on your team, bring them on board with the strategy, sit down, hash it out early on, and just use it as your terminology on the team. Then you can forget about the pixels. So that's font size, which is kind of what got us into M's in the first place. We wanted to be accessible. Like, get us, that's our accessibility. But I think we can go beyond that. I think M's are incredibly powerful if we start using them for other properties as well. And I'll show you some tricks. There was actually an article in CSS tricks.com a couple weeks ago that's very similar to this. I'm gonna modify his method a little bit and you'll see why at the end. But consider designing pixels. This will have a tile module. I'm gonna use this max terminology, but if you're familiar with BEM, a block. And this module is padding, border radius, margin, font size, everything defines in pixels. It's got a sub element which has a smaller font size defined in pixels. And if you wanna scale this down or up, you have to get in there and you have to recalculate probably using some sort of scalar, a smaller padding, maybe a smaller border radius, smaller margin, smaller font size. And then you gotta choose each sub element and readjust their font sizes. So let's do it this way instead. Let's save some trouble. Let's take the top level element of that module and we'll establish its font size using REM. It will be our one REM declaration for that module. And having used REM, we now know what an M is locally. So if we use M's for the sub elements of that module, we know what they are. And the computation is only one level deep, two levels deep in a really complicated module, but you should really strive to avoid that. So we'll use one REM value and a bunch of M values for everything else in between. Reworking that tile module looks like this. We have a padding of M's, 0.6 and 1.2 for vertical and horizontal. We've got a border radius in M, we've got a margin in M, font size in REM for the top level element and a font size in M for the sub element. Now, to scale this, we've defined everything in this module in terms of this one REM value. So all we have to do is change that one REM value and it changes all the other values. It's kind of like reactive functional programming for CSS. Everything just responds to one value. If we want to make it large, we can make it large, everything responds. So again, we use REM to establish ourself in the global font size. And then we use M locally based on that REM. So here's that tile again, you can see a live demo. And I've got buttons here to rescale the thing. And you can see if you look here at the browser tools that I'm not cheating, that's the wrong property. It's the wrong element. There we go. If I change the REM value, everything scales cleanly. This is kind of fun to play with actually. So we'll do it again. This is a really powerful pattern. Let's do it with some other modules. Let's do it with the drop down menu. This drop down menu, I've used the border trick to draw a triangle. I've done it with the after pseudo element of the button. So it's absolutely positioned. It's positioning is an M, top one M, right one M. And the border size in this case is one M because that's what determines the size of the triangle. We see a demo here. We can make it small, we can make it medium, we can make it large. And because we used M's in our positioning, the position of the triangle is always right. The size of the triangle is always right. And again, we can just play with the value here. Even better, if we open that menu and play with the value, it still works. It scales perfectly because this element, the drawer that we've revealed has a top position of 2.1 M. 2.1 M is the size of our button. It's the line height, it's the top padding and the bottom padding. Here's a different example. This time we'll use images. The image is sized in M. We can make it small, we can make it large. And because SVG is awesome, the image always looks nice and crisp no matter what crazy size we scale it to. I can find the right element again. The other thing here I've done is the vertical alignment is in M. Now, the vertical line used to drive me crazy because I would use top and bottom and center and baseline and it was never right. Until one day I realized you can put an arbitrary length in the vertical line. If you use M for vertical line in an inline image like this and then you scale it, however you scale it, it just works. One last example, this is less of a module more of just kind of global style but I'm gonna do an effect here with links on the page. So instead of just a normal underline I'm gonna use a transition on a box shadow to make that underline when I hover the mouse over it grow and kind of emulate a background image. And of course I've used M in the inset box shadow. If we see the demo, this is what the effect is. We make it large and it just works. We can put a link now in the heading, we could put a link in a footer, we could put a link wherever we want in the page because we've defined the size of that box shadow. In terms of the font, it's always the right size. Use this pattern throughout your page. Use REM to establish yourself back into the global font size so that your font size locally is stable and predictable. Use M within that and you can nest modules within other modules and because they each reach back up to the REM value it doesn't matter where you place them they're gonna be sized predictably. Now you might be thinking that's cool, I need big and small buttons but I don't need everything in my page to scale. I don't need a large and a small version of the footer. Actually you do because if we've defined everything on our page in terms of one value, the root font size all we have to do is change that one value and the entire page will respond. Something like this. Root which is also a selector if you're not familiar with that. This targets the HTML element. We give it mobile styles. Point 8M on the root element, medium break point, font size of 1M, large break point, font size of 1.25M. Now everything on the page, here's a sample page I stole from my book. Everything here is defined in M's and REM's which is in turn, oh there there's a break point. So you can see at the break point the root font size changes and everything else changes with it. The heading gets smaller, the buttons get smaller, padding's adjust, go down to our small break point. Did I pass it? I missed it. There it is, same thing. Everything scales which is pretty slick but that break point's annoying. So let's get rid of that. We can use viewport relative units now. If you're not familiar with these, these four units are in terms not of a font size but in terms of the browser's viewport window. And specifically we want VW, percentage of the viewport width. Now we can do something like this. Throw away the media queries, we don't need them. The font size on the root element is now 2% of the viewport width. See what that looks like. Same page. Now, there are no media queries in the CSS. As I resize the page, everything resizes with it. It's pretty slick, it's fun to play with. Unfortunately, it's a little too responsive because you get down to mobile size and it's hard to read even on the big screen. And you go to very large screens and it approaches something along the lines of 24 pixel font size on a 1200 pixel screen which is obviously too much, it's more than we want. So what we need is to soften the effect and we can do that with calc. Now the root element will establish the font size as a blend of an M value and a viewport width unit. So that 0.5M serves as sort of a theoretical minimum font size for the page. Now, even if we were to impossibly shrink the browser to zero, it will be at least half of the browser default. So eight pixels minimum, add a few pixels to the actual 200, 300 pixels of a mobile device. Now, it's responsive, but it's not quite so extreme. Now it shrinks to a nice readable font size and it grows to a larger font size but still appropriate and responsive. This is a really cool pattern. We've scaled the whole page with no media queries at all. Now, is it perfect? Are you all the way to a responsive design? No, you'll need to come in and touch some things up. We need to line break these things and you could do that with a flex box. Maybe we want the columns to stack or you could stick a few selective media queries where you want, but if you use this pattern, you'll find you don't need nearly so many media queries throughout your style sheet. You won't need to create a media query to tweak the padding a little larger on a large break viewport. You won't need one to kind of make sure that this is a little smaller on small screens. All that little fiddly stuff will kind of naturally happen and you can still come in, use some media queries and touch up the rough edges where you want. If you are still trying to figure out how many pixels this text is, you're gonna have a rough day but the whole point is it doesn't matter. If you use a scalar math on your font sizes, they will all be the appropriate font size. Your scale works at any scale. So what we've done is we've created a consistent hierarchy of our font sizes on the page. We have the REM value up at the top. Selectively, so the root font size at the top, selectively on modules throughout the page we use a REM font size to go back and establish ourself and then leaf nodes inside of each module are defined in terms of the module's root element. And what this allows you to do is to get in there at any one point in the tree, fiddle with the value and have its effects just automatically ripple down the tree. Nesting M's is chaos when there's no system but if you use a system like this, keep it structured, know how it's supposed to work in your design and you don't have to use my system exactly but something that you know you understand, something that you can keep control of and it gives you a very flexible design. So jump into M's. I found that trying to kind of do it halfway with some pixel values and some end values just makes a mess. They just really don't blend well because pixels don't want to change and the M's want to change. So to go for it, if you're nervous, pick a side project and just use M's everywhere and M brands, I say M's collectively but I think it provides us some really cool things that we can get out of our pages. It shortens our code. You don't have to override so many values if we just tell the browser, hey, respond to this when I ask you to respond and it will. So that's all I have. Oh, the astronaut got cut off. There's an astronaut right here. But this is my book. The book didn't cut off so that's, I guess that's good. It is, here's code right now. This is good for the next week or so. That will get you 50% off. The first three chapters are up at Manning.com. More will roll out as they go through the editing process. It should be finished in print in the spring. And I cover all sorts of stuff. So this was basically an extended version of my chapter two where I dive into M's and that sort of thing. But I also tackle fundamentals that developers typically miss. Really understanding the box model and not to set the height on things. And stuff like that. I spend a large section of the book talking about layout and then I look at code organization methods, working at scale, very large projects. And then just some more advanced stuff like introductions to animations and transitions and that sort of thing. Thank you for listening. And thanks to these people for helping out. Here's a question that came to mind for me. How do you propagate like scale or thinking throughout your teams to avoid the like three pixels here, 12 pixels here? How did you get, how do you do that? I, well I cheat. Cheat? I am the CSS guy on my team and I wrote 95% of the CSS. But I did have to work with the designer. We had the designer put together our design. And so there was just, and I hadn't, these thoughts of mine hadn't fully matured by that point. So I was still doing a lot of the division. The compute this to get this, compute this to get this. But I did find that if I just took their pixel numbers and did the math that the workload of that arithmetic really did drop off pretty quickly once things were just kind of established. So are you using like any SAS functions for it or like modular scalar tools? You're just like, I'm just gonna think this way. These are my numbers. The code is simple. I mean you can obviously, you know, you stick those scalars into a SAS variable or something like that. Then you don't have to commit it to memory. You know, just call it up one, up two, up three, down one, down two. I don't know, something like that would be helpful, yeah. That's great. You're lucky you're the only person. I find it gets really challenging when you have bigger teams or just that, like one thing I've had to learn is just patience. Like with our designers at first it was like, no, we don't wanna use a scale. And then now the designers are like, oh, I can't believe, like we did it any other way. I think there's just, I think there's just an awareness needs to get out there. It needs to be seen as a valid approach. I mean, I saw that TypeScale website ages ago and the ramifications of it didn't really click until much later. Well, do you think that, I mean for a designer, because most of us is front end, we're like usually getting our designs from a designer and they've done this thinking and we've come from the like measure everything. But their tools don't think scaler. Like they don't think that way, right? Do you think that is probably the disconnect? What I did on our websites is if you open up your Chrome DevTools and resize the window, it'll show you the pixel size up in the corner. And so I would make sure that my window was precisely the size of their mockup. So they gave me a mockup for a 1200 pixel viewport and I said, okay, I set it to exactly 1200 pixels and I just did the math to make it match at that size and let the rest kind of fall into place in between. Very cool. So we have some questions. So someone asked, what should we set the line height to then? You can use the unit list number. I don't think I said that, but just, I don't know if you're looking for the exact number whoever said that. I usually do 1.2 or 1.5. If you set an M or any unit really, if you say your line height of this element is 1.5M, what the browser will do is it will compute it for that element. So it'll say 16 times 1.5, which is whatever. But that computed value will then be inherited. So if you change the font size of the children, they'll still have the same line height as the parent. But if you, I don't know if that made sense. You kind of just have to play with an impressive C. But if you assign a unit list number, the unit list number will inherit. And so the line height will be recomputed for each and every child element. With the same. With the same ratio. Yeah, if that makes, I hope I explained that okay. Hopefully it makes sense. The person who asked it, if you want more details, come find Keith after. You'll be at the party, right? I'll be at the party. There we go. Another question was, how do M's and REM's work and web components? Same. Meaning I'm doing React components? Is that kind of, or? I used to tell they work. My guess would be the same, but it was awesome. Awesome. Yeah, I don't know. I'm not quite sure how to interpret that. Like in a React component, I use, so I would use my module, my CSS module, of, you know, as a base element. I would just use that as kind of the root element of the React component. So the component has self-becomes. Right, so the component should ground itself back to a REM value. So that whatever the top level element that component is, you know what the establish font size is. And then, you know, you're safe to use that. So you're not setting it on the whole document, you're setting it on a component level when you're doing it that way. That makes sense. That makes sense to me. Speaking about React and things, I was taking a peek at your blog beforehand. And you've got an article called Into the Future of CSS where you talk about this idea of like scoped CSS. And I was just wondering how, you know, just bring up some more controversy. I was curious what your thoughts, like if you could talk about that a little bit, what your thoughts on scoped CSS are and this whole. Yeah, scoped CSS is a spec that is now dead. It's a done deal. I tried to raise a big stink and Tab Adkins was like, no, it's over, give it up. The thinking was, and if you actually look at the candidate recommendation of the spec for the cascade, scope is in there. It's just that there's no way to define a scope. The only way we can actually define a scope is an inline style. If you think about the way an inline style works, it's a small scope applied to one element. And regardless of selector specificity, it will override styles out of it. So the idea with scoped CSS was that you could scope CSS to a selector and the inner scope would take presence over the outer scope regardless of specificity. So that decision would happen before specificity went into effect. The net result being that if you design for module A and you style for module A, style for module B, whichever one you put inside the other will get the correct styles. They won't receive styles from the outer element. It'll stay encapsulated. Yeah, so the way forward with that is shadow DOM. So that's really the replacement. The reason the spec is died is because shadow DOM is taking its place, so. So someone that just wrote a whole book, CSS, and it sounds like you also do work with React. Yes. Where, and just also because of your blog and these articles that you've written, where do you stand curiously on this, the fate of CSS? The fate of it? Well, like the fate, but like CSS and your views of it in this component. I think we are having important discussions in CSS right now. The whole inline styles encapsulation discussion. I think it's a really important discussion. I'm also incredibly optimistic that those things will find an answer in the spec someday. That may take a few years. But I think that libraries like CSS modules and inline styles work as a stop gap and they're a great way to experiment with possible solutions. But if you look at things like coffee script, which was this crazy thing that people are like, oh, what are you doing? And then half of coffee script makes itself into the JS specification. Like it's officially JavaScript now. So you use an arrow function to do these things. I think in just the same way, these experiments are really important because they will shape the future of the spec. Absolutely, I love ending in that note. And that's really the philosophy behind the whole extensible web and the web manifesto is to create this to hopefully be that beacon and that informant to the browser. Cool. Well, thank you so much, Keith. Thank you. I really enjoyed your talk.