positionは、CSSで「重ねる」「浮かせる」「追従させる」を担当するプロパティ——なのにabsoluteにした瞬間に要素が画面のどこかへ飛んでいく経験、誰にでもあります。この記事は、5つの値の違いと「基準がどう決まるか」を引ける早見表です。
5つの値の早見表
| 値 | 元の場所 | 位置の基準 | 主な用途 |
|---|---|---|---|
static | 残る | ずらせない(標準) | 指定を打ち消したいとき |
relative | 残る | 自分の元の位置 | 少しだけずらす/absoluteの基準になる |
absolute | 抜ける | 祖先のうちstatic以外の最も近い要素 | バッジ、重ねた文字、右上のボタン |
fixed | 抜ける | 画面(ビューポート) | 追従ヘッダー、右下のボタン |
sticky | 残る | スクロールに応じて切りかわる | 追従する見出し、テーブルのヘッダー |
いちばん大事なのは「元の場所が残るか、抜けるか」の列です。抜ける(absolute・fixed)と、その要素があった場所は詰められ、周りのレイアウトが動きます。
下のデモで、値を切り替えて動きの違いを見てください。オレンジの箱にpositionを指定しています。
relativeでは箱3が動かないのに、absoluteにすると箱3が左に詰まります。これが「元の場所から抜ける」という意味です。
位置は top / right / bottom / left で決める
positionをstatic以外にすると、この4つが使えるようになります。
.badge {
position: absolute;
top: 8px; /* 基準の上から8px */
right: 8px; /* 基準の右から8px */
}top: 8pxは「上端から下へ8px」、right: 8pxは「右端から左へ8px」です。書いた側から内側へ入っていくと覚えると迷いません。
4つまとめて書けるinsetもあります。
.overlay {
position: absolute;
inset: 0; /* top・right・bottom・left すべて0=親いっぱいに広がる */
}inset: 0は「親をぴったり覆う」の最短の書き方です。写真の上に半透明の黒を重ねるときの定番です。
absoluteの基準はどう決まるか
ここがpositionでいちばんつまずくところです。absoluteの位置は、祖先をさかのぼって、最初に見つかった「positionがstatic以外の要素」を基準にします。
<div class="card"> <!-- position: relative -->
<img src="photo.jpg" alt="写真">
<span class="badge">NEW</span> <!-- position: absolute -->
</div>.card {
position: relative; /* ← これが基準の宣言 */
}
.badge {
position: absolute;
top: 8px;
right: 8px;
}.cardにposition: relativeがないと、.badgeはページ全体の右上へ飛びます。 「absoluteにしたら変な場所へ行った」の原因は、ほぼ100%これです。
fixed——画面に張りつける
基準が画面そのものになるので、スクロールしても位置が変わりません。
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
}sticky——途中から張りつく
stickyは「ふだんは普通に流れていて、スクロールで指定の位置に来たら張りつく」というハーフの動きをします。
.section-title {
position: sticky;
top: 0;
}fixedとの違いは親の外へは出ないことです。親の領域を抜けると、いっしょにスクロールアウトします。だから「セクションごとに見出しが張りついて、次のセクションで入れかわる」という動きが1行で作れます。
z-index——重なりの順番
重なったときにどちらが前に出るかはz-indexで決めます。数字が大きいほど前です。
.modal {
position: fixed;
z-index: 100;
}大事な前提が1つあります。z-indexはpositionがstatic以外の要素にしか効きません。 「z-indexを書いたのに前に出ない」ときは、まずpositionを指定してください。
もう1つの落とし穴が重なりの層です。親にz-indexやtransform、opacity(1未満)が付いていると、その中の要素は親の層の中だけで順番を競うようになります。子にz-index: 9999を書いても、親の層ごと後ろに沈んでいれば前には出ません。「なぜか前に出ない」ときは、親をさかのぼって確認してください。
そのまま使えるレシピ6つ
画像の右上にバッジ
.card {
position: relative;
}
.badge {
position: absolute;
top: 8px;
right: 8px;
}画像の上に半透明の黒を重ねる
.thumb {
position: relative;
}
.thumb::after {
content: "";
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.35);
}重ねた要素をど真ん中に置く
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 自分の幅・高さの半分だけ戻す */
}top: 50%だけでは「要素の左上」が中央に来てしまいます。transformで自分の半分だけ戻すのがセットです。
スクロールしても消えないヘッダー
.header {
position: sticky;
top: 0;
z-index: 10;
}fixedではなくstickyにすると、本文が裏に潜り込まないので余白の調整が不要です。追従ヘッダーはstickyのほうが楽です。
右下に浮かぶボタン
.fab {
position: fixed;
right: 20px;
bottom: 20px;
z-index: 50;
}表の見出し行を固定する
thead th {
position: sticky;
top: 0;
background: #fff; /* 背景色は必須。透明だと下の行が透けて重なる */
}効かないときのチェックリスト
topやleftが効かない →positionがstatic(標準)のままです。まずrelativeなどを指定しますabsoluteが遠くへ飛ぶ → 基準にしたい親にposition: relativeを足しますstickyが動かない →topを書いたか、祖先にoverflowがないか、親の高さがあるかz-indexが効かない →positionを指定したか、親が重なりの層を作っていないかfixedが画面に張りつかない → 祖先にtransformやfilterが付いていると、そこが基準になります- 横スクロールが出た →
absoluteやfixedの要素が画面の外へはみ出しています。right側の値を見直します
まとめ
- 5つの値でいちばん大事な違いは「元の場所が残るか、抜けるか」
absoluteの基準は祖先でstatic以外の最も近い要素——親にrelativeを書くのがセットstickyは親の中でだけ張りつく。top必須、祖先のoverflowに注意z-indexはpositionを指定した要素にしか効かない- 追従ヘッダーは
fixedよりstickyが楽(本文が裏に潜らない)
positionは「基準がどこか」を言えるようになった瞬間に、こわくなくなります。飛んでいったら、まず親を見てください。