I’m a big fan of resetting box-sizing to border-box, so much that we have a special day of the year around here. But there is a little adjustment to setting it that seems like a pretty good idea.
Here’s the adjusted version:
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
Credit on the inheritence idea to Jon Neal here, who says:
This will give you the same result, and make it easier to change the box-sizing in plugins or other components that leverage other behavior.
Explaining further, let’s say you have a component that was just designed to work with the default content-box
box-sizing
. You just wanted to use it and not mess with it.
.component {
/* designed to work in default box-sizing */
/* in your page, you could reset it to normal */
box-sizing: content-box;
}
The trouble is, this doesn’t actually reset the entire component. Perhaps there is a <header>
inside that component that expects to be in a content-box
world. If this selector is in your CSS, in “the old way” of doing a box-sizing
reset…
/* This selector is in most "old way" box-sizing resets */
* {
box-sizing: border-box;
}
Then that header isn’t content-box
as you might expect, it’s border-box
. Like:
<div class="component"> <!-- I'm content-box -->
<header> <!-- I'm border-box still -->
</header>
</div>
In order to make that reset easier and more intuitive, you can use the inheriting snippet up at the top there, and the inheriting will be preserved.
It works:
See the Pen Easy to Reset Box-Sizing by Chris Coyier (@chriscoyier) on CodePen.
This isn’t a majorly huge thing. You might already be using the box-sizing
reset the “old way” and never have gotten bit by it. That’s the case for me. But as long as we’re promoting a “best practice” style snippet, we might as well hone it to be the best it can be.
In my opinion there one situation that border-box isn’t welcome by default: img.
Try to add a border to img with “box-sizing: border-box”. The image is resized.
So, I usually add the pseudo class :not() for images:
You could target
img
s directly with box-sizing: content-box instead of using the potentially expensive:not
selector:@sanson, I’m curious. How can the image be resized if the border is created on the outside?
In this Pen you can clearly see that both images have the same dimensions even when one of them has a 10px border: http://codepen.io/ricardozea/pen/anCyq
Am I missing something then?
Would you mind making a demo where we can see what you say?
Thanks in advance,
@sanson, when using responsive images, that you just want to set to
width:100%
of their container, you’ll most likely wantborder-box
… see this modified version of Ricardo’s codepen, http://codepen.io/anon/pen/sijtD – withoutborder-box
, the image with the border “breaks out” of its container element, which is most likely not what you’d want in such circumstances.@sanson is right.
Image gets resized: http://codepen.io/ricardozea/pen/KtFlH (I increased the border width to see this issue better).
In the new demo I changed
html { box-sizing: border-box; }
to* { box-sizing: border-box; }
) and bam! image gets resized.However, this is THE desired outcome if you ask me.
If you add
:not(img)
to the universal selector [*:not(img) { box-sizing: border-box; }
], then you’ll see the image stay at the same size and ‘bleed’ out its container.This saves lives. Srsly
I love the idea of using
:not
selectors. So much that every component I build is styled using a:not(.exclude)
Makes it very easy to override any functionality without having to retrace your steps.
I recently did this in a website where all buttons animate on click. When using the same component in a button group, it was causing issues.
This is where box-shadow wins over border.
Which I actually created a Sass mixin for: http://codepen.io/ricardozea/pen/5d729855e63d404f0dfa46bda3bb56d1/
Truth be told, this mixin uses
inset
value, so if you want to make it work for images, remove theinset
value from thebox-shadow
declaration and voilá, border is now outside.At the end of the article, a bonus point for saying “gotten” instead of “got”, but it should read “never been bitten”.
Couldn’t you just use:
Absolutely you could. You just need to remember to do that, as it isn’t super intuitive (at least to me). And it’s a case of “doing something just to undo it.” Not a huge deal, but like I said at the bottom of the article, if we’re sharing around this “best practice” type snippet, might as well make it the best we can.
I agree, it seems just as easy and shorter over all to do it this way. So unless inherit has some kind of performance benefit over setting every element, which I can’t imagine that it does, this seems more intuitive and obvious.
Although in his specific example, for completeness the css would have to be:
The article makes it seem hard to reset a specific subset of the html to content-box, but the reality is that its simple.
Right by why think about it all when you could just reset the component and that’s it? All the same benefits, simpler resets, not undoing yourself, slightly better best practice.
After realizing that the * selector does not include :before and :after I would agree with you Chris, because now the code would have to be:
to guarantee that you covered all elements within the component. And if you had any special rules like Sanson mentioned you could potentially have to reset those as well under the current standard method.
Thank you for this, it sounds like something to implement in my work.
Christian Longe, you also forget
.component
pseudo elements:A bit off-topic question:
@Christian Longe (uh, everyone else for that matter): How is it that the
*
selector does not include:before
and:after
?* {}
is likehtml {}
, it is a universal selector for the name of the element. Just like there is*:hover
(pseudo class), there is*::before
(pseudo element).I think
.component *
is bad in terms of selector performance, whereas*
and.component
are much better.@PhistucK, I have to disagree,
html {}
is not a universal selector as you seem to refer. As you can read there are no references tohtml
being “universal”: https://developer.mozilla.org/en-US/docs/Web/CSS/Universal_selectors*
andhtml
are two completely different selectors. But that’s not what my question is about.Let me rephrase my question then.
Doesn’t the universal selector
*
include:before
and:after
pseudo elements as well?You either misunderstood me, or I was simply unclear. I meant that it is like
html
in that both of them match element names.*
is a universal selector (means ‘any’) for element names, not for anything and everything in CSS.Calling it “universal” is a pretty weird term, actually. It really means “any element name”.
Here is a question –
Why does
*, *:*, *::* { ... }
not match any element, pseudo element and pseudo class?(The technical answer is probably because it would be horrible in terms of performance and memory, the conceptual answer is probably because it is not very useful, since browsers can add new pseudo elements and pseudo classes that can suddenly show up where they should not and the standard answer is that it was not specified this way in the standards. But no real answer)
@Ricardo The
*
selector only matches elements.::before
and::after
pseudo-elements are not proper elements (they don’t exists in the DOM as independent elements). And the other pseudo-elements (::first-line
,::first-letter
) don’t need to be matched by*
because they are already matched via their parent element. An exception would be::selection
but now we’re just getting into weird edge cases in the spec. (And the only properties that apply to selection, first-line and first-letter are the font-related properties, anyway.)Great idea!
Shouldn’t the
html { box-sizing: border-box; }
declaration come after the wildcard*
selector so that thebox-sizing
value will properly cascade, or ishtml
not included in the*
selector? Like so:html is included in *, but the order doesn’t matter here, it works either way.
The order doesn’t matter. The universal selector (*) has a specificity of 0-0-0 and the element selector
html
has a higher specificity of 0-0-1. Thus, the element selector will win regardless of source order.If box-sizing is inherited, why do we need to declare inherit at all? Shouldn’t
html { box-sizing: border-box; }
be enough?It’s not naturally inherited, it just supports an inherent value that can force it to be inherited.
Ha. I was going to suggest the article be amended to not say that box-sizing was an inherited property. But it seems you already have. ;-)
That clarifies my issue, since
inherit
can be used on any CSS property, whether inherited or not.While we’re at it, what about those *:before and after selectors? How necessary are they?
If you use
:before
and:after
and want to applybox-sizing: border-box
using this method, you’ll have to select them like this. If you don’t, just leave them out.Man, I honestly have not had to go back to
box-sizing: content-box;
after learningborder-box
.That looks like a good suggestion. Perhaps update all of the posts regarding this matter (the border box day post, for example and any other post that uses a similar snippet)?
Hi thanks for the tip. Inherit box size would be a better and convenient way to size.
I don’t think I agree with this idea.
If you create one specific default box-sizing method, you know exactly what to expect further on down the road. It’s predictable and it does not stop you from creating a component oriented towards a box-sizing method that differs from the default if you need it.
Having a default box-sizing method that’s the same everywhere might force you to be explicit if you want to use another property, but that’s okay: Being explicit is a good thing anyways!
If you inherit the box-sizing method by default, this means that, as soon as you change the behavior of one element, all nested elements suddenly behave differently even though they themselves did not change. This is unpredictable and that makes it harder to understand what’s going on if (when) things go wrong.
The problems introduced by unpredictable box-sizing only gets worse when you start nesting components in multiple places, or when you start updating or adding components in the future, or when working in teams where multiple people have to dig around the same CSS code, etc. etc.
If your plugin only works with one box-sizing methodology, you have two choices: Fix the plugin, or fix the CSS. Pick whichever one makes most sense and get it done.
I have to +1
Chris’ (I don’t know yet if this is an industry best practice yet) suggestion is certainly clever and makes sense under very specific use cases, but the more I think about it the more I feel it may suffer from the similar cascading issue of dealing with
em
s.Hmm, I’m not really sure if this is better. Just because you want to make one element use content-box doesn’t mean you want everything inside to use it as well. I don’t think I would use content-box because I want all the elements to live in a “content-box world”. I think I’d be using it for a specific layout reason on that one element. But then again I can’t say that I’ve had much reason to go back to content-box, so I can’t say for sure.
totes cray. nice Chris/Jon
This is an OO solution, a better practice. Cool man!
This idea will not break content that expects the standard box model for all elements (provided it is wrapped in an element whose
box-sizing
property is explicitly set tocontent-box
) but it will still break the layout of any content that uses any non-defaultbox-sizing
for some outer elements while expecting its inner elements to havebox-sizing
set tocontent-box
(the default value in CSS3 iscontent-box
– notinherit
).I’ve seen layouts that use
border-box
only for outer layout divs but not for inside elements leaving them with the default box model – so now the inside elements have to be set explicitly to a value that normally is default per CSS standard. I am talking about including content that you have no control over which is sometimes impossible to avoid.Also there is some issue with images with border etc. so some exceptions are needed there as well.
I’ve written a proposal to hopefully solve those and similar problems without any need for those wildcard selector hacks, pseudoclasses or adding any classes to elements at all, or forcing properties to be inherited when they aren’t in CSS standard etc. Here is my proposal, just a first draft:
Better Box Model == No Box Model
https://gist.github.com/rsp/9f47e0105e105dae6869
I’m waiting for comments.
No offense, but this proposal sounds at bit ridiculous to me …
You’re suggesting to split
width
andheight
properties into eight new properties –width-content
,width-padding
,width-border
,width-margin
and likewise forheight
… you really think this would make it easier to specify layout dimensions in real life? I doubt that – a lot.(Besides,
width-margin
makes the least sense IMHO – including the margins into the width or height has never been done in any box-model before, and what exactly would be the benefit of doing so? I can’t think of actual use cases for this where this would make sense. And keep in mind, margins allow negative values – so with your proposal,width-margin: 500px
combined withmargin: -100px
would result in an element that is 700px wide or what …?)Your intention to make stuff easier is fair enough – but I think you’re not achieving that with this proposal; on the contrary, it even adds complexity.
CBroe, thanks for your comment. The point is that you don’t have to use all of the values and it’s not that width and height are split into other values – they are still available but you also have different values that the browser already has to know but now you have easy access to them – if you need them. For example, instead of writing:
.x { box-sizing: border-box; width: 100px; }
you can use:
.x { width-border: 100px; }
instead of writing:
.y { box-sizing: content-box; width: 100px; }
you can use:
.y { width-content: 100px; }
You could also use:
.z { width-content: 200px; height-border: 100px; }
which is impossible with ‘width’ and box-sizing.
The *-margin values were added for the completeness and they are probably the least useful but it doesn’t hurt to have them – you just don’t have to use them. And yes, for negative margin values the width-margin would be greater that the width-border. And before you ask it – yes, certain values wouldn’t make sense if they resulted in negative content width.
I don’t want to be off-topic here but I will gladly answer all comments on GitHub and on twitter:
https://gist.github.com/rsp/9f47e0105e105dae6869
if this is a best practice snippet, I thought I should be able to find it easily in the snippets section. Maybe I’m just not looking in the right place, but I couldn’t find it. Just say’n :)
Just to note, guess you still need -moz-box-sizing (FF < 29 incl. e.g. 17 and 24 which are ESR) and -webkit-box-sizing (Android <= 3). Even caniuse may be misleading in default filter view. Guess only interesting for oldschool types who still type their CSS by hand ;)
What about performance?
The * matcher is the most expensive matcher that does exist in css, right?
Maybe you should mention that in your article – at least as a side note.
The universal selector (
*
) is as fast as a simpleh1
ordiv
selector. It only gets bad performance if you nest the selctor like.something *
.The “little adjustment” made to Paul Irish’s version doesn’t seem to work on HTML5 elements (for me). The element does not inherit the box-sizing layout, but it has the box-sizing: inherit on it. Any idea why?
So instead of using box-sizing on html and letting all other elements inherit, I still prefer
*,
*:before,
*:after {
.box-sizing(border-box);
}
You’ll have to make a demo to show off the problem. The elements are supposed to have box-sizing: inherit; on them, so that they inherit from their parent, making setting a box-sizing context easier. It definitely doesn’t have anything to do if the elements are “HTML5” or not.
I think it is something really tricky. It will help many but the bloggers like me will find it difficult to implement. Anyways, a great approach.
Is there some corner-case I’m unaware of where
*:before, *:after
is a better idea than just:before, :after
? I wouldn’t be surprised if some old browser versions balked at it.@Tigt –
If both of them work, I guess
*:before
is better because it is explicit, intended and readable.I was about to add this suggestion to my own boilerplate but have we achieved consensus on it yet?
If you use normalize, be sure to add input type=”search” the border-box value, because the put “inherit” normalize inherited from the content-box default value.
for example:
No need to “add” it to your CSS, just change it in normalize.css.
It’s easier to maintain down the road. If you have issues you know there’s only one place to look at. Besides, normalize.css was made so we could customize it to our specific needs, not necessarily leave it “as is”.
I prefer not to change the use of third party tools, be normalize, jQuery etc …
because if for some reason need to use some cdn, this did not compromise my work.
Well, you’re touching on two different beasts here, plus the CDN part.
The “different beasts” part is that you usually never, at least I haven’t heard the first one to do it or known about it, edits the core jQuery library. Or any major JavaScript library for that matter. Again, I haven’t heard anyone doing something like this. At least yet.
But as far as editing a CSS file, especially “resets” (yes, I know normalize.css is not a reset, so bear with me), that’s a common thing to see many Web Designers/Front/Back End Devs do.
I see your point about not editing a third party asset, but it all depends on the type of asset you’re dealing with.
On the other hand, you mentioned using a CDN to host those third party assets. That’s great.
However, if you’re using a CDN is because you and/or your team actually DO have access to it. If you don’t have access to it you might not be hosting your CSS files in it in the first place, hence, editing normalize.css is still doable.
“just change it in normalize.css”
Yes, that’s what I meant.