What’s in the box?
(upbeat music) - [Alex] Hello everyone. Oh great, the mic's working. My name's Alex.
As mentioned, I'm a principal engineer at Atlassian. And today what I'm going to be talking about is the CSS Box Model.
That most people have a pretty broken understanding of what it is and how it works.
I think it just adds to a lot of confusion in this space. So let's get into it.
So when a browser views your webpage, it just sees a series of boxes.
And if you open up your developer tools, you go to the elements pane, you'll see this little box here. And I'll highlight it. And that's the Box Model for a given element so when you select a different element you'll see different values populated here. But let's dive into what the Box Model is.
Okay, so this is the first component.
This is called the content, the content box. And this is where the real content is called for an element sit.
So this is things like text, image, video, it all goes in this box.
That's known as the content box.
On the outside of that, we have padding.
Padding is spacing that sits on the inside of the visible edge of an compo- of an element.
And that box there, everything kind of the padding and down, is called the padding box. On the outside of padding, we have border.
And this is the visible edge of an element. And that box and everything inside it, is known as the border box.
This is a really important one, we use this one all the time.
On the outside of the border box, we have margin. So margin is spacing on the outside of an element that is relative to other elements on the page. Margin has this, sort of interesting behaviour where, in certain circumstances, the margins between two elements will be collapsed. Now the rules for this are fairly nuanced, so I won't go into a great detail about them here. But if you just type in m-d-n margin collapsing, they've got a great guide there, which you can look at if you want to.
Coming back to this box, if you have a scrollbar, that will sit inside of the padding box of an element.
Which kinda looks something like this.
This is sort of a way of looking at it.
So in this case, I've set overflow:scroll or could be in overflow order and what's happening is that padding box and the content box are being cut when it's sort of reached its maximum level height. So the padding box gets clipped if you set an overflow to scroll or order.
Cool, uh Box sizing.
So, if you have an element and you set a width or a height, or some other dimension-related property on it, by default, that's targeting the content box. So if you say, "I want my element to be 100 pixels wide," and then you put a nice padding and border around it your element's actually going to be wider than 100 pixels. 'Cause that 100 pixels is targeting that content box. Now, you can change what box those properties are targeting by changing that box-sizing property there. Which is probably more what you expect when you're actually setting, like, widths and heights and things.
That's just something to be aware of, some CSS resets will will actually just set everything to border box, but yeah, just something to be aware of. Cool, there's also this concept of borders that don't take up any spacing in the browser, so these are things like outline and box-shadow. And they sit outside of the visible edge of an element, but they don't actually push anything else away, they don't add an extra space to the document. The Box Model can run in lots of different modes and they're controlled by using that display property there. And these control how the padding and margin works on the element, how the elements flow and how they kind of relate to their siblings. So some, like, pretty classic ones are block, inline, inline-block.
There're also loads of other ones like table, flex, grid. These ones here control the spacing of their child blocks. And there's lots of other ones as well.
And these kind of control how these elements all interact with one another. Cool, so that's the Box Model.
We'd need to know some information about this element to be able to do this.
So this is just a few different examples and there are lots of other ones out there. Where you may need to know different aspects of the Box Model and what those values are. So let's take a look at how to actually get some of this information.
So here's our beautiful Box Model that we've seen before. The first API that we have available to us is offsetWidth and offsetHeight.
And what these do is they return the borderBoxWidth and borderBoxHeight for an element.
I think the naming of these APIs is pretty bad and I'm gonna list what I think is, like, a more sane API name underneath in grey there. So this simply just returned the borderBoxWidth and borderBoxHeight.
After that we have clientWidth and clientHeight and these are just simply borderBoxWidth and borderBoxHeight.
After that, I'll probably post this on Twitter so you can grab it if you want.
I'll pause for a sec so you can take a screen shot of the whole thing when I'm done.
Is scrollWidth and scrollHeight and I mentioned before that the border box can get clipped if it has a scroll on it.
What these two values here will they will give you the full height if it wasn't clipped.
So the alternative name could've been unclippedPaddingBoxWidth and height.
Uh there's no APIs for content box and no API for margin box so, yeah, if you wanna take a photo you're welcome to. I wish I had this, like, a year ago, three years ago, five years ago, probably longer.
Would've saved me a lot of time.
Anyway, so there's the one.
So those are some APIs.
Now they return sizes, but not coordinates. And all of these values that they return are rounded so they will round, the browser will internally store such things in fractional units often and this will round it to the closest pixel value. So what if we wanted to get coordinates for our element? Well we have this pretty cool API here called an element prototype called Element.getBoundingClientRect(). And it returns the coordinates and sizes for an element. But the question is, which of these boxes does it give us? Well, we've seen this sort of word called client before, it was referring to the border box, borderBoxWidth and height.
So maybe it's gonna give us back the padding box? Uh and no, it doesn't do that.
It actually gives us back the border box.
Okay, well maybe it returns a ClientRect, whatever that is? Eh, no, it actually returns a DOMRect.
To be fair, a DOMRect and a ClientRect are pretty similar. So, a ClientRect has a top, right, bottom, left, width and height.
And a DOMRect just adds x and y to that.
X and y is actually redundant it's the same as left, top, but, anyway, they're in there.
So we have this API, it's actually super powerful it will return fractional units so they're not rounded, its like a raw fractional unit.
It accounts for any transforms, so if you have a scale on it or if you're sliding it somewhere, it will know, like, it's actual live size.
It accounts for any scroll in scroll containers. But I guess something to be aware of is that returning positions are relative to the current client viewpoint.
So if you needed those coordinates relative to the entire page, you would need to shift those values by whatever the current window scroll is in order to get it's actual on, like, a full document. Cool, so this returns us the border box.
But what if we wanted the margin box, the padding box and the content box? Unfortunately, there's no direct APIs for these things we sort of have to go about them a very roundabout way. So, there's another API called window.getComputedStyles(element).
Not super sure why this one is on the window element, sorry, window object, and the other one's on the element prototype.
So this one's on the window and you pass near an element and it spits back a CSSStyleDeclaration.
And this is a massive object, which contains all of the computed style information, as the name suggests, about an element.
So it'll have things like your boxSizing, your colour, but it will also have all of those nice spacing things that we care about.
So like marginTop, marginLeft, paddingLeft, borderRightWidth and so on.
And this will coerce them into pixel values even if they were like EMs or some other value. So, what we can do then is take this object here that we got back, pass it through a function, toSpacing's just a name I made up, pass in that style and get it to spit back an object that we can work with.
So, in this case, it's the margin object with the top, right, bottom, left values so, awesome. So, now we can use these objects to generate the margin border and padding for an object. Okay, bear with me, so we now have the border box so we've gotten this from Element.getBoundingClientRect() and what we can do is we can expand that by the margin object that we just computed to get the margin box.
We can shrink the border box by the border to get the padding box, and we can shrink the padding box by the padding to get the content.
It's pretty wild, but by using those two APIs, you can get all of the different boxes that are associated with an element.
So, what can you take home, like today, from all this stuff, all this kind of information dump? So first up, I hope you have a bit more understanding about the Box Model, like it's actually not too overly complicated.
And just looking at like this and the different terms kind of associated with it. I created a library called CSS-Box-Model.
Anytime you're using like .getBoundingClientRect(), you may wanna consider using this one instead. So what it'll do is it gives you a function, getBox, and will spit back a big object here so it has properties marginBox, borderBox, paddingBox, contentBox, and border, padding, margin.
And those boxes are, I call them like a rect. So, instead of DOMRect and ClientRect just dot rect, just to add more confusion, but those are the same as ClientRect and DOMRect, but also add a centre 'cause like centre is a super useful piece of information about a box, pretty easy to calculate, but, anyway, that's in there.
And then, also those as well so you just have access to those.
It also has another export called withScroll. So I mentioned before that the .getBoundingClientRect() only considers where it is in the current viewpoint, if you want to you can use this API and adjust it for where it is on the page.
So you can get like it's true coordinates relative to the entire page, not just the current viewpoint.
And so, yes, be aware that there is this craziness that you have to deal with, but also, I think, at a high level, like think about the applications and systems that you are writing and it's so often to be naming things that kind of, to look at the same problem through different lenses and come up with different names or slightly different abstractions, and this adds to friction and makes it harder to understand kind of the core building blocks. I think the web is kind of a bit stuck in that still, for backwards compatibility reasons, we can't really change things but maybe in your software you can think about how you could be realigning these kind of concepts to make them more cohesive 'cause when you do that it's surprising how easy things become to actually understand what's going on.
Cool, that's it. Thanks, everyone.
(applause) (upbeat music)