Skip to content

Instantly share code, notes, and snippets.

@rsp
Last active July 19, 2023 04:12
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rsp/9f47e0105e105dae6869 to your computer and use it in GitHub Desktop.
Save rsp/9f47e0105e105dae6869 to your computer and use it in GitHub Desktop.
Better Box Model == No Box Model - how to solve problems with CSS3 box-sizing: border-box; https://twitter.com/pocztarski/status/489619558991675393

Better Box Model == No Box Model

The premise is that a better box model would be no box model at all, or rather no dependence on any particular box model that happens to be in use.

This proposal specifies new CSS properties that would make working with issues related to box-sizing easier and less error prone.

Problems

The inconsistencies of different box models have always caused problems and the question of which model should be used has resulted in countless arguments in the community.

Earlier versions of the Internet Explorer used a box model different than the standard model recommended by the W3C. In 2001 Internet Explorer 6 introduced the W3C recommended box model in "standards-compliant mode" while still using its old box model in "quirks mode" for backwards compatibility. This made it possible to define layouts that would look the same in all browsers (if a certain doctype was present) but it didn't make defining those layouts easy, or indeed even possible.

The problem is that it is very hard (if not outright impossible) to define certain layouts (especially fluid and responsive layouts) using the standard box model so historically people have used divs inside of divs as a workaround.

But as it turs out many of the layouts so hard to implement in the standard box model would be simple using the original box model used in Internet Explorer so in CSS3 a new box-sizing property was introduced which when set to border-box makes the box model work as in quirks mode of Internet Explorer.

Recently many ways were proposed to set the rendering to border-box by default:

Box model reset #1:

* {
  -webkit-box-sizing: border-box; 
  -moz-box-sizing: border-box; 
  box-sizing: border-box;
}

Box model reset #2:

*, *:before, *:after {
  -webkit-box-sizing: border-box; 
  -moz-box-sizing: border-box; 
  box-sizing: border-box;
}

Box model reset #3:

html {
  box-sizing: border-box;
}
*, *:before, *:after {
  box-sizing: inherit;
}

See:

All of the solutions listed above have some problems. The first two break the layout of content that expects the standard box model. The last one while not breaking 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) it will 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).

Another problem is that it is not easy to write general purpose code to programatically get or set the values of width and height without taking into account which box model is in effect in which context.

The problem is that the semantics of width and height depend on the (possibly inherited) value of box-sizing.

Solution

The solution to those problems is not introducing different box models to choose from but to not rely on any box model at all.

Instead of making width and height mean different things depending on which value of box-sizing is in effect, it would be much more straightforward to have a set of properties that always mean the same thing.

This is just one possible set of names with alternatives below:

  1. width-content - just the width of the content
  2. width-padding - the width of the content and padding
  3. width-border - the width of the content, padding and border
  4. width-margin - the width of the content, padding, border and margin
  5. height-content - just the height of the content
  6. height-padding - the height of the content and padding
  7. height-border - the height of the content, padding and border
  8. height-margin - the height of the content, padding, border and margin

This would have the same values as, respectively:

  1. width with box-sizing: content-box
  2. width with box-sizing: padding-box
  3. width with box-sizing: border-box
  4. width with box-sizing: margin-box (if it ever gets introduced)
  5. height with box-sizing: content-box
  6. height with box-sizing: padding-box
  7. height with box-sizing: border-box
  8. height with box-sizing: margin-box (if it ever gets introduced)

Note: 4 properties: width-content, height-content, width-border and height-border (corresponding to width and height with box-sizing: content-box and box-sizing: border-box) would be used in most cases in which currently the content-box and border-box box models are used. Properties: width-padding and height-padding would be used for cases where the padding-box box model would be used. The width-margin and height-margin are provided for completeness, to have consistent access to all of the dimensions in the box model.

Examples

div.a {
  width-border:  200px;
}
div.b {
  width-content: 100px;
}
div.c {
  width-border:  300px;
  width-content: 200px;
}

Advantages

It would be not only possible but trivial to set a width of one element's content to the width of other element's padding etc.

Different parts of code could manipulate the values as if the box model was best suited for the kind of operation that was needed, without interfering with each other.

The meaning of the values of the properties would not depend on values of other properties, unlike with current width and height (the meaning of which depend on the value of the own or inherited box-sizing property that is in use).

Implementation

Where:

PT = padding-top
BR = padding-right
BB = padding-bottom
BL = padding-left
BT = border-top-width
BR = border-right-width
BB = border-bottom-width
BL = border-left-width
MT = margin-top
MR = margin-right
MB = margin-bottom
ML = margin-left

How it would work in standard box model terms:

  • Setting width-content to X would set width to X
  • Setting width-padding to X would set width to: X - (PL + PR)
  • Setting width-border to X would set width to: X - (PL + PR + BL + BR)
  • Setting width-margin to X would set width to: X - (PL + PR + BL + BR + ML + MR)
  • Getting width-content would return width
  • Getting width-padding would return width + (PL + PR)
  • Getting width-border would return width + (PL + PR + BL + BR)
  • Getting width-margin would return width + (PL + PR + BL + BR + ML + MR)

How it would work in border-box model terms:

  • Setting width-content to X would set width to X + (PL + PR + BL + BR)
  • Setting width-padding to X would set width to: X + (BL + BR)
  • Setting width-border to X would set width to: X
  • Setting width-margin to X would set width to: X - (ML + MR)
  • Getting width-content would return width - (PL + PR + BL + BR)
  • Getting width-padding would return width - (BL + BR)
  • Getting width-border would return width
  • Getting width-margin would return width + (ML + MR)

From a different point of view the width and height could be seen as:

  • aliases for width-content and height-content when box-sizing is set to content-box
  • aliases for width-padding and height-padding when box-sizing is set to padding-box
  • aliases for width-border and height-border when box-sizing is set to border-box
  • aliases for width-margin and height-margin when box-sizing is set to margin-box (possibly in the futire)

And the same for height and vertical values of padding, border and margin.

Even if implementation was not easy it would make designing layouts and reasoning about content dimensions much easier than it is today, with lower possibility for error and easier maintainability.

With the @supports CSS at-rule and the CSS.supports() method it would be easier to use those new properties with backwards compatible fallbacks.

See:

Alternative property names

Some possible naming schemes for the proposed properties - on the example of the width with padding and border:

  1. width-border in CSS, widthBorder in JavaScript
  2. width-with-border in CSS, widthWithBorder in JavaScript
  3. width-including-border in CSS, widthIncludingBorder in JavaScript
  4. width-border-box in CSS, widthBorderBox in JavaScript
  5. width-bbox in CSS, widthBbox in JavaScript

Number 1 is short, numbers 2-4 are more descriptive. Number 5 is an abbreviation: cbox, pbox, bbox, mbox - possibly not descriptive enough for CSS and the DOM API.

The naming scheme that has the most support of the community should be chosen.

Resources

@ausi
Copy link

ausi commented Jul 18, 2014

What if someone uses multiple width-properties on one element, which property should take precedence then?

For example, how should this box look like:

.box {
    width-content: 100px;
    width-padding: 200px;
    width-border: 300px;
}

@rsp
Copy link
Author

rsp commented Jul 18, 2014

I would argue that the last one would be used, just like if you wrote:

.box {
    box-sizing: content-box;
    width: 100px;
    box-sizing: padding-box;
    width: 200px;
    box-sizing: border-box;
    width: 300px;
}

You may argue that it should set padding and border to 100px each but I don't think that would be a good idea.

@ausi
Copy link

ausi commented Jul 18, 2014

So you would say this:

.box {
    width-content: 100px;
    width-padding: 200px;
}

generates a different result as this:

.box {
    width-padding: 200px;
    width-content: 100px;
}

What if someone sorts his properties alphabetically?

I think it is no good idea to introduce new "magic" properties which overwrite each other and behave differently than all other CSS properties.

Automatically settings padding and border may not be a good idea, but some stylesheet authors would expect it.

Why not just go with box-sizing: border-box and use calc() if you want to substract or add paddings and borders?

@rsp
Copy link
Author

rsp commented Jul 18, 2014

Those are all valid points. I don't know what would be the perfect solution. Maybe some of those values should always mask the others if two or more are present to make the order irrelevant (eg. width-content always wins with width-padding or the other way around) to avoid the problem that you describe with alphabetically sorted properties (and you would have to rely on more specific selectors like with any attributes that are defined twice).

I understand that there are some edge cases and there is no perfect solution. But I don't think that the box-sizing is perfect either (even though I do think that border-box box model is usually much better than content-box, it is still problematic to use as a general solution with wildcard selectors). This is just a first draft of this idea that I ever published and it certainly needs some discussion. Thanks for your valuable feedback.

@matthew-dean
Copy link

This feels like it solves a problem no one is having, with a solution that is orders of magnitude more complicated than what exists now.

@MuTLY
Copy link

MuTLY commented Apr 18, 2016

Thanks, but no thanks.

@jacekwilczynski
Copy link

jacekwilczynski commented Jan 4, 2018

Hey! I've been learning frontend for about half a year now, and I can still quite freshly remember that at first I'd really be glad to see a feature like this. Now I'm used to dealing with the box-model that's there, provided I set * {box-sizing: border-box}, but while I'm far from an authority to speak on the subject, as a thinking human being I think you proposal makes a lot of sense. Also, I don't think the precedence issue that's been raised above is really an issue. Why would someone use two conflicting properties on one selector? I think a bigger problem would be slow change in browser support. So for many years to come, you'd have to write:

{
  box-sizing: border-box;
  width: 100px;
  width-border: 100px;
}

with the two first two lines being there just for browser versions that don't support the new properties. This is of course redundancy, which we generally don't like, I guess. And when it comes to width-margin/height-margin, you couldn't count on your client's browser understanding it for a long time, so you'd have to write:

{
  margin-left: 10px;
  margin-right: 10px;
  box-sizing: border-box;
  width: 100px;
  width-margin: 120px;
}

And if you wanted to set width and height using different methods, e.g. mix width-margin and height-padding in one selector, there would be no easy fallback to use in older browsers. So to get the same effect as in:

{
  margin: 10px;
  border-width: 1px;
  padding: 5px;
  width-margin: 120px;
  height-padding: 80px;
}

you'd have to write e.g.:

{
  margin: 10px;
  border-width: 1px;
  padding: 5px;
  box-sizing: content-box;
  width: 88px; /* 120 - 2*10 - 2*1 - 2*5 = 120-20-2-10 = 88 */
  height: 70px; /* 80 - 2*5 = 80-10 = 70 */
}

By which time adding:

  width-margin: 120px;
  height-padding: 80px;

Would be redundant anyway. (I didn't check my calculations so I'm not sure if they're 100% right but you know what I mean.)
So while I like your idea in principle, I think its introduction to the standard would cause additional complications that might outweigh the benefit the syntax brings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment