Recursive DIVs with borders: the base case

Viewed 420

It's possible to obtain the following masterpiece of recursive positioning of DIVs

recursive divs have proper borders

by using the following code.

html, body { width: 100%; height: 100%; margin: 0; }
.outer {
    width: 100%; height: 100%;
    display: flex; flex-direction: column;
}
.inner {
    margin: 10px;
    border: 10px solid gray; border-radius: 10px;
    display: flex; flex-direction: column;            
    flex: 1;
}
<div class="outer">
    <div class="inner">
        <div class="inner">
            <div class="inner">
            </div>
        </div>
    </div>
</div>

But something is odd about it. My attempts to display a border for the outer box fail (and box-sizing: border-box; doesn't help).

Can you modify this code to use styling for just html, body as well as one-box, where one-box would be used for both the outer DIV as well as an arbitrary nesting of inner DIVs?

Or does HTML5/CSS3 really require this baroque treatment of the base case when recursively nesting DIVs?

My attempt:

html, body {
    width: 100%; height: 100%; margin: 0;
    box-sizing: border-box;
}
.one-box-to-rule-them {
    width: 100%; height: 100%;

    display: flex;
    flex-direction: column;
    flex: 1;

    margin: 10px;
    border: 10px solid gray; border-radius: 10px;

    box-sizing: border-box;
}
<div class="one-box-to-rule-them">
    <div class="one-box-to-rule-them">
        <div class="one-box-to-rule-them">
            <div class="one-box-to-rule-them">
            </div>
        </div>
    </div>
</div>

fails. Adding a border to the outer box using a unified box makes the boxes go out of bounds.

adding a border to the outer box by using the same styling fails

Explanation

A teacher who solves the problem I was having is valuable. A teacher who (also) points out how my knowledge was misleading me is invaluable. There are at this moment four (nice and correct) answers, but the insight into what I was misunderstanding is provided in a comment made by Temani Afif. Let me illustrate it with a diagram.

options for box sizing

My mistake was thinking that when one changes box-sizing from its default content-box and specifies instead box-sizing: border-box;, the box calculation includes all four, content, padding, border, and margin. As its names implies, box-sizing: border-box; includes in the calculation the border-box. It does not include the margin.

4 Answers

You can rely on flexbox and the default stretch alignment to do this without the need of specifying height or width or box-sizing:

body {
  height: 100vh;
  margin: 0;
  display: flex;
}

.inner{
  margin: 8px;
  border: 8px solid gray;
  border-radius: 10px;
  display: flex;
  flex-grow: 1;
}
<div class="inner">
  <div class="inner">
    <div class="inner">
      <div class="inner">
        <div class="inner">
        </div>
      </div>
    </div>
  </div>
</div>

Remove the width: 100% from all elements, replace margins with paddings, and remove the redundant flex rules:

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  margin: 0;
}

.one-box-to-rule-them {
  height: 100%;
  padding: 10px;
  border: 10px solid gray;
  border-radius: 10px;
}
<div class="one-box-to-rule-them">
  <div class="one-box-to-rule-them">
    <div class="one-box-to-rule-them">
      <div class="one-box-to-rule-them">
      </div>
    </div>
  </div>
</div>

Your problematic rule is margin because no matter if you set your box-sizing to border-box, margin is never included in the dimensions of the element in the box-model, it is basically "outer-spacing". You also don't need the flexbox rules for what you want! But of course you need some kind of spacing for your desired effect, I therefore chose to create a pseudo-element that will receive the border, position it absolutely to fit the dimensions of the parent and give the parent a double-border-width padding to mimic the spacing you had before:

html, body {
    width: 100%; height: 100%; margin: 0;
    box-sizing: border-box;
}
*, *:before, *:after {
    box-sizing: inherit;
}
.one-box-to-rule-them {
    height: 100%;
    padding: 20px;
    position: relative
}
.one-box-to-rule-them:before {
    border: 10px solid gray; border-radius: 10px;
    content: '';
    position: absolute; top: 0; right: 0; bottom: 0; left: 0;
}
<div class="one-box-to-rule-them">
    <div class="one-box-to-rule-them">
        <div class="one-box-to-rule-them">
            <div class="one-box-to-rule-them">
            </div>
        </div>
    </div>
</div>

EDIT: @Ori Drori solution is cleaner, but uses the two-classes approach, mine uses just one but is more "ugly" CSS (in my opinion) =D

EDIT: @Temani Afif should be the accepted solution!

Is this what you want?
if I understand it correctly the the problem with width 100% only
Like this:

html, body { height: 100%; margin: 0; }
.outer {
    height: 100%;
    display: flex; flex-direction: column;
}
.inner {
    margin: 10px;
    border: 10px solid gray; border-radius: 10px;
    display: flex; flex-direction: column;            
    flex: 1;
}
<div class="outer">
   <div class="inner">
        <div class="inner">
            <div class="inner">
            </div>
        </div>
    </div>
</div>

Related