CSS position 完全理解 ― 5つの値の使い分けと実践パターン

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

position が「わからない」の正体

CSSを学び始めて最初に壁にぶつかるのが、この position プロパティではないでしょうか。要素がどこかに飛んでいく、思った場所に来ない、z-indexを100にしても前に出ない——こういう「なんで!?」の多くは、positionの仕組みを体系的に理解していないことが原因です。

positionには5つの値があります。staticrelativeabsolutefixedsticky。それぞれの「何を基準に、どう配置されるのか」を正しく知れば、レイアウトの自由度は格段に上がります。

大事なのは、これらは「どれか1つを覚えれば良い」というものではなく、組み合わせて使うことで威力を発揮するということ。特に relativeabsolute の親子関係は、バッジ、ツールチップ、オーバーレイなど、実務で毎日のように登場するパターンです。

▶ Live Demo — 4つのpositionを一画面で比較
① static(通常の流れ)
② relative(元の位置から移動)
③ absolute(親基準)
④ fixed風(右下に固定)
📌 ポイント
positionの値を変えると、要素が「通常フロー(普通に上から並ぶ流れ)」に参加するかどうかが変わります。staticとrelativeはフローに残り、absoluteとfixedはフローから外れます。stickyは状況に応じて切り替わるハイブリッド型です。

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で動いた要素がまだそこにいるかのように振る舞います。

relative の基本
.nudge {
  position: relative;
  top: 10px;   /* 元の位置から下に10px */
  left: 20px;  /* 元の位置から右に20px */
}
/* ↑ 周りの要素の配置は一切変わらない */

ただし、実務で relative を「要素をズラす」目的で使う場面は実はそこまで多くありません。relativeの本当の出番は、次のセクションで解説する absolute の「基準点」を作ることです。これがpositionの最重要パターンです。

▶ Live Demo — relativeで要素をズラす
通常の要素(static)
position: relative; top: 15px; left: 30px;
次の要素(↑の元のスペースがまだ残っている)
💡 現場の経験則
relativeでtop/leftを使って要素をズラすのは、微調整(アイコンの位置を数px直すなど)に限定しましょう。大きなレイアウト変更にrelativeのオフセットを使うと、他の要素との関係が見えづらくなり、保守性が下がります。

absolute ― フローを離脱して「親」を基準に配置する

position: absolute を指定すると、要素は通常フローから完全に離脱します。その要素があった場所にはスペースが残らず、他の要素は「そこに何もなかったかのように」詰まります。

ここで最も重要なのが「基準点(containing block)」の概念です。absoluteの要素がどこを基準にtop / leftで配置されるかは、直近のpositionがstatic以外の祖先要素によって決まります。もし祖先にpositionを指定した要素が1つもなければ、ビューポート(ブラウザの表示領域)全体が基準になります。

❌ よくある失敗:親にpositionがない
<!-- absoluteの子が画面の左上に飛んでいく… -->
<div class="card">
  <span class="badge">NEW</span>
</div>

.card {
  /* position の指定なし → static */
}
.badge {
  position: absolute;
  top: 0;
  right: 0;
}
✅ 正解:親に position: relative を付ける
.card {
  position: relative;  /* ← これだけ! */
}
.badge {
  position: absolute;
  top: -8px;
  right: -8px;
}

親に position: relative を付ける——これがabsoluteを制御する鉄則です。relativeは何もオフセットを指定しなければ見た目に一切影響しないので、「absoluteの基準を作るためだけに付ける」という使い方が現場では圧倒的に多いです。

▶ Live Demo — relative + absolute でバッジを配置
NEW
プレミアムプラン
全機能が使い放題。チームでの利用にも最適です。

absolute で上下左右中央に配置する

「要素を親の中央にぴったり配置したい」——これもabsoluteの定番パターンです。top: 50% と left: 50% で親の中心点に要素の左上角を持っていき、transform: translate(-50%, -50%) で自身のサイズの半分だけ戻す。これで要素のサイズに関係なく、常に中央に配置できます。

absolute + transform で完全中央配置
.parent {
  position: relative;
  height: 200px;
}
.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
▶ Live Demo — 中央配置
完全に中央です ✓

absolute で全面を覆う(オーバーレイ)

もう1つの頻出パターンが、親要素と同じサイズのオーバーレイを作ること。top / right / bottom / left すべてを0にすると、absoluteの要素は基準となる親と同じ大きさに広がります。

absolute でオーバーレイを作る
.card {
  position: relative;
}
.overlay {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  /* inset: 0; と書くこともできる(ショートハンド) */
  background: rgba(0, 0, 0, 0.5);
}
▶ Live Demo — 画像の上にオーバーレイ
テキストオーバーレイ
💡 現場の経験則
inset: 0 は top / right / bottom / left すべてに0を一括指定するショートハンドです。モダンブラウザすべてでサポートされているので、実務では積極的に使って問題ありません。コードが短く読みやすくなります。

fixed と sticky ― スクロールに対抗する2つ

fixed:ビューポートに張り付く

position: fixed は、absoluteと似た動作をしますが、基準が親要素ではなくビューポート(ブラウザの表示領域)になります。スクロールしても要素は常に同じ位置に留まるため、固定ナビゲーション、トップに戻るボタン、チャットウィジェットなどに使われます。

fixed でトップに戻るボタンを配置
.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つは、親要素に transformfilterwill-change などが設定されていると、fixedの基準がビューポートではなくその親に変わってしまう仕様です。これはハマりやすいポイントなので覚えておきましょう。

sticky:スクロールの途中から「張り付く」ハイブリッド

position: sticky は、relativeとfixedを合わせたような動作をします。通常はrelativeと同じくフローの中にいますが、スクロールして指定した閾値(top: 0 など)に達すると、そこに「貼り付いて」固定されます。

sticky ナビゲーションの基本
.nav {
  position: sticky;
  top: 0;       /* ここに達したら貼り付く */
  background: white;
  z-index: 100;
}
▶ Live Demo — stickyヘッダー(スクロールしてみてください)
↓ 下にスクロールしてください
MyApp

このナビゲーションバーは position: sticky で固定されています。コンテンツをスクロールすると、上部に張り付いて離れません。

スクロール可能なコンテンツが続きます。sticky要素はスクロールコンテナの中で動作し、親要素の範囲を超えることはありません。

これは非常に自然なUXを提供します。ユーザーは常にナビゲーションにアクセスできますが、最初はコンテンツの一部として自然に表示されます。

fixedと違い、stickyはドキュメントフローの中に存在し続けるため、スペースの調整が不要です。

ここがコンテンツの最後です。

sticky が「効かない」ときのチェックリスト

stickyは便利ですが「効かない!」と悩む人が非常に多いプロパティでもあります。効かない原因のほとんどは、以下の3つに集約されます。

❌ 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;   /* ← 親と同じ → スクロール余地ゼロ */
}
⚠️ 注意
stickyの要素は「親要素の範囲内」でしか貼り付けません。親の底辺に達すると、stickyは解除されて一緒にスクロールアウトします。これは仕様です。Flexboxで子要素がstretch(デフォルト)になっていると、親と同じ高さに引き伸ばされてstickyが効かないことがよくあります。align-self: flex-start を指定しましょう。

現場で本当に使う実践パターン

パターン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;
}
▶ Live Demo — ホバーでドロップダウン表示
プロフィール
設定
ログアウト

パターン3:セクション見出しの sticky(テーブルのようなUI)

アルファベット順のリスト、日付ごとのタイムラインなど、「見出しがスクロールに付いてくる」UIはstickyの最も実用的な使い方の1つです。

sticky セクション見出し
.section-header {
  position: sticky;
  top: 0;
  background: #f8fafc;
  padding: 8px 16px;
  font-weight: 700;
  border-bottom: 1px solid #e2e8f0;
  z-index: 5;
}
▶ Live Demo — stickyセクション見出し(スクロールで確認)
A
Apple
Avocado
Almond
Apricot
B
Banana
Blueberry
Blackberry
Boysenberry
C
Cherry
Coconut
Cranberry
Cantaloupe
D
Dragonfruit
Date
Durian
Damson

z-index とスタッキングコンテキスト

positionを使いこなすうえで避けて通れないのが z-index の話です。「z-indexを9999にしたのに前に出ない!」——これは多くの人が経験する挫折ポイントですが、原因は「スタッキングコンテキスト(重ね合わせコンテキスト)」を理解していないことにあります。

まず前提として、z-indexは position: static 以外の要素にしか効きません。staticのままz-indexを付けても無視されます。

そして、z-indexの比較は「同じスタッキングコンテキスト内」でしか行われません。スタッキングコンテキストとは「重なり順を決める独立したグループ」のことで、z-indexが指定されたposition付き要素や、opacity が1未満の要素、transform が指定された要素などが新しいスタッキングコンテキストを作ります。

❌ z-index: 9999 でも前に出ない例
<!-- 親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 を削除する
/* 親に z-index を指定しなければ、
   子要素は同じルートコンテキストで比較される */
.parent-a { position: relative; /* z-index なし */ }
.child-a  { position: absolute; z-index: 10; }

.parent-b { position: relative; /* z-index なし */ }
💡 現場の経験則
z-indexは「なるべく使わない」のが理想です。使う場合もHTML上の出現順を利用して自然に重なりを制御し、どうしても必要なときだけ最小限の値を付ける。チームで開発するなら、z-indexの値をルール化しておくと混乱を防げます(例:ドロップダウン=100、モーダル=1000、トースト=1100など)。
▶ Live Demo — z-index の重なり順
z-index: 1
z-index: 2
z-index: 3

まとめ

  • 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 の親子パターン。バッジ、ドロップダウン、オーバーレイ、中央配置——すべてこの組み合わせ