Distributing along the main axis
Most day-to-day layout tickets are placement tickets, asking to center something, to push two things to opposite ends, or to explain why something is misaligned. The two properties in this lesson close nearly all of them.
.row {
display: flex;
justify-content: space-between;
}| Value | Effect |
|---|---|
flex-start | pack items at the start, the default |
center | pack items in the middle |
flex-end | pack items at the end |
space-between | first and last touch the edges, equal gaps between |
space-around | equal space around every item |
For the cross axis, align-items: center is the value typed most often, since it vertically centers the items of a row.
gap: 16px adds fixed space between items and never at the edges, which is far cleaner than putting margins on the children and then fighting the extra margin on the last one.
The two recipes worth knowing cold
Perfect centering puts one child dead center in its parent, and it is the recipe behind almost every hero section, meaning the full-screen opening banner from lesson 5-3.
.hero {
display: flex;
justify-content: center; /* main axis */
align-items: center; /* cross axis */
min-height: 100vh; /* vh from lesson 5-3 */
}The stretchy middle uses flex-grow to hand leftover space to one item. The classic header has a logo, a search box filling whatever is left, then buttons.
.search {
flex-grow: 1;
}flex-grow | Share of leftover space |
|---|---|
0 | none, the item stays content-sized |
1 | one share |
2 | twice as much as an item with 1 |
Items default to 0, so nothing stretches until asked. Grow values split the leftover space proportionally rather than setting widths, which is why the header keeps working at any window size.
Centering on both axes
The pair that centers a child horizontally and vertically in a flex row is justify-content: center with align-items: center.
One property per axis, with justify-content centering along the main horizontal axis and align-items along the cross vertical axis.
| Property | Axis in a row |
|---|---|
justify-content | horizontal |
align-items | vertical |
Pairs such as text-align with vertical-align are not how flex containers are controlled, and they belong to text and table-cell layout instead. Reaching for them inside a flex container is a common early habit that produces no effect.
A group of cards centered on both axes
Both alignment properties set to center, on a container tall enough for the centering to be visible.
CSS
.stage {
display: flex;
justify-content: center;
align-items: center;
height: 220px;
border: 2px dashed gray;
}
.card {
padding: 16px 24px;
margin: 4px;
background: #d4af37;
border-radius: 8px;
font-family: sans-serif;
}| Declaration | Axis | Result |
|---|---|---|
display: flex | none | enables the other two |
justify-content: center | main, horizontal | group sits in the middle |
align-items: center | cross, vertical | group sits halfway down |
height: 220px | none | gives the cross axis room |
display: flex comes first, because the other two do nothing without it.
The explicit height matters more than it looks. A container that is only as tall as its content has nothing to center within, so vertical centering appears to fail when the real problem is a missing height.
Computing space-between by hand
spaceBetween(containerWidth, itemWidth, count) returns the x position of each item's left edge, with the first item at 0, the last touching the right edge, and the leftover space split into equal gaps.
function spaceBetween(containerWidth, itemWidth, count) { const leftover = containerWidth - itemWidth * count; const gap = leftover / (count - 1); const positions = []; for (let i = 0; i < count; i++) { positions.push(i * (itemWidth + gap)); } return positions; } console.log(spaceBetween(500, 100, 3)); console.log(spaceBetween(400, 50, 2));
Output
[ 0, 200, 400 ] [ 0, 350 ]
| Step | Formula |
|---|---|
| leftover | containerWidth − itemWidth × count |
| gap | leftover ÷ (count − 1) |
| position of item i | i × (itemWidth + gap) |
Three 100px items in 500px leave 200px, split into 2 gaps of 100px each. Each item starts one stride of itemWidth + gap after the previous one.
The divisor is count - 1 rather than count because the gaps sit only between items, never at the edges. That single detail is the whole difference between space-between and space-around.