19

I want two elements to take up an exact percent of the parent's width, but I also need margins on them holding them apart. I have the following markup:

<div class='wrap'>
  <div class='element'>HELLO</div><div class='element'>WORLD</div>
</div>​
.wrap {
  background:red;
  white-space:nowrap;
  width:300px;
}
.element {
  background:#009; color:#cef; text-align:center;
  display:inline-block;
  width:50%;
  margin:4px;
}

As you can see in http://jsfiddle.net/NTE2Q/ the result is that the children overflow the wrapper:

The "world" box extends past the end of the "wrap" container

How can I get them to fit within the space? Sadly, there is no box-sizing:margin-box for this case.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • Oh, and it is intended to be assumed that the width of the parent wrapper is unknown and/or flexible; the fact that it is hardcoded to 300px is just for the demo. Setting the children to `width:142px` is not an acceptable general solution. :) – Phrogz Jan 04 '13 at 21:54

4 Answers4

40

Technique #1 - Modern CSS3 calc()

Using CSS3's calc() length, you can do this by setting the width of the .element to:

.element {
  width: 49%;                     /* poor approximation for old browsers    */
  width: calc(50% - 8px);         /* standards-based answer for IE9+, FF16+ */
  width: -moz-calc(50% - 8px);    /* support for FF4 - FF15                 */
  width: -webkit-calc(50% - 8px); /* support for Chrome19+ and Safari6+     */
}

Demo: http://jsfiddle.net/NTE2Q/1/

Hello and World have 4px margins around them

See http://caniuse.com/calc for details on which browsers and versions support this.

 


Technique #2 - Old School Wrapping

Calculations can be made by piling up multiple elements. For this case, we wrap each 'element' in a wrapper that is 50% wide but with a 4px padding:

<div class='wrap'>
  <div class='ele1'>
    <div class='element'>HELLO</div>
  </div><div class="ele1">
    <div class='element'>WORLD</div>
  </div>
</div>​
.ele1 {
    display:inline-block;
    width:50%;
    padding:4px;
    box-sizing:border-box;          /* Make sure that 50% includes the padding */
    -moz-box-sizing:border-box;     /* For Firefox                             */
    -webkit-box-sizing:border-box;  /* For old mobile Safari                   */
}
.element {
    background:#009; color:#cef; text-align:center;
    display:block;
}

Demo: http://jsfiddle.net/NTE2Q/2/

Hello and World have 4px margins around them

 


Technique #3 - Using (CSS) Tables

The same result can be made by treating the wrapper as a 'table' and each element as a cell within the same row. With this, whitespace between elements is not important:

<div class='wrap'>
  <div class='element'>HELLO</div>
  <div class='element'>WORLD</div>
</div>​
.wrap {
    background:red;
    width:300px;
    display:table;
    border-spacing:4px
}
.element {
    background:#009; color:#cef; text-align:center;
    width:50%;
    display:table-cell;
}
​

Demo: http://jsfiddle.net/NTE2Q/4/

Blue cells with 4px consistently around the edges

Note that this last technique collapses the 4px spacing between the two elements, while the first two techniques cause 8px to appear between the two items and 4px at the edges.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • Another option is using CSS `box-sizing: border-box;` – Falmarri Jan 04 '13 at 23:19
  • @Falmarri No, that explicitly does not work, because `box-sizing:border-box` only goes up to the edge of the borders. Additional width due to margins is not included in the `50%`. – Phrogz Jan 05 '13 at 17:55
  • Actually I misread your second answer, and that's what I meant – Falmarri Jan 07 '13 at 17:40
2

What you are describing is basically a border. So why not to use CSS border property with background-clip? Just don't forget appropriate vendor prefixes.

http://jsfiddle.net/NTE2Q/8/

.wrap {
    background-color: red;
    white-space:nowrap;
    width:300px;
}
.element {
    background:#009; color:#cef; text-align:center;
    display:inline-block;
    width:50%;
    border:4px solid rgba(0,0,0,0);
    box-sizing: border-box;
    background-clip: padding-box;
}
Evgeny
  • 6,261
  • 8
  • 35
  • 43
  • The visual appearance in this particular simplified test case happens to be a red border (so that you can see the width of the parent container), but the general case described is a margin preventing the two elements from visually touching. Unfortunately, a border with a color of `transparent` or `rgba(0,0,0,0)` will cause the background color of the element to shine through, preventing the desired effect. – Phrogz Jan 04 '13 at 22:48
  • 1
    it is possible to make transparent border with background-clip: padding-box (you can combine it with shortcut background property) – Evgeny Jan 04 '13 at 23:05
0

None of the above techniques worked consistently enough cross browser for me. I found a slightly different technique using display:table-cell allowed me to place 2 or more elements next to each other. Here is an example of it in action.

The CSS:

    display:table-cell;
    background:#009; color:#cef; text-align:center;
    width:22%; /*Set percentage based on # of elements*/
    border:4px solid rgb(255,0,0);/*no longer need background to be red, just make border red*/

You no longer even need the wrapper since the div is now treated as a <td>.

Joe Komputer
  • 1,325
  • 3
  • 12
  • 25
  • 1
    I'm not sure how none of the above worked for you, but this did, when this technique is the third one I listed in my answer. What am I missing? – Phrogz Mar 11 '14 at 22:48
  • Sorry, should of read your answer more carefully. Your third method is using table-cell like I am, so it would of worked for me. Although I removed some unneeded code in my answer. – Joe Komputer Mar 12 '14 at 15:33
0

Though I strongly suggest using Phorgz's calc() technique whenever possible, I also want to propose an old-school way of doing this that uses only one wrapper and position: relative to achieve the effect.

.two-blocks-by-side() LESS Mixin:

.two-blocks-by-side(@padding) {
  padding: @padding (@padding + @padding / 2);
  font-size: 0;

  & > div {
    position: relative;
    display: inline-block;
    font-size: initial;
    width: 50%;

    &:first-child { left: -1 * @padding / 2 };
    &:last-child { right: -1 * @padding / 2 };
  }
}

JS Bin example

Christophe Marois
  • 6,471
  • 1
  • 30
  • 32