Center and bottom-align flex items
css, css-grid, flexbox, html
Solution
Below are five options for achieving this layout:
- CSS Positioning
- Flexbox with Invisible DOM Element
- Flexbox with Invisible Pseudo-Element
- Flexbox with `flex: 1`
- CSS Grid Layout
Method #1: CSS Positioning Properties
Apply `position: relative` to the flex container.
Apply `position: absolute` to the green flex item.
Now the green square is absolutely positioned within the flex container.
More specifically, the green square is removed from the document flow but stays within the bounds of the nearest positioned ancestor.
Use the CSS offset properties `top`, `bottom`, `left` and `right` to move the green square around.
flex-container {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
position: relative;
border: 4px solid blue;
height: 300px;
width: 300px;
}
flex-container > flex-item:first-child {
display: flex;
}
flex-container > flex-item:first-child > flex-item {
border: 4px solid aqua;
height: 50px;
width: 50px;
margin: 0 5px;
}
flex-container > flex-item:last-child {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%); /* fine tune horizontal centering */
border: 4px solid chartreuse;
height: 50px;
width: 50px;
}
<flex-container>
<flex-item><!-- also flex container -->
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-item>
<flex-item></flex-item>
</flex-container>
One caveat: Some browsers may not completely remove an absolutely-positioned flex item from the normal flow. This changes the alignment in a non-standard, unexpected way. More details: Absolutely positioned flex item is not removed from normal flow in Firefox & IE11
Method #2: Flex Auto Margins & Invisible Flex Item (DOM element)
With a combination of `auto` margins and a new, invisible flex item the layout can be achieved.
The new flex item is identical to the bottom item and is placed at the opposite end (the top).
More specifically, because flex alignment is based on the distribution of free space, the new item is a necessary counterbalance to keep the three blue boxes vertically centered. The new item must be the same height as the existing green item, or the blue boxes won't be precisely centered.
The new item is removed from view with `visibility: hidden`.
In short:
- Create a duplicate of the green box.
- Place it at the beginning of the list.
- Use flex `auto` margins to keep the blue boxes centered, with both green boxes creating equal balance from both ends.
- Apply `visibility: hidden` to the duplicate green box.
flex-container {
display: flex;
flex-direction: column;
align-items: center;
border: 4px solid blue;
height: 300px;
width: 300px;
}
flex-container > flex-item:first-child {
margin-top: auto;
visibility: hidden;
}
flex-container > flex-item:nth-child(2) {
margin-top: auto;
display: flex;
}
flex-container > flex-item:last-child {
margin-top: auto;
margin-bottom: auto;
}
flex-container > flex-item:first-child,
flex-container > flex-item:last-child {
border: 4px solid chartreuse;
height: 50px;
width: 50px;
}
flex-container > flex-item:nth-child(2) > flex-item {
border: 4px solid aqua;
height: 50px;
width: 50px;
margin: 0 5px;
}
<flex-container>
<flex-item></flex-item>
<flex-item><!-- also flex container -->
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-item>
<flex-item></flex-item>
</flex-container>
Method #3: Flex Auto Margins & Invisible Flex Item (pseudo-element)
This method is similar to #2, except it's cleaner semantically and the height of the green box must be known.
- Create a pseudo-element with the same height as the existing green box.
- Place it at the start of the container with `::before`.
- Use flex `auto` margins to keep the blue boxes centered, with the green pseudo and DOM elements creating equal balance from both ends.
flex-container {
display: flex;
flex-direction: column;
align-items: center;
border: 4px solid blue;
height: 300px;
width: 300px;
}
flex-container::before {
content: "";
margin-top: auto;
height: calc(50px + 8px); /* height + borders */
visibility: hidden;
}
flex-container > flex-item:first-child {
margin-top: auto;
display: flex;
}
flex-container > flex-item:last-child {
margin-top: auto;
margin-bottom: auto;
border: 4px solid chartreuse;
height: 50px;
width: 50px;
}
flex-container > flex-item:first-child > flex-item {
border: 4px solid aqua;
height: 50px;
width: 50px;
margin: 0 5px;
}
<flex-container>
<flex-item><!-- also flex container -->
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-item>
<flex-item></flex-item>
</flex-container>
Method #4: Add `flex: 1` to top and bottom items
Starting with Method #2 or #3 above, instead of worrying about equal height for the top and bottom items to maintain equal balance, just give each one `flex: 1`. This will force them both to consume available space, thus centering the middle item.
You can then add `display: flex` to the bottom item in order to align the content.
Method #5: CSS Grid Layout
This may be the cleanest and most efficient method. There is no need for absolute positioning, fake elements or other hackery.
Simply create a grid with three rows. Then center-align the items in the second and third rows. The first row can remain empty.
grid-container {
display: grid;
grid-template-rows: repeat(3, 1fr);
align-items: center;
justify-items: center;
border: 4px solid blue;
height: 300px;
width: 300px;
}
grid-item:nth-child(2) {
display: flex;
}
grid-item:nth-child(2)>flex-item {
width: 50px;
height: 50px;
margin: 0 5px;
border: 4px solid aqua;
}
grid-item:nth-child(3) {
border: 4px solid chartreuse;
height: 50px;
width: 50px;
}
<grid-container>
<grid-item></grid-item>
<grid-item><!-- also flex container -->
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</grid-item>
<grid-item></grid-item>
</grid-container>
Problem
I have a flex container (the blue square) with the following properties: ``` display: flex; justify-content: center; align-items: center; flex-wrap: nowrap; ``` Therefore, its children (the light blue squares) arrange themselves as you see below. However, I'd like to add another child (the green square) out of the normal flow and position it relative to its parent. To position it as you see below, I'd ideally write something like `bottom: 20px;` and `margin: auto;`. I tried to play around with `z-index` to no avail. How should I approach this? Should I resort to creating another parent element?