At work this week I had to implement some CSS Flexible Boxes (flexbox),
and I'm getting better at using them. The layout called for a fixed-
height title bar, fixed-height lower bar with a variable height middle.
And in the middle, a fixed-width left and right bar with a variable
middle. This illustrates the layout I'm looking for:
The goal here is to have the middle box adjust to the remaining space
available. CSS flexboxes are exactly what is needed, but they can be a
little tricky to setup. The important thing to remember is that for a
<div> to be flexible, it's parent must be marked as flex. The
children then need to either specify their size, or be told to stretch.
Let's start with a stripped down version of what is need. In this
example, we need one fixed-sized <div> on the left, and a variable
size <div> on the right that will take the remaining space.
Here is the code to accomplish this :
.body
{
flex-direction: row;
width: 400px;
height: 50px;
padding: 2px;
}
.left
{
padding: 2px;
margin-right: 2px;
width: 50px;
background-color: #A0A0A080;
}
.right
{
padding: 2px;
flex-grow : 1;
background-color: #A0A0A080;
}
…
<div class="body">
<div class="left">Left</div>
<div class="right">Right</div>
</div>
Note that we can add boxes to the right and the center will still take
the remaining space.
Left
Center
Right 1
Right 2
.body
{
flex-direction: row;
width: 400px;
height: 50px;
padding: 2px;
}
.left
{
padding: 2px;
margin-right: 2px;
width: 50px;
background-color: #A0A0A080;
}
.center
{
padding: 2px;
flex-grow : 1;
background-color: #A0A0A080;
}
.right
{
padding: 2px;
margin-left: 2px;
width: 50px;
background-color: #A0A0A080;
}
…
<div class="body">
<div class="left">Left</div>
<div class="center">Center</div>
<div class="right">Right 1</div>
<div class="right">Right 2</div>
</div>
This takes care of the center portion of our request. Now about adding
the top and bottom. Let's make a simple change to our setup so this
is column based rather than row.
.body
{
flex-direction: column;
width: 400px;
height: 150px;
padding: 2px;
}
.top
{
padding: 2px;
margin-bottom: 2px;
height: 25px;
background-color: #A0A0A080;
}
.center
{
padding: 2px;
flex-grow : 1;
background-color: #A0A0A080;
}
.bottom
{
padding: 2px;
margin-top: 2px;
height: 25px;
background-color: #A0A0A080;
}
…
<div class="body">
<div class="top">Top</div>
<div class="center">Center</div>
<div class="bottom">Bottom</div>
</div>
Now we combine the two setups, nesting the column-based set in the row-
based set.
.body
{
flex-direction: column;
width: 400px;
height: 150px;
padding: 2px;
}
.top
{
padding: 2px;
margin-bottom: 2px;
height: 25px;
background-color: #A0A0A080;
}
.middle
{
padding: 2px;
flex-grow : 1;
background-color: #A0A0A080;
display: flex;
flex-direction: row;
padding: 4px;
width: calc( 100% - 8px );
}
.bottom
{
padding: 2px;
margin-top: 2px;
height: 25px;
background-color: #A0A0A080;
}
.middleLeft
{
padding: 2px;
margin-right: 2px;
width: 50px;
background-color: #A0A0A080;
}
.middleCenter
{
padding: 2px;
flex-grow : 1;
background-color: #A0A0A080;
}
.middleRight
{
padding: 2px;
margin-left: 2px;
width: 50px;
background-color: #A0A0A080;
}
…
<div class="body">
<div class="top">Top</div>
<div class="middle">
<div class="middleLeft">Left</div>
<div class="middleCenter">Middle</div>
<div class="middleRight">Right</div>
</div>
<div class="bottom">Bottom</div>
</div>
A couple of items to note. We have two flexible parents, the outer body
div, and the middle div. The middle div can both grow and is a flex
parent. The middle also uses a bit of math on the width to account
for the padding. It should take 100% of the cell, but account for
the 8 pixels of padding, 4 on the left, and 4 on the right.
Let's allow the flex area to be resized. Try changing the size by
dragging the bottom left corner around.
So it would appear we are done, however there is just one problem.
Top
Left
- Value A
- Value B
- Value C
- Value D
Bottom
The problem here may not be immediately obvious, but the right column
has been shrunk. This happens because the content of the middle div is
large and the left and right divs are allowed to shrink. However, we
can prevent that.
Top
Left
- Value A
- Value B
- Value C
- Value D
Bottom
.top
{
padding: 2px;
margin-bottom: 2px;
height: 25px;
background-color: #A0A0A080;
flex-shrink: 0;
}
.middle
{
padding: 2px;
flex-grow : 1;
background-color: #A0A0A080;
display: flex;
flex-direction: row;
padding: 4px;
width: calc( 100% - 8px );
}
.bottom
{
flex-shrink: 0;
padding: 2px;
margin-top: 2px;
height: 25px;
background-color: #A0A0A080;
}
.middleLeft
{
flex-shrink: 0;
padding: 2px;
margin-right: 2px;
width: 50px;
background-color: #A0A0A080;
}
.middleCenter
{
padding: 2px;
flex-grow : 1;
flex-shrink: 1;
background-color: #A0A0A080;
}
.middleRight
{
flex-shrink: 0;
padding: 2px;
margin-left: 2px;
width: 50px;
background-color: #A0A0A080;
}
While that fixes the column width, it is easy to see that there is a
problem with overflow. To fix this we need to address overflow in
two location: the middle parent div, and the middle child. The
parent can be marked to hide overflow, and the child can either hide
or use scroll bars.
Top
Left
- Value A
- Value B
- Value C
- Value D
Bottom
.top
{
padding: 2px;
margin-bottom: 2px;
height: 25px;
background-color: #A0A0A080;
flex-shrink: 0;
}
.middle
{
padding: 2px;
flex-grow : 1;
background-color: #A0A0A080;
display: flex;
flex-direction: row;
padding: 4px;
width: calc( 100% - 8px );
overflow: hidden;
}
.bottom
{
flex-shrink: 0;
padding: 2px;
margin-top: 2px;
height: 25px;
background-color: #A0A0A080;
}
.middleLeft
{
flex-shrink: 0;
padding: 2px;
margin-right: 2px;
width: 50px;
background-color: #A0A0A080;
}
.middleCenter
{
padding: 2px;
flex-grow : 1;
flex-shrink: 1;
background-color: #A0A0A080;
overflow: auto;
}
.middleRight
{
flex-shrink: 0;
padding: 2px;
margin-left: 2px;
width: 50px;
background-color: #A0A0A080;
}
When all of this is done we can get a complex layout that sizes how
we want. With the demo below try resizing the content to see how the
layout allows the middle box to take the remaining room.
- Value A
- Value B
- Value C
- Value D
- Value E
- Value F
- Value G
- Value H
- Value I
- Value J
- Value K
- Value L
- Value M
- Value N
- Value O
- Value P
- Value Q
- Value R
- Value S
- Value T
- Value U
- Value V
- Value W
- Value X
- Value Y
- Value Z
Column A |
Column B |
Column C |
Column D |
Value A | Value B | Value C | Value D |
Value A | Value B | Value C | Value D |
Value A | Value B | Value C | Value D |
Value A | Value B | Value C | Value D |
A |
C |
D |
There you have it. I find it helps to build a page layout in steps
so you can get the complexities of items such as flex boxes and
overflows.