はじめに#
CSSでWebページを構築していると、「要素を特定の位置に固定したい」「要素を重ねて表示したい」「スクロールに追従するヘッダーを作りたい」といった場面に遭遇します。これらの実装には、positionプロパティの理解が不可欠です。
positionプロパティは、要素が文書内でどのように配置されるかを制御するCSSの重要なプロパティです。この仕組みを正しく理解することで、複雑なUIレイアウトを自在に実装できるようになります。
本記事では、positionプロパティについて以下の内容を解説します。
- 5つの
position値(static・relative・absolute・fixed・sticky)の違い
- 基準となる包含ブロック(Containing Block)の決定ルール
z-indexによる重なり順の制御と重ね合わせコンテキスト
- 実務でよく使うレイアウトパターン(固定ヘッダー、モーダル、ツールチップなど)
前提条件#
本記事を読み進めるにあたり、以下の知識があることを前提としています。
- HTMLの基本的なタグ構造(
div、span、headerなど)
- CSSの基本構文(セレクタ・プロパティ・値の関係)
- CSSボックスモデルの基本的な理解
動作確認環境#
- Google Chrome 131以降
- Firefox 133以降
- Safari 18以降
- Microsoft Edge 131以降
positionプロパティとは#
MDN Web Docsでは、positionプロパティを次のように説明しています。
positionはCSSのプロパティで、文書内で要素がどのように配置されるかを設定します。top、right、bottom、leftの各プロパティが、配置された要素の最終的な位置を決定します。
positionプロパティには5つの値があり、それぞれ異なる配置方法を提供します。
| 値 |
説明 |
通常フロー |
オフセット適用 |
static |
デフォルト値。通常のフローに従って配置 |
あり |
なし |
relative |
通常の位置から相対的にオフセット |
あり |
あり |
absolute |
包含ブロックに対して絶対配置 |
なし |
あり |
fixed |
ビューポートに対して固定配置 |
なし |
あり |
sticky |
スクロールに応じて相対/固定を切り替え |
あり |
あり |
位置指定要素とは#
positionの値がstatic以外の要素を「位置指定要素(positioned element)」と呼びます。位置指定要素には、top、right、bottom、leftプロパティでオフセットを指定できます。
1
2
3
4
5
|
.positioned {
position: relative; /* static以外なので位置指定要素 */
top: 20px; /* オフセットが適用される */
left: 30px;
}
|
position: static(デフォルト)#
staticはpositionプロパティのデフォルト値です。要素は文書の通常フローに従って配置されます。
staticの特性#
| 特性 |
説明 |
| フロー |
通常の文書フローに従う |
| オフセット |
top、right、bottom、leftは無効 |
| z-index |
適用されない |
| 包含ブロック |
影響しない |
staticの使用例#
1
2
3
4
5
|
<div class="container">
<div class="box static-box">Static Box 1</div>
<div class="box static-box">Static Box 2</div>
<div class="box static-box">Static Box 3</div>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
.container {
background: #f0f0f0;
padding: 20px;
}
.box {
width: 150px;
height: 80px;
margin: 10px 0;
display: flex;
align-items: center;
justify-content: center;
}
.static-box {
position: static; /* デフォルト値なので省略可能 */
background: #3498db;
color: white;
/* top: 20px; は無効 */
}
|
static要素ではtopやleftなどのオフセットプロパティは無視されます。通常のレイアウトでは明示的に指定する必要はありません。
position: relative(相対配置)#
relativeは要素を通常の位置から相対的にオフセットさせます。重要な点として、要素が本来占めていた空間は維持されます。
relativeの特性#
| 特性 |
説明 |
| フロー |
通常の文書フローを維持 |
| オフセット基準 |
要素の本来の位置 |
| 元の空間 |
維持される(他の要素に影響しない) |
| z-index |
適用可能 |
| 重ね合わせコンテキスト |
z-indexがauto以外で生成 |
relativeの動作イメージ#
flowchart LR
subgraph before["オフセット前"]
A1["Box 1"]
A2["Box 2"]
A3["Box 3"]
end
subgraph after["relative + オフセット後"]
B1["Box 1"]
B2["Box 2<br>(移動)"]
B3["Box 3"]
B2_space["元の空間<br>(維持)"]
end
before --> afterrelativeの使用例#
1
2
3
4
5
|
<div class="container">
<div class="box">Box 1</div>
<div class="box relative-box">Box 2 (relative)</div>
<div class="box">Box 3</div>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
.box {
width: 100px;
height: 100px;
background: #3498db;
color: white;
display: inline-block;
margin: 5px;
text-align: center;
line-height: 100px;
}
.relative-box {
position: relative;
top: 30px; /* 本来の位置から下に30px */
left: 30px; /* 本来の位置から右に30px */
background: #e74c3c;
}
|
上記の例では、Box 2は本来の位置から下に30px、右に30pxオフセットされますが、Box 3の位置には影響しません。
relativeの主な用途#
relativeは主に以下の用途で使用します。
- absoluteの基準点として使用:子要素を
absoluteで配置する際の包含ブロックを作成
- 微調整:要素の位置を微調整したい場合
- 重ね合わせコンテキストの生成:
z-indexを適用するための基盤作り
position: absolute(絶対配置)#
absoluteは要素を通常のフローから完全に取り除き、包含ブロックに対して絶対的に配置します。
absoluteの特性#
| 特性 |
説明 |
| フロー |
通常の文書フローから除外 |
| オフセット基準 |
位置指定された直近の祖先(包含ブロック) |
| 元の空間 |
維持されない(他の要素が詰める) |
| 幅 |
内容に合わせて縮小(autoの場合) |
| z-index |
適用可能 |
包含ブロックの決定ルール#
absolute要素の配置基準となる包含ブロックは、以下のルールで決定されます。
flowchart TD
A["position: absolute の要素"] --> B{"直近の祖先に<br>position: static以外<br>の要素はある?"}
B -->|はい| C["その祖先の<br>パディングボックスが<br>包含ブロック"]
B -->|いいえ| D{"祖先にtransform等の<br>プロパティが設定<br>されている?"}
D -->|はい| E["その祖先の<br>パディングボックスが<br>包含ブロック"]
D -->|いいえ| F["初期包含ブロック<br>(ビューポート)"]包含ブロックを形成する主な条件は以下の通りです。
positionがstatic以外(relative、absolute、fixed、sticky)
transform、perspective、filterがnone以外
containがlayout、paint、strict、contentのいずれか
will-changeで上記のプロパティを指定
absoluteの使用例#
1
2
3
4
|
<div class="parent">
<div class="child absolute-child">Absolute Child</div>
<p>親要素のコンテンツです。absolute要素はフローから除外されます。</p>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
.parent {
position: relative; /* 包含ブロックを形成 */
width: 400px;
height: 200px;
background: #ecf0f1;
padding: 20px;
}
.absolute-child {
position: absolute;
top: 10px; /* 親のパディングボックス上端から10px */
right: 10px; /* 親のパディングボックス右端から10px */
width: 120px;
height: 60px;
background: #e74c3c;
color: white;
display: flex;
align-items: center;
justify-content: center;
}
|
absoluteでの要素サイズ制御#
absolute要素では、topとbottom、またはleftとrightを同時に指定することで、要素のサイズを包含ブロックに基づいて制御できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/* 包含ブロックいっぱいに広げる */
.full-cover {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
/* width: 100%; height: 100%; と同等 */
}
/* 余白を残して広げる */
.with-margin {
position: absolute;
top: 20px;
right: 20px;
bottom: 20px;
left: 20px;
}
|
absoluteによる中央配置#
absoluteを使って要素を中央に配置する一般的なパターンです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/* 方法1: transform を使用 */
.center-transform {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* 方法2: inset と margin: auto を使用 */
.center-inset {
position: absolute;
inset: 0; /* top, right, bottom, left すべて0 */
width: 200px;
height: 100px;
margin: auto;
}
|
position: fixed(固定配置)#
fixedは要素をビューポートに対して固定配置します。スクロールしても画面上の同じ位置に留まります。
fixedの特性#
| 特性 |
説明 |
| フロー |
通常の文書フローから除外 |
| オフセット基準 |
ビューポート(通常時) |
| スクロール |
ビューポートに固定(追従しない) |
| 重ね合わせコンテキスト |
常に生成 |
fixedの包含ブロックに関する注意#
通常、fixed要素の包含ブロックはビューポートですが、祖先要素に以下のプロパティが設定されている場合、その祖先が包含ブロックになります。
transformがnone以外
perspectiveがnone以外
filterがnone以外
contain: paint
will-changeで上記のプロパティを指定
1
2
3
4
|
/* 注意: 親にtransformがあるとfixedが期待通り動作しない */
.parent-with-transform {
transform: translateZ(0); /* この親の子のfixedは親基準になる */
}
|
fixedの使用例:固定ヘッダー#
1
2
3
4
5
6
7
8
9
10
|
<header class="fixed-header">
<nav>
<a href="#home">ホーム</a>
<a href="#about">概要</a>
<a href="#contact">お問い合わせ</a>
</nav>
</header>
<main class="main-content">
<!-- メインコンテンツ -->
</main>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
.fixed-header {
position: fixed;
top: 0;
left: 0;
right: 0; /* 横幅いっぱいに広げる */
height: 60px;
background: #2c3e50;
color: white;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000; /* 他の要素より前面に */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.fixed-header nav a {
color: white;
text-decoration: none;
margin: 0 20px;
}
.main-content {
margin-top: 60px; /* ヘッダーの高さ分の余白 */
padding: 20px;
}
|
fixedの使用例:トップに戻るボタン#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
.back-to-top {
position: fixed;
bottom: 30px;
right: 30px;
width: 50px;
height: 50px;
background: #3498db;
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
z-index: 999;
transition: opacity 0.3s, transform 0.3s;
}
.back-to-top:hover {
transform: scale(1.1);
}
|
position: sticky(粘着配置)#
stickyはrelativeとfixedの特性を組み合わせた配置方法です。通常はrelativeのように振る舞い、指定した閾値に達するとfixedのように振る舞います。
stickyの特性#
| 特性 |
説明 |
| フロー |
通常の文書フローを維持 |
| オフセット基準 |
直近のスクロールする祖先 |
| 閾値 |
top、bottom等で指定 |
| 重ね合わせコンテキスト |
常に生成 |
stickyの動作条件#
stickyが正しく動作するためには、以下の条件を満たす必要があります。
- 閾値の指定:
top、right、bottom、leftのうち少なくとも1つを指定
- 親の高さ:親要素に十分な高さがある
- overflow:祖先の
overflowがvisibleである(hidden、scroll、autoだと動作しない場合がある)
stickyの動作イメージ#
flowchart TD
subgraph scroll["スクロール状態による動作"]
A["閾値に達していない"] --> |"relative として動作"| B["通常のフローで配置"]
C["閾値に達した"] --> |"fixed として動作"| D["スクロール祖先に固定"]
E["親の下端に達した"] --> |"スクロールアウト"| F["親と共にスクロール"]
endstickyの使用例:粘着ヘッダー#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<div class="scroll-container">
<section>
<h2 class="sticky-header">セクション A</h2>
<div class="content">
<p>セクションAのコンテンツです。</p>
<p>スクロールすると見出しが固定されます。</p>
</div>
</section>
<section>
<h2 class="sticky-header">セクション B</h2>
<div class="content">
<p>セクションBのコンテンツです。</p>
</div>
</section>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
.scroll-container {
height: 400px;
overflow-y: auto;
}
section {
min-height: 300px;
}
.sticky-header {
position: sticky;
top: 0; /* 閾値: 上端から0pxで固定 */
background: #3498db;
color: white;
padding: 15px 20px;
margin: 0;
z-index: 10;
}
.content {
padding: 20px;
background: #ecf0f1;
}
|
stickyの使用例:サイドバーナビゲーション#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
.sidebar {
width: 250px;
float: left;
}
.sidebar-nav {
position: sticky;
top: 80px; /* 固定ヘッダーの下に配置 */
max-height: calc(100vh - 100px);
overflow-y: auto;
}
.main-article {
margin-left: 270px;
}
|
z-indexと重ね合わせコンテキスト#
位置指定要素の重なり順を制御するには、z-indexプロパティを使用します。
z-indexの基本#
z-indexは位置指定要素(positionがstatic以外)に対してのみ有効です。値が大きいほど前面に表示されます。
1
2
3
4
5
6
7
8
9
10
11
|
.box-back {
position: relative;
z-index: 1; /* 背面 */
background: #3498db;
}
.box-front {
position: relative;
z-index: 2; /* 前面 */
background: #e74c3c;
}
|
重ね合わせコンテキスト(Stacking Context)#
重ね合わせコンテキストは、z-indexの比較が行われる独立した空間です。異なる重ね合わせコンテキストに属する要素のz-indexは直接比較されません。
flowchart TB
subgraph root["ルート重ね合わせコンテキスト"]
A["要素A<br>z-index: 1"]
subgraph context1["コンテキスト1 (z-index: 2)"]
B["要素B<br>z-index: 100"]
end
subgraph context2["コンテキスト2 (z-index: 3)"]
C["要素C<br>z-index: 1"]
end
end上記の例では、要素Bのz-indexが100でも、コンテキスト1全体のz-indexが2なので、コンテキスト2の要素C(z-index: 1)より背面に表示されます。
重ね合わせコンテキストを生成する条件#
以下の条件で新しい重ね合わせコンテキストが生成されます。
| 条件 |
例 |
ルート要素(<html>) |
- |
positionがrelative/absoluteでz-indexがauto以外 |
position: relative; z-index: 1; |
position: fixed |
position: fixed; |
position: sticky |
position: sticky; top: 0; |
opacityが1未満 |
opacity: 0.9; |
transformがnone以外 |
transform: scale(1); |
filterがnone以外 |
filter: blur(0); |
isolation: isolate |
isolation: isolate; |
z-indexのベストプラクティス#
z-indexの管理を容易にするため、以下のプラクティスを推奨します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
:root {
/* z-indexスケールを定義 */
--z-dropdown: 100;
--z-sticky: 200;
--z-fixed: 300;
--z-modal-backdrop: 400;
--z-modal: 500;
--z-tooltip: 600;
--z-toast: 700;
}
.dropdown-menu {
z-index: var(--z-dropdown);
}
.sticky-header {
z-index: var(--z-sticky);
}
.modal-backdrop {
z-index: var(--z-modal-backdrop);
}
.modal {
z-index: var(--z-modal);
}
|
実践的なレイアウトパターン#
パターン1: モーダルダイアログ#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<div class="modal-backdrop">
<div class="modal">
<div class="modal-header">
<h2>確認</h2>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<p>この操作を実行しますか?</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary">キャンセル</button>
<button class="btn btn-primary">実行</button>
</div>
</div>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
.modal-backdrop {
position: fixed;
inset: 0; /* top, right, bottom, left すべて0 */
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal {
position: relative; /* 内部のabsolute要素の基準 */
width: 90%;
max-width: 500px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
overflow: hidden;
}
.modal-close {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
}
.modal-header {
padding: 20px;
border-bottom: 1px solid #eee;
}
.modal-body {
padding: 20px;
}
.modal-footer {
padding: 15px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 10px;
}
|
パターン2: ツールチップ#
1
2
3
4
5
6
|
<div class="tooltip-container">
<button class="tooltip-trigger">ヘルプ</button>
<div class="tooltip">
ここに説明テキストが表示されます。
</div>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
.tooltip-container {
position: relative;
display: inline-block;
}
.tooltip {
position: absolute;
bottom: 100%; /* トリガーの上に配置 */
left: 50%;
transform: translateX(-50%);
padding: 8px 12px;
background: #2c3e50;
color: white;
font-size: 14px;
border-radius: 4px;
white-space: nowrap;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s, visibility 0.2s;
z-index: 100;
margin-bottom: 8px;
}
/* 矢印 */
.tooltip::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: #2c3e50;
}
.tooltip-container:hover .tooltip {
opacity: 1;
visibility: visible;
}
|
パターン3: カード内のバッジ#
1
2
3
4
5
6
7
8
|
<div class="card">
<span class="badge">NEW</span>
<img src="product.jpg" alt="商品画像" class="card-image">
<div class="card-content">
<h3>商品名</h3>
<p>商品の説明文</p>
</div>
</div>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
.card {
position: relative; /* バッジの包含ブロック */
width: 300px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.badge {
position: absolute;
top: 10px;
right: 10px;
padding: 4px 12px;
background: #e74c3c;
color: white;
font-size: 12px;
font-weight: bold;
border-radius: 4px;
z-index: 1;
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card-content {
padding: 15px;
}
|
パターン4: スティッキーテーブルヘッダー#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
.table-container {
max-height: 400px;
overflow-y: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
thead th {
position: sticky;
top: 0;
background: #3498db;
color: white;
padding: 12px 15px;
text-align: left;
z-index: 1;
}
tbody td {
padding: 10px 15px;
border-bottom: 1px solid #eee;
}
|
よくある問題と解決策#
問題1: absoluteの位置がずれる#
原因: 包含ブロックとなる祖先要素が存在しない
解決策: 親要素にposition: relativeを設定
1
2
3
4
5
6
7
8
9
|
/* Before: absoluteがビューポート基準になってしまう */
.parent {
/* position未指定 */
}
/* After: 親が包含ブロックになる */
.parent {
position: relative;
}
|
問題2: fixedがビューポートに固定されない#
原因: 祖先要素にtransformやfilterが設定されている
解決策: 該当のプロパティを削除するか、要素の構造を変更
1
2
3
4
5
6
|
/* 問題のあるケース */
.ancestor {
transform: translateZ(0); /* これがfixedの動作を変える */
}
/* 解決策: transformを使わないか、fixed要素を外に出す */
|
問題3: stickyが効かない#
原因: 親要素にoverflow: hiddenが設定されている、または閾値が未指定
解決策:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/* Before */
.parent {
overflow: hidden; /* stickyを無効化 */
}
.sticky-element {
position: sticky;
/* top未指定 */
}
/* After */
.parent {
overflow: visible;
}
.sticky-element {
position: sticky;
top: 0; /* 閾値を指定 */
}
|
問題4: z-indexが効かない#
原因: 要素が位置指定されていない、または重ね合わせコンテキストの影響
解決策: positionを設定し、重ね合わせコンテキストを確認
1
2
3
4
5
6
7
8
9
10
|
/* Before: staticでは z-index無効 */
.element {
z-index: 100; /* 無効 */
}
/* After */
.element {
position: relative;
z-index: 100; /* 有効 */
}
|
まとめ#
positionプロパティの理解は、複雑なUIレイアウトを実装する上で不可欠です。本記事で解説した内容を整理すると以下のようになります。
| 値 |
主な用途 |
ポイント |
static |
デフォルト配置 |
オフセット・z-index無効 |
relative |
微調整、absoluteの基準 |
元の空間を維持 |
absolute |
自由配置、モーダル |
包含ブロックを意識 |
fixed |
固定ヘッダー、フローティングボタン |
transformに注意 |
sticky |
スティッキーヘッダー |
閾値とoverflow確認 |
z-indexと重ね合わせコンテキストの概念を理解することで、要素の重なり順を正確に制御できます。実践では、CSS変数を使ったz-indexスケールの管理を推奨します。
参考リンク#