Grow your CSS skills. Land your dream job.

Inheriting box-sizing Probably Slightly Better Best-Practice

Published by Chris Coyier

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.

Comments

  1. Tyler
    Permalink to comment#

    At the end of the article, a bonus point for saying “gotten” instead of “got”, but it should read “never been bitten”.

  2. Couldn’t you just use:

    .component * {
       box-sizing: border-box;
    }
    
    • 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:

      .component, .component * {
        box-sizing: content-box;
      }
      

      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:

      .component, .component *, .component *:before, .component *:after {
        box-sizing: content-box;
      }
      

      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:

      .component, 
      .component:before, 
      .component:after,
      .component *,
      .component *:before,
      .component *:after {
        box-sizing: content-box;
      }
      
    • 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?

    • PhistucK
      Permalink to comment#

      * {} is like html {}, it is a universal selector for the name of the element. Just like there is *:hover (pseudo class), there is *::before (pseudo element).

    • ausi
      Permalink to comment#

      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 to html being “universal”: https://developer.mozilla.org/en-US/docs/Web/CSS/Universal_selectors

      * and html 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?

    • PhistucK
      Permalink to comment#

      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”.

    • PhistucK
      Permalink to comment#

      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)

    • Jason
      Permalink to comment#

      @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.)

  3. Permalink to comment#

    Great idea!

    Shouldn’t the html { box-sizing: border-box; } declaration come after the wildcard * selector so that the box-sizing value will properly cascade, or is html not included in the * selector? Like so:

    *, *:before, *:after { box-sizing: inherit; }
    html { box-sizing: border-box; }
    
    • html is included in *, but the order doesn’t matter here, it works either way.

    • Jason
      Permalink to comment#

      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.

  4. Jason
    Permalink to comment#

    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.

    • Jason
      Permalink to comment#

      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.

  5. While we’re at it, what about those *:before and after selectors? How necessary are they?

    • Patrick
      Permalink to comment#

      If you use :before and :after and want to apply box-sizing: border-box using this method, you’ll have to select them like this. If you don’t, just leave them out.

  6. Man, I honestly have not had to go back to box-sizing: content-box; after learning border-box.

  7. PhistucK
    Permalink to comment#

    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)?

  8. Hi thanks for the tip. Inherit box size would be a better and convenient way to size.

  9. Ruben Vreeken
    Permalink to comment#

    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 ems.

  10. 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.

  11. totes cray. nice Chris/Jon

  12. kiko
    Permalink to comment#

    This is an OO solution, a better practice. Cool man!

  13. 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 to content-box) but it will still break the layout of any content that uses any non-default box-sizing for some outer elements while expecting its inner elements to have box-sizing set to content-box (the default value in CSS3 is content-box – not inherit).

    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.

    • CBroe
      Permalink to comment#

      No offense, but this proposal sounds at bit ridiculous to me …

      You’re suggesting to split width and height properties into eight new properties – width-content, width-padding, width-border, width-margin and likewise for height … 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 with margin: -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

  14. Chris G
    Permalink to comment#

    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 :)

  15. Mike
    Permalink to comment#

    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 ;)

  16. 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.

    • ausi
      Permalink to comment#

      The universal selector (*) is as fast as a simple h1 or div selector. It only gets bad performance if you nest the selctor like .something *.

  17. TK
    Permalink to comment#

    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.

  18. 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.

  19. Permalink to comment#

    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.

    • PhistucK
      Permalink to comment#

      @Tigt –
      If both of them work, I guess *:before is better because it is explicit, intended and readable.

  20. Christian
    Permalink to comment#

    I was about to add this suggestion to my own boilerplate but have we achieved consensus on it yet?

  21. 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:

    // normalize default value
    input[type="search"] {
        box-sizing: content-box;
    }
    
    // apply to your css 
    input[type="search"] {
        box-sizing: border-box;
    }
    
    • 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.

  22. Christian
    Permalink to comment#

    “just change it in normalize.css”

    Yes, that’s what I meant.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".