justify-content と align-items をひと言で
flex コンテナでは、justify-content がアイテムを 主軸 (行に沿った方向) に配分し、align-items が 交差軸 (行に対して上下の方向) で揃えます。どちらもコンテナに書きます:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Center both ways</title>
<style>
.box {
display: flex;
justify-content: center; /* horizontal, in a row */
align-items: center; /* vertical, in a row */
height: 140px;
background: #e5e7eb;
}
.item { background: #2563eb; color: white; padding: 12px 18px; }
</style>
</head>
<body>
<div class="box"><div class="item">Centered</div></div>
</body>
</html>
ボックスは両方向の中央にあります。始まりと終わりの端から同じ距離で、上と下からも同じ距離です。height: 140px を取ると、コンテナがアイテム自身の高さまで縮むので、align-items: center はすることがなくなります。
justify-content のすべての値を実測する
下の各行は、同じ 3 つのアイテムに違う justify-content を指定したものです。各行の上のラベルの数値は、ブラウザが残したすき間です。最初のアイテムの前、アイテムの間、最後のアイテムの後の順に並んでいます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>justify-content values</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.row { display: flex; width: 420px; background: #e5e7eb; margin: 2px 0 10px; }
.row div { width: 60px; height: 28px; background: #2563eb; }
.row div:nth-child(2) { background: #16a34a; }
.row div:nth-child(3) { background: #f59e0b; }
</style>
</head>
<body>
<div id="list"></div>
<script>
const values = ['flex-start', 'center', 'flex-end', 'space-between', 'space-around', 'space-evenly'];
const list = document.getElementById('list');
values.forEach((v) => {
const label = document.createElement('code');
const row = document.createElement('div');
row.className = 'row';
row.style.justifyContent = v;
row.innerHTML = '<div></div><div></div><div></div>';
list.append(label, row);
const b = row.getBoundingClientRect();
const r = [...row.children].map((c) => c.getBoundingClientRect());
const gaps = [r[0].left - b.left, r[1].left - r[0].right, r[2].left - r[1].right, b.right - r[2].right];
label.textContent = 'justify-content: ' + v + ' gaps: ' + gaps.map(Math.round).join(' / ');
});
</script>
</body>
</html>
コンテナは 420px、アイテムは 180px を使うので、空きスペースは 240px です。その行き先は次のとおりです:
| 値 | 空きスペースの行き先 | デモのすき間 |
|---|
flex-start (と start) | すべてアイテムの後 | 0 / 0 / 0 / 240 |
center | 半分が前、半分が後 | 120 / 0 / 0 / 120 |
flex-end (と end) | すべてアイテムの前 | 240 / 0 / 0 / 0 |
space-between | アイテムの間だけ | 0 / 120 / 120 / 0 |
space-around | 各アイテムの両側に同じだけ。両端は半分 | 40 / 80 / 80 / 40 |
space-evenly | 両端も含めてすべてのすき間が同じ | 60 / 60 / 60 / 60 |
flex-start は flex の方向に従い、start は書字方向に従います。違いが出るのは row-reverse や column-reverse のときだけで、そのとき flex-start は逆側の端になります。
align-items のすべての値
align-items は行に対して直角の方向に働きます。下のアイテムは高さがばらばらなので、各値がどう扱うかがわかります:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>align-items values</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.grid { display: flex; flex-wrap: wrap; gap: 16px; }
.row { display: flex; gap: 4px; width: 170px; height: 100px; background: #e5e7eb; }
.row div { width: 50px; background: #2563eb; color: white; }
.row div:nth-child(1) { font-size: 26px; }
.row div:nth-child(2) { background: #16a34a; padding-top: 20px; }
.row div:nth-child(3) { background: #f59e0b; }
</style>
</head>
<body>
<div class="grid" id="grid"></div>
<script>
['stretch', 'flex-start', 'center', 'flex-end', 'baseline'].forEach((v) => {
const cell = document.createElement('div');
cell.innerHTML = '<code>align-items: ' + v + '</code><div class="row" style="align-items: ' + v + '"><div>Ag</div><div>Ag</div><div>Ag</div></div>';
document.getElementById('grid').append(cell);
});
</script>
</body>
</html>
| 値 | 交差軸での動き |
|---|
stretch (初期値) | 高さを指定していないアイテムが、コンテナいっぱいに伸びる |
flex-start (と start) | アイテムは自分の高さのまま上に置かれる |
center | アイテムは自分の高さのまま中央に置かれる |
flex-end (と end) | アイテムは下に置かれる |
baseline | 文字の 1 行目が同じベースラインにそろうようにアイテムが動く |
baseline のボックスでは、1 つ目が大きく、2 つ目は上に padding があるのに、3 つの「Ag」のラベルが同じ線の上に並んでいます。大きな見出しと小さな文字が混ざる行や、ラベルと入力欄の行にはこの値を使います。
クリックして揃える
3 × 3 のグリッドのどのセルでもクリックしてください。ボックスがそこに移動し、そこに置くための CSS が下に表示されます:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Alignment picker</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 14px; }
.wrap { display: flex; gap: 16px; align-items: flex-start; }
.picker { display: grid; grid-template-columns: repeat(3, 28px); gap: 4px; }
.picker button { height: 28px; border: 1px solid #111827; background: white; cursor: pointer; }
.picker button[aria-pressed="true"] { background: #2563eb; }
#stage { display: flex; width: 260px; height: 150px; background: #e5e7eb; }
.item { background: #16a34a; color: white; padding: 10px 14px; }
pre { background: #f3f4f6; padding: 8px; }
</style>
</head>
<body>
<div class="wrap">
<div class="picker" id="picker"></div>
<div id="stage"><div class="item">Box</div></div>
</div>
<pre id="out"></pre>
<script>
const spots = ['flex-start', 'center', 'flex-end'];
const stage = document.getElementById('stage');
const pick = (row, col, btn) => {
stage.style.alignItems = spots[row];
stage.style.justifyContent = spots[col];
document.querySelectorAll('.picker button').forEach((b) => b.setAttribute('aria-pressed', b === btn));
document.getElementById('out').textContent =
'.stage {\n display: flex;\n justify-content: ' + spots[col] + ';\n align-items: ' + spots[row] + ';\n}';
};
spots.forEach((_, row) => spots.forEach((_, col) => {
const btn = document.createElement('button');
btn.setAttribute('aria-label', 'row ' + (row + 1) + ', column ' + (col + 1));
btn.addEventListener('click', () => pick(row, col, btn));
document.getElementById('picker').append(btn);
}));
pick(1, 1, document.querySelectorAll('.picker button')[4]);
</script>
</body>
</html>
ピッカーの行は align-items を、列は justify-content を変えます。真ん中のセルが定番の「div の中央寄せ」です。
縦並びでは 2 つが入れ替わる
justify-content は常に主軸に従います。flex-direction: column では主軸が縦になるので、justify-content がアイテムを上下に、align-items が左右に動かします:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Column axes</title>
<style>
.col {
display: flex;
flex-direction: column;
justify-content: flex-end; /* now vertical: pushed to the bottom */
align-items: center; /* now horizontal: centered */
height: 160px;
background: #e5e7eb;
}
.col div { background: #2563eb; color: white; padding: 6px 12px; }
</style>
</head>
<body>
<div class="col"><div>One</div><div>Two</div></div>
</body>
</html>
取り違えたときは、レイアウトを書き直すのではなく 2 つのプロパティを入れ替えましょう。
align-self と auto の margin: 1 つのアイテムだけ
アイテムに align-self を付けると、そのアイテムについて align-items を上書きします。flexbox に justify-self はありません。1 つのアイテムを主軸に沿って動かすには auto の margin を付けます。auto の margin はその側の空きスペースをすべて使います:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>align-self</title>
<style>
.bar { display: flex; align-items: flex-start; gap: 8px; height: 90px; background: #e5e7eb; }
.bar div { background: #2563eb; color: white; padding: 8px 12px; }
.bar .down { align-self: flex-end; background: #16a34a; }
.bar .last { margin-inline-start: auto; background: #f59e0b; }
</style>
</head>
<body>
<div class="bar">
<div>Top</div>
<div class="down">align-self: flex-end</div>
<div class="last">margin-inline-start: auto</div>
</div>
</body>
</html>
緑のアイテムだけが下に下がり、琥珀色のアイテムは行の反対側の端まで押し出され、ほかは始まりにとどまります。
複数行のとき: align-content
align-items はアイテムをその行の中で揃えます。コンテナが複数行に折り返すとき、行そのもの の間隔を決めるのが align-content で、justify-content と同じ値を使えます。nowrap のコンテナでは何もしません。その 1 行が常にコンテナの高さいっぱいになるからです。flex-wrap のページ で実例を見られます。
よくある間違い
- 軸を取り違える。
justify-content は並ぶ方向、align-items はそれと直角の方向です。縦並びでは、縦方向の中央寄せは justify-content: center です。
- 空きスペースがない。
flex: 1 のアイテムがスペースをすべて使うので、justify-content が配分するものがありません。コンテナが inline-flex でアイテムとちょうど同じ幅のときも同じです。
align-items なのに高さがない。 内容と同じ高さのコンテナでは、揃える余地がありません。height か min-height を指定しましょう。
- flexbox で
justify-self を探す。 flexbox では無視されます。アイテムに margin-inline-start: auto (または margin-left: auto) を使いましょう。
align-items: stretch なのに伸びない。 自分の height を持つアイテムはそれを保ちます。高さを取り除きましょう (最小値が必要なら min-height を使います)。
- プロパティをアイテムに書く。 どちらも flex コンテナに書きます。アイテムに書くのは
align-self だけです。