CSS position 完全理解 ― 5つの値の使い分けと実践パターン
「なんかズレる」「position: absolute が効かない」——そんなモヤモヤを、5つの値の仕組みから現場でよく使うパターンまで一気に整理します。

position が「わからない」の正体
CSSを学び始めて最初に壁にぶつかるのが、この position プロパティではないでしょうか。要素がどこかに飛んでいく、思った場所に来ない、z-indexを100にしても前に出ない——こういう「なんで!?」の多くは、positionの仕組みを体系的に理解していないことが原因です。
positionには5つの値があります。static、relative、absolute、fixed、sticky。それぞれの「何を基準に、どう配置されるのか」を正しく知れば、レイアウトの自由度は格段に上がります。
大事なのは、これらは「どれか1つを覚えれば良い」というものではなく、組み合わせて使うことで威力を発揮するということ。特に relative と absolute の親子関係は、バッジ、ツールチップ、オーバーレイなど、実務で毎日のように登場するパターンです。
static と relative ― フローの中にいる2つ
static:何もしない、がデフォルト
position: static はすべての要素の初期値です。HTMLに書いた順番どおりに上から下へ並ぶ——これが「通常フロー」であり、staticの動作そのものです。top、left、right、bottom、z-indexはいずれも効きません。
「何もしない」のがstaticなら、なぜ知る必要があるのか? それは「意図的にpositionをリセットしたい場面」があるからです。レスポンシブでPC版ではabsoluteだけどスマホでは通常フローに戻したい、というとき position: static を明示的に書きます。
relative:自分の「元の位置」を基準に動かす
position: relative を指定した要素は、通常フロー上の位置を確保したまま、そこからtop / left / right / bottomで指定した分だけ視覚的にズレます。重要なのは「元の場所にスペースが残る」ということ。周りの要素はrelativeで動いた要素がまだそこにいるかのように振る舞います。
.nudge {
position: relative;
top: 10px; /* 元の位置から下に10px */
left: 20px; /* 元の位置から右に20px */
}
/* ↑ 周りの要素の配置は一切変わらない */
ただし、実務で relative を「要素をズラす」目的で使う場面は実はそこまで多くありません。relativeの本当の出番は、次のセクションで解説する absolute の「基準点」を作ることです。これがpositionの最重要パターンです。
absolute ― フローを離脱して「親」を基準に配置する
position: absolute を指定すると、要素は通常フローから完全に離脱します。その要素があった場所にはスペースが残らず、他の要素は「そこに何もなかったかのように」詰まります。
ここで最も重要なのが「基準点(containing block)」の概念です。absoluteの要素がどこを基準にtop / leftで配置されるかは、直近のpositionがstatic以外の祖先要素によって決まります。もし祖先にpositionを指定した要素が1つもなければ、ビューポート(ブラウザの表示領域)全体が基準になります。
<!-- absoluteの子が画面の左上に飛んでいく… -->
<div class="card">
<span class="badge">NEW</span>
</div>
.card {
/* position の指定なし → static */
}
.badge {
position: absolute;
top: 0;
right: 0;
}
.card {
position: relative; /* ← これだけ! */
}
.badge {
position: absolute;
top: -8px;
right: -8px;
}
親に position: relative を付ける——これがabsoluteを制御する鉄則です。relativeは何もオフセットを指定しなければ見た目に一切影響しないので、「absoluteの基準を作るためだけに付ける」という使い方が現場では圧倒的に多いです。
absolute で上下左右中央に配置する
「要素を親の中央にぴったり配置したい」——これもabsoluteの定番パターンです。top: 50% と left: 50% で親の中心点に要素の左上角を持っていき、transform: translate(-50%, -50%) で自身のサイズの半分だけ戻す。これで要素のサイズに関係なく、常に中央に配置できます。
.parent {
position: relative;
height: 200px;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
absolute で全面を覆う(オーバーレイ)
もう1つの頻出パターンが、親要素と同じサイズのオーバーレイを作ること。top / right / bottom / left すべてを0にすると、absoluteの要素は基準となる親と同じ大きさに広がります。
.card {
position: relative;
}
.overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* inset: 0; と書くこともできる(ショートハンド) */
background: rgba(0, 0, 0, 0.5);
}
fixed と sticky ― スクロールに対抗する2つ
fixed:ビューポートに張り付く
position: fixed は、absoluteと似た動作をしますが、基準が親要素ではなくビューポート(ブラウザの表示領域)になります。スクロールしても要素は常に同じ位置に留まるため、固定ナビゲーション、トップに戻るボタン、チャットウィジェットなどに使われます。
.back-to-top {
position: fixed;
bottom: 24px;
right: 24px;
width: 48px;
height: 48px;
border-radius: 50%;
background: #2563eb;
color: white;
z-index: 1000;
}
fixedの注意点は2つあります。1つは、通常フローから完全に離脱するため、その分のスペースを他の要素が考慮しないこと。固定ヘッダーを設置したらbodyにpadding-topを付けるなどの調整が必要です。もう1つは、親要素に transform、filter、will-change などが設定されていると、fixedの基準がビューポートではなくその親に変わってしまう仕様です。これはハマりやすいポイントなので覚えておきましょう。
sticky:スクロールの途中から「張り付く」ハイブリッド
position: sticky は、relativeとfixedを合わせたような動作をします。通常はrelativeと同じくフローの中にいますが、スクロールして指定した閾値(top: 0 など)に達すると、そこに「貼り付いて」固定されます。
.nav {
position: sticky;
top: 0; /* ここに達したら貼り付く */
background: white;
z-index: 100;
}
このナビゲーションバーは position: sticky で固定されています。コンテンツをスクロールすると、上部に張り付いて離れません。
スクロール可能なコンテンツが続きます。sticky要素はスクロールコンテナの中で動作し、親要素の範囲を超えることはありません。
これは非常に自然なUXを提供します。ユーザーは常にナビゲーションにアクセスできますが、最初はコンテンツの一部として自然に表示されます。
fixedと違い、stickyはドキュメントフローの中に存在し続けるため、スペースの調整が不要です。
ここがコンテンツの最後です。
sticky が「効かない」ときのチェックリスト
stickyは便利ですが「効かない!」と悩む人が非常に多いプロパティでもあります。効かない原因のほとんどは、以下の3つに集約されます。
/* 原因1: 親に overflow: hidden / auto / scroll が指定されている */
.parent {
overflow: hidden; /* ← これがあると sticky は無効化 */
}
/* 原因2: top / left などの閾値を指定し忘れている */
.nav {
position: sticky;
/* top が未指定 → どこで貼り付くかわからない */
}
/* 原因3: 親の高さ = sticky要素の高さ(貼り付く余地がない) */
.parent {
height: 60px; /* 親の高さ */
}
.nav {
position: sticky;
top: 0;
height: 60px; /* ← 親と同じ → スクロール余地ゼロ */
}
現場で本当に使う実践パターン
パターン1:カード上のバッジ(relative + absolute)
通知バッジ、「SALE」ラベル、ステータスインジケーターなど、要素の「角」に何かを配置するのは最も頻出するpositionパターンです。
<div class="card">
<span class="badge">SALE</span>
<img src="..." alt="商品">
</div>
.card {
position: relative; /* 基準を作る */
overflow: hidden; /* はみ出しを防ぐ */
border-radius: 12px;
}
.badge {
position: absolute;
top: 12px;
left: 12px;
background: #ef4444;
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 700;
}
パターン2:ドロップダウンメニュー(relative + absolute)
ナビゲーションのホバーで表示されるドロップダウンも、position の組み合わせで実現します。トリガーとなる親をrelativeにし、メニュー本体をabsoluteで「ぶら下げる」構造です。
.nav-item {
position: relative; /* ドロップダウンの基準 */
}
.dropdown {
position: absolute;
top: 100%; /* 親の直下に配置 */
left: 0;
min-width: 200px;
background: white;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
border-radius: 8px;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s, visibility 0.2s;
}
.nav-item:hover .dropdown {
opacity: 1;
visibility: visible;
}
パターン3:セクション見出しの sticky(テーブルのようなUI)
アルファベット順のリスト、日付ごとのタイムラインなど、「見出しがスクロールに付いてくる」UIはstickyの最も実用的な使い方の1つです。
.section-header {
position: sticky;
top: 0;
background: #f8fafc;
padding: 8px 16px;
font-weight: 700;
border-bottom: 1px solid #e2e8f0;
z-index: 5;
}
z-index とスタッキングコンテキスト
positionを使いこなすうえで避けて通れないのが z-index の話です。「z-indexを9999にしたのに前に出ない!」——これは多くの人が経験する挫折ポイントですが、原因は「スタッキングコンテキスト(重ね合わせコンテキスト)」を理解していないことにあります。
まず前提として、z-indexは position: static 以外の要素にしか効きません。staticのままz-indexを付けても無視されます。
そして、z-indexの比較は「同じスタッキングコンテキスト内」でしか行われません。スタッキングコンテキストとは「重なり順を決める独立したグループ」のことで、z-indexが指定されたposition付き要素や、opacity が1未満の要素、transform が指定された要素などが新しいスタッキングコンテキストを作ります。
<!-- 親Aが z-index: 1 のスタッキングコンテキストを形成 -->
.parent-a { position: relative; z-index: 1; }
.child-a { position: absolute; z-index: 9999; }
<!-- 親Bが z-index: 2 → 親B全体が親Aの上 -->
.parent-b { position: relative; z-index: 2; }
/* child-a は 9999 でも、parent-a(z-index:1) の中にいるので
parent-b(z-index:2) より前には出られない */
/* 親に z-index を指定しなければ、
子要素は同じルートコンテキストで比較される */
.parent-a { position: relative; /* z-index なし */ }
.child-a { position: absolute; z-index: 10; }
.parent-b { position: relative; /* z-index なし */ }
まとめ
- static はデフォルト値。通常フローに従い、top/left/z-indexは効かない。レスポンシブでpositionをリセットしたい時に明示的に使う
- relative は元の位置にスペースを残したまま視覚的にズラせる。最大の用途は absolute の「基準点」を作ること
- absolute はフローから完全に離脱し、直近の「position: static 以外の祖先」を基準に配置される。親に relative を付けるのが鉄則
- fixed はビューポート固定。固定ヘッダーやFABボタンに使う。transform/filter を持つ親の中ではビューポート基準にならないので注意
- sticky は relative と fixed のハイブリッド。top 等の閾値指定が必須で、親の overflow: hidden や高さ不足で無効化されやすい
- z-index は同じスタッキングコンテキスト内でのみ比較される。数値を大きくしても効かない時は親のコンテキストを確認する
- 実務で最も頻出するのは relative + absolute の親子パターン。バッジ、ドロップダウン、オーバーレイ、中央配置——すべてこの組み合わせ