Here's the layout I'm trying to achieve:
Mobile: Desktop:
+-------------------+ +-----------+---------------------+
| C | | C | |
+-------------------+ +-----------+ |
| | | P | |
| | +-----------+ |
| V | | | V |
| | | | |
| | | | |
+-------------------+ | | |
| P | | | |
+-------------------+ +-----------+---------------------+
100% 300px 100% - 300px
So if the screen is wide enough (min-width: 960px
), element V
is pulled out from between the other two, and moved to the right.
Note that none of the elements (including the outer container) have a fixed, known height. They must all be automatically sized to fit their contents.
The mobile version is also a sensible order for the DOM, so let's use this HTML:
<div class="outer">
<div class="C"></div>
<div class="V"></div>
<div class="P"></div>
</div>
I first tried to achieve the desktop layout with flexbox:
@media screen and (min-width: 960px) {
.outer {
display: flex;
flex-direction: column;
}
.C { order: 1; width: 300px; }
.V { order: 3; }
.P { order: 2; width: 300px; }
}
This takes care of the reordering, but there seems to be no way to force a wrap between P
and V
without setting fixed heights. So the elements remain stacked on top of each other.
I also tried pulling V
out using a float:
@media screen and (min-width: 960px) {
.C { width: 300px; }
.V { width: calc(100% - 300px); margin-left: 300px; float: right; }
.P { width: 300px; }
}
But then it ends up below C
. To fix that, I would have to change the DOM order, which I'd rather not do for accessibility reasons.
I could also pull V
out with absolute positioning:
@media screen and (min-width: 960px) {
.outer { position: relative; }
.C { width: 300px; }
.V { width: calc(100% - 300px); right: 0; top: 0; }
.P { width: 300px; }
}
The problem with that is that V
no longer affects the height of the outer
div, and starts overlapping the content below outer
.
Finally I considered CSS grid
, but it would reduce browser compatibility.
Is there a way to create this layout without resorting to ugly hacks?
Add more floating:
@media screen and (min-width: 960px) {
.C {
width: 300px;
float: left;
}
.V {
width: calc(100% - 300px);
float: right;
}
.P {
width: 300px;
clear: left; /* clear only left to go under C */
}
.outer {
overflow: auto; /* create a BFC to avoid the float getting outside the container */
}
}
.outer {
margin:10px;
}
<div class="outer">
<div class="C" style="height:50px;background:red"></div>
<div class="V" style="height:150px;background:blue"></div>
<div class="P" style="height:60px;background:green"></div>
</div>
<div class="outer">
<div class="C" style="height:50px;background:red"></div>
<div class="V" style="height:100px;background:blue"></div>
<div class="P" style="height:100px;background:green"></div>
</div>
<div class="outer">
<div class="C" style="height:100px;background:red"></div>
<div class="V" style="height:50px;background:blue"></div>
<div class="P" style="height:50px;background:green"></div>
</div>
Or CSS grid like below:
@media screen and (min-width: 960px) {
.V {
grid-row:span 3;
}
.outer {
display:grid;
grid-template-columns:300px 1fr;
align-items:start;
}
}
.outer {
margin:10px;
}
<div class="outer">
<div class="C" style="height:50px;background:red"></div>
<div class="V" style="height:150px;background:blue"></div>
<div class="P" style="height:60px;background:green"></div>
</div>
<div class="outer">
<div class="C" style="height:50px;background:red"></div>
<div class="V" style="height:100px;background:blue"></div>
<div class="P" style="height:100px;background:green"></div>
</div>
<div class="outer">
<div class="C" style="height:100px;background:red"></div>
<div class="V" style="height:50px;background:blue"></div>
<div class="P" style="height:50px;background:green"></div>
</div>