- CSS
CSS positionの使い方|relative・absolute・fixed
CSSのpositionとは、要素を「どこを基準に」「どう配置するか」を決めるプロパティです。 値は static・relative・absolute・fixed・sticky の5つで、違いは「基準になる位置」「元の場所が残るかどうか」「スクロールしたときの動き」の3点に集約できます。この3点を押さえると、画像の上に文字を重ねる、右上にバッジを付ける、ヘッダーを画面上部に固定する、といった配置を迷わず書けるようになります。
この記事では、5つの値を同じHTMLで動かし、Playwright(Chromium 141.0.7390.37)で要素の座標を測った結果を載せています。あわせて、よく使う5つのレイアウトのコピペ用コード、inset の書き方、そして「z-indexが効かない」「stickyが止まらない」「fixedがずれる」を再現して直す手順までを1本にまとめました。
positionの早見表(5つの値の違い)
| 値 | 基準になる位置 | 元の場所は残るか | スクロールしたときの動き | 主な用途 |
|---|---|---|---|---|
static(初期値) |
通常の並び順(基準なし) | 残る(そもそも動かない) | ページと一緒に流れる | 指定を元に戻す |
relative |
自分が本来いた位置 | 残る(空白として確保される) | ページと一緒に流れる | 少しずらす、absolute の基準になる親 |
absolute |
position が static 以外の、いちばん近い祖先(なければページ全体) | 残らない(後ろの要素が詰まる) | 基準の祖先と一緒に流れる | 重ねる、バッジ、キャプション、中央配置 |
fixed |
画面(ビューポート) | 残らない | 動かない(画面に貼り付く) | 固定ヘッダー、追従ボタン、モーダル |
sticky |
通常の位置 → しきい値に達したらスクロールする枠 | 残る | 途中まで流れ、しきい値で止まる | 追従サイドバー、表の見出し行 |
top・right・bottom・left(まとめて書くなら inset)と z-index は、static 以外の4つの値で働きます。 static の要素に top: 20px と書いても位置は変わりません。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS positionの使い方|relative・absolute・fixed」
5つの値を同じHTMLで動かして測った結果
まず、違いを数字で確かめます。A・B・C の3つの箱を縦に並べ、B だけ position を切り替えて top: 20px; left: 30px を指定しました。
<!DOCTYPE html>
<style>
body { margin: 0; }
.wrap { position: relative; margin: 100px 0 0 50px; width: 400px; border: 1px solid #999; }
.a, .b, .c { height: 50px; }
.b { position: relative; /* ← ここを5つの値で切り替える */
top: 20px; left: 30px; width: 200px; }
.tall { height: 3000px; }
</style>
<div class="wrap">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
<div class="tall"></div>
</div>
画面サイズ 1000×600px で、getBoundingClientRect() の値(画面左上からの座標)を取りました。
| B の position | B の位置(x, y) | すぐ後ろの C の y | 300pxスクロール後の B の y | 読み取れること |
|---|---|---|---|---|
| static | (51, 151) | 201 | -149 | top・left は無視される |
| relative | (81, 171) | 201 | -129 | 本来の位置から右へ30・下へ20。C は動かない(元の場所が残る) |
| absolute | (81, 121) | 151 | -179 | 親 .wrap の左上から30・20。C が上に詰まる |
| fixed | (30, 20) | 151 | 20 | 画面の左上から30・20。スクロールしても同じ場所 |
| sticky | (51, 151) | 201 | 20 | 最初は通常の位置。スクロールすると上から20pxで止まる |
relative と absolute は同じ top: 20px; left: 30px でも、relative は「自分の元の位置」から、absolute は「親の内側の角」から数えるため、y が 171 と 121 で50px違います(Aの高さぶん)。sticky の x が 51 のままなのは、横スクロールが発生しておらず left のしきい値に達していないためです。
値ごとの使い方
static:初期値に戻す
何も指定しない状態が static です。書く場面は「メディアクエリで position を解除したいとき」がほとんどです。
.side { position: sticky; top: 16px; }
@media (max-width: 768px) {
.side { position: static; } /* 幅が狭いときは追従をやめる */
}
relative:元の場所を残したまま少しずらす
relative は、元の場所を確保したまま見た目だけ動かします。 周りの要素は「動く前の位置にまだいる」ものとして並ぶため、レイアウトが崩れにくいのが特徴です。使いどころは次の2つです。
/* 1. アイコンを文字のベースラインに合わせて1pxだけ下げる */
.icon { position: relative; top: 1px; }
/* 2. 子の absolute の基準にする(top などは書かない) */
.card { position: relative; }
2 の「基準にするためだけの relative」が、実際にはいちばん多い使い方です。top などを書かなければ、relative を付けても見た目は変わりません。
absolute:親の中の好きな位置に重ねる
absolute は、要素を通常の並びから取り出して、基準になる祖先の内側に配置します。 元の場所は残らず、ほかの要素の上に重なります。
.card { position: relative; } /* 基準 */
.card__label { position: absolute; top: 8px; left: 8px; }
覚えておきたい性質が3つあります。
| 性質 | 実測した結果 | 補足 |
|---|---|---|
| 基準の祖先がないと、ページ全体が基準になる | 親に relative なしで right: 0; bottom: 0 → 画面右下 (950, 550) に配置。親に relative を足すと親の右下 (400, 350) |
「どこかへ飛んでいった」ときはまず親を確認 |
| 幅が中身の大きさに縮む | 幅400pxの親の中で、通常の div は幅400px、absolute にすると幅22.2px(文字3つぶん) | 幅を広げたいときは width: 100% か left: 0; right: 0 |
| top・left を書かないと、本来の位置にとどまる | 本来の位置 (50, 130) のまま。後ろの要素は同じ (50, 130) に詰まって重なる | 「その場で浮かせる」用途に使える |
fixed:画面に固定する
fixed は画面(ビューポート)を基準に配置し、スクロールしても動きません。 ヘッダー、ページ上部へ戻るボタン、モーダルの背景などに使います。
.to-top { position: fixed; right: 16px; bottom: 16px; }
fixed にした要素は元の場所を残さないため、固定ヘッダーの下に本文の先頭がもぐり込みます。ヘッダーの高さぶんの余白を本文側に足すのが定番です(後述のコピペ用コード参照)。
sticky:途中から貼り付く
sticky は「普段は通常の位置、スクロールしてしきい値に達したら固定」という動きをします。 top などのしきい値を必ず1つ書きます。固定されるのは、親要素の範囲の中だけです。
.table thead th { position: sticky; top: 0; }
基準になる要素(包含ブロック)の決まり方
position の挙動は、「何を基準にするか」でほぼ決まります。この基準のことを包含ブロックと呼びます。MDNの「Layout and the containing block」の説明を表にまとめました。
| position | 基準(包含ブロック) | % 指定(top: 50% など)の基準 |
|---|---|---|
| static / relative / sticky | いちばん近いブロックの祖先のコンテンツ領域 | その祖先の幅・高さ |
| absolute | position が static 以外の、いちばん近い祖先のパディング領域 | その祖先のパディングを含む幅・高さ |
| fixed | ビューポート(画面) | 画面の幅・高さ |
| absolute / fixed の例外 | 祖先に transform・filter・backdrop-filter・perspective・rotate・scale・translate・contain(layout・paint・strict・content)・will-change(transform など)・content-visibility: auto があると、その祖先が基準になる |
その祖先のパディングを含む幅・高さ |
absolute は「パディング領域」が基準なので、親に padding: 20px があっても top: 0; left: 0 は親の枠線のすぐ内側(パディングの外側の角)に付きます。パディングの内側に置きたいときは top: 20px; left: 20px のように同じ値を足します。
relative の % 指定には1つ特徴があります。親の高さが auto(中身まかせ)のとき、top: 50% は0として扱われます。 実測でも、高さ指定のない親の中で position: relative; top: 50%; left: 50% を指定すると、x は親の幅の半分(200)に動き、y は 0 のままでした。縦に % で動かしたいときは、親に高さを指定します。
top・right・bottom・left と inset の書き方
位置の指定は4つのプロパティで行います。inset はこの4つをまとめて書けるショートハンドで、値の並びは margin や padding と同じ「上 → 右 → 下 → 左」です。
幅300px・高さ200pxの親(position: relative)の中に absolute の子を置き、子の位置とサイズを測りました。
| 書き方 | 意味 | 子の実測(x, y, 幅, 高さ) |
|---|---|---|
top: 0; right: 0; bottom: 0; left: 0 |
4辺とも0 | (0, 0, 300, 200) |
inset: 0 |
上と同じ。親いっぱいに広がる | (0, 0, 300, 200) |
inset: 10px 20px |
上下10px・左右20px | (20, 10, 260, 180) |
inset: 10px 20px 30px 40px |
上10・右20・下30・左40 | (40, 10, 240, 160) |
inset: auto 0 0 auto(幅・高さ50px) |
右下の角に付ける | (250, 150, 50, 50) |
inset-block: 10px; inset-inline: 20px |
論理プロパティ版(上下・左右) | (20, 10, 260, 180) |
ポイントは3つです。
inset: 0は「親と同じ大きさに広げる」定番の書き方です。画像の上に半透明の膜を重ねる、カード全体をクリック範囲にする、といった場面で使います。- 向かい合う辺を両方指定すると、幅や高さが自動で決まります。
left: 0; right: 0なら幅は親と同じ、という具合です。widthも同時に指定した場合は、横書きでは left が優先されます。 autoは「指定しない」の意味です。inset: auto 0 0 autoは「下と右だけ0」になります。
inset-block と inset-inline は、縦書きや右から左へ書く言語でも「行の進む向き」に合わせて働く論理プロパティです。横書きの日本語ページでは、inset-block が上下、inset-inline が左右にあたります。
コピペ用コード5選
ここからは、そのまま貼って使える形で載せます。座標はすべて同じ環境(Chromium 141、画面 1000×600px)での実測です。
1. 画像に文字を重ねる
<figure class="card">
<img src="photo.jpg" alt="窓辺の観葉植物" width="320" height="200">
<figcaption class="card__caption">窓辺のグリーン特集</figcaption>
</figure>
.card { position: relative; width: 320px; margin: 0; }
.card img { display: block; width: 100%; height: 200px; object-fit: cover; }
.card__caption {
position: absolute;
inset: auto 0 0 0; /* 下端に、左右いっぱい */
padding: 12px 16px;
color: #fff;
background: linear-gradient(transparent, rgb(0 0 0 / .7));
}
実測では、カードが (0, 0) から幅320・高さ200のとき、キャプションは (0, 158) から幅320・高さ42 で、下端がぴったりカードの下端(y=200)にそろいました。img に display: block を付けるのは、画像の下にできる数pxのすき間をなくし、キャプションが画像の下端からずれないようにするためです。
2. アイコンの右上にバッジを付ける
<a class="icon" href="/notice">
<svg width="48" height="48" aria-hidden="true"><!-- ベルのアイコン --></svg>
<span class="icon__badge">3</span>
</a>
.icon { position: relative; display: inline-block; width: 48px; height: 48px; }
.icon__badge {
position: absolute;
top: -6px; right: -6px; /* 負の値で角から少しはみ出させる */
min-width: 20px; height: 20px;
border-radius: 10px;
background: #e11d48; color: #fff;
font-size: 12px; line-height: 20px; text-align: center;
}
アイコンが (20, 220) から48px四方のとき、バッジは (54, 214) から20px四方になりました。右端が 74(アイコンの右端68 + 6)、上端が 214(アイコンの上端220 − 6)で、指定どおり6pxずつはみ出しています。親を inline-block にするのは、インライン要素のままだと幅と高さが効かず、基準の箱が文字の行の大きさになるためです。
3. ヘッダーを画面上部に固定する
<header class="header">サイト名</header>
<main>
<h2 id="about">このサイトについて</h2>
<p>本文…</p>
</main>
:root { --header-h: 64px; }
.header {
position: fixed;
inset: 0 0 auto 0; /* 上端に、左右いっぱい */
height: var(--header-h);
background: #fff;
z-index: 100;
}
main { padding-top: var(--header-h); } /* ヘッダーぶん本文を下げる */
html { scroll-padding-top: var(--header-h); } /* ページ内リンクの着地点を下げる */
実測では、ヘッダーは初期状態でも800pxスクロール後でも (0, 0) から幅1000・高さ64 のままでした。また scroll-padding-top を指定した状態で見出しへページ内移動すると、見出しの上端は y=64、つまりヘッダーのすぐ下に来ました。この1行がないと、見出しがヘッダーの裏に隠れます。
ヘッダーが元の場所を残してもかまわない場合は、position: sticky; top: 0 に置き換えると padding-top が不要になります。
4. 上下左右の中央に配置する
.parent { position: relative; height: 400px; }
/* A. 幅と高さが決まっているとき */
.center-a {
position: absolute;
inset: 0;
margin: auto;
width: 200px; height: 100px;
}
/* B. 大きさが中身しだいのとき */
.center-b {
position: absolute;
top: 50%; left: 50%;
translate: -50% -50%; /* transform: translate(-50%, -50%) でも同じ */
}
/* C. 画面の中央(モーダルなど) */
.center-c {
position: fixed;
inset: 0;
margin: auto;
width: 200px; height: 100px;
}
幅600px・高さ400pxの親(中心は 300, 200)で、子の中心座標を測りました。
| 書き方 | 子の中心(x, y) | 子の大きさ | 結果 |
|---|---|---|---|
A. inset: 0 + margin: auto + 幅・高さ指定 |
(300, 200) | 200×100 | 中央 |
B. top/left: 50% + translate: -50% -50% |
(300, 200) | 200×100 | 中央 |
B'. top/left: 50% + transform: translate(-50%, -50%) |
(300, 200) | 200×100 | 中央 |
top/left: 50% だけ |
(400, 250) | 200×100 | 左上の角が中央に来るため、右下へ半分ずれる |
inset: 0 + margin: auto(幅・高さなし) |
(300, 200) | 600×400 | 親いっぱいに広がる |
inset: 0 + margin: auto + width/height: fit-content |
(300, 200) | 24×18(文字の大きさ) | 中身の大きさのまま中央 |
| C. fixed 版(画面 1000×600) | (500, 300) | 200×100 | 画面の中央 |
top: 50%; left: 50% は「要素の左上の角」を中央に運ぶ指定です。そこから自分の大きさの半分だけ戻すのが translate: -50% -50% の役目です。translate の % は自分自身の大きさが基準なので、大きさがわからない要素にも使えます。
重ねる必要がなく、単に中央へ置きたいだけなら、親に display: grid; place-items: center と書くほうが短く済みます。position は「ほかの要素の上に重ねたい」ときに選ぶ、と覚えておくと使い分けやすくなります。
5. スクロールに追従するサイドバー
<div class="layout">
<article class="content">本文(長い)</article>
<aside class="side">目次</aside>
</div>
.layout {
display: grid;
grid-template-columns: 1fr 240px;
gap: 24px;
align-items: start; /* サイドバーを中身の高さにする */
}
.side {
position: sticky;
top: 80px; /* 固定ヘッダー64px + 余白16px */
}
固定ヘッダー(高さ64px)と組み合わせて測ると、サイドバーは800pxスクロールした後も y=80 の位置に止まったままでした。
align-items: start は大事な1行です。grid や flex の子は、初期設定では行の高さいっぱいに伸びます。試しに align-items: stretch(初期値)にしたところ、サイドバーの高さが本文と同じ3000pxになり、800pxスクロール後の y は -736、つまり本文と一緒に流れていきました。サイドバー自身が親と同じ高さだと、貼り付いて動ける余地がなくなるためです。
効かないときの原因と直し方
症状から探す早見表
| 症状 | よくある原因 | 直し方 |
|---|---|---|
| top・left を書いても動かない | position が static のまま | relative・absolute などを指定する |
| absolute の要素が画面の端へ飛ぶ | 祖先に基準(static 以外)がない | 基準にしたい親に position: relative |
| absolute にしたら幅が縮んだ | 幅が中身の大きさになる性質 | width: 100% または left: 0; right: 0 |
| z-index を上げても前に出ない | static のまま/親がスタッキングコンテキストを作っている | 下の「z-index」の項へ |
| sticky が止まらない | 祖先の overflow/top の書き忘れ/親の高さ | 下の「sticky」の項へ |
| fixed なのにスクロールで流れる | 祖先に transform や filter がある | 下の「fixed」の項へ |
top: 50% が効かない(relative) |
親の高さが auto | 親に高さを指定する |
| 中央に置いたつもりが右下にずれる | top/left: 50% だけで、戻す指定がない |
translate: -50% -50% を足す |
| 固定ヘッダーの裏に見出しが隠れる | fixed は元の場所を残さない | padding-top と scroll-padding-top |
z-indexが効かない(スタッキングコンテキスト)
z-index は重なりの順番を決めるプロパティですが、効くのは「position が static 以外の要素」と「flex・grid の子」だけです。さらに、z-index の数値は同じスタッキングコンテキスト(重なり順のグループ)の中でしか比べられません。
赤と青の100px四方の箱を50px重ね、重なった部分で document.elementFromPoint() を呼んで、どちらが手前かを調べました。青はHTML上で赤の後ろに書いています。
| ケース | 手前に来たのは | 理由 |
|---|---|---|
A. 赤が static のまま z-index: 10 |
青 | static には z-index が効かない |
B. 赤に position: relative + z-index: 10 |
赤 | position を付けたので効く |
C. 赤の親が z-index: 1、赤は z-index: 9999、青は z-index: 2 |
青 | 赤の9999は親のグループ内の順位。親(1)と青(2)の比較で決まる |
D. 赤の親が opacity: .99、赤は z-index: 9999、青は z-index: 1 |
青 | opacity が1未満の親もグループを作る |
| E. 赤の親が relative だけ(z-index なし)、赤は9999、青は2 | 赤 | 親はグループを作らないので、赤と青が直接比べられる |
F. 赤の親を z-index: 3 に上げる(青は2) |
赤 | グループごと青より上になる |
G. 赤の親が isolation: isolate、赤は9999、青は1 |
青 | isolate もグループを作る |
H. 赤が flex の子(static)で z-index: 10 |
赤 | flex・grid の子は position なしで効く |
Cを図にすると、次のような入れ子になっています。
ページ全体のグループ
├─ 赤の親 z-index: 1 ← ここで青と比べられる(1 対 2 で青が上)
│ └─ 赤 z-index: 9999 ← 親のグループの中だけの順位
└─ 青 z-index: 2
直し方は「子の数値を大きくする」ではなく「グループ同士の順番を直す」です。
/* 直す前:子だけ大きくしても変わらない */
.parent { position: relative; z-index: 1; }
.child { position: relative; z-index: 9999; }
.other { position: relative; z-index: 2; }
/* 直し方1:親の z-index を上げる(ケースF) */
.parent { position: relative; z-index: 3; }
/* 直し方2:親の z-index を外してグループを作らせない(ケースE) */
.parent { position: relative; }
親がグループを作る代表的な指定は、position + z-index(auto 以外)、opacity(1未満)、transform、filter、isolation: isolate、position: fixed・sticky です。モーダルやドロップダウンのように必ず最前面に出したい要素は、HTML上で body の直下に置くか、<dialog> 要素の showModal() を使うと、祖先の影響を受けなくなります。
stickyが止まらない(親の overflow)
position: sticky; top: 0 の見出しを置き、その親の overflow を切り替えて、500pxスクロールした後の見出しの y を測りました。y=0 なら画面上端に貼り付いています。
| 条件 | 500pxスクロール後の y | 貼り付いたか |
|---|---|---|
親が overflow: visible(初期値) |
0 | 貼り付く |
親が overflow: hidden |
-400 | 流れていく |
親が overflow: auto |
-400 | 流れていく |
親が overflow: clip |
0 | 貼り付く |
top の指定なし |
-400 | 流れていく |
| 親の高さが sticky 要素と同じ | -400 | 流れていく |
sticky は「いちばん近い、スクロールする仕組みを持つ祖先」を基準に貼り付きます。 overflow: hidden や auto を指定した要素は、実際にスクロールバーが出ていなくてもこの「スクロールする枠」として扱われます。その枠自体はスクロールしていないので、中の sticky も動かない、という流れです。
/* 直す前:はみ出しを隠すための hidden が sticky を止めている */
.wrapper { overflow: hidden; }
.wrapper .head { position: sticky; top: 0; }
/* 直した後:clip は切り取るだけで、スクロールする枠にならない */
.wrapper { overflow: clip; }
overflow: clip は、はみ出した部分を切り取る点は hidden と同じで、スクロールする枠を作らない点が異なります。横方向だけ切りたいときは overflow-x: clip と書けます。
残りの2つも直し方は単純です。top(または bottom・left・right)を1つ書くこと、そしてsticky 要素を、動ける余地のある背の高い親の直下に置くことです。sticky 要素だけを div で包むと、親の高さが自分と同じになり、貼り付く範囲が0になります。
fixedがずれる・スクロールで流れる(祖先の transform)
fixed は画面基準のはずですが、祖先に transform などがあると、その祖先が基準に変わります。 親(画面の上から200px・左から100pxの位置)にいろいろなプロパティを付け、中の position: fixed; top: 0; left: 0 の要素の座標を測りました。
| 親に付けた指定 | 初期位置(x, y) | 150pxスクロール後 | 画面に固定されたか |
|---|---|---|---|
| なし | (0, 0) | (0, 0) | 固定される |
transform: translateX(0) |
(100, 200) | (100, 50) | 親と一緒に流れる |
filter: blur(0) |
(100, 200) | (100, 50) | 親と一緒に流れる |
will-change: transform |
(100, 200) | (100, 50) | 親と一緒に流れる |
contain: paint |
(100, 200) | (100, 50) | 親と一緒に流れる |
backdrop-filter: blur(1px) |
(100, 200) | (100, 50) | 親と一緒に流れる |
perspective: 100px |
(100, 200) | (100, 50) | 親と一緒に流れる |
opacity: .9 |
(0, 0) | (0, 0) | 固定される |
translateX(0) や blur(0) のように見た目が何も変わらない値でも、プロパティが指定されているだけで基準が切り替わります。アニメーション用に付けた will-change: transform や、ふわっと表示させるための transform が原因になっていることが多いです。なお opacity は重なり順のグループは作りますが、fixed の基準は変えません。
<!-- 直す前:アニメーションする要素の中にモーダルがある -->
<div class="page fade-in"> <!-- transform を使うアニメーション -->
<div class="modal">…</div> <!-- position: fixed -->
</div>
<!-- 直した後:モーダルを transform のある要素の外に出す -->
<div class="page fade-in">…</div>
<div class="modal">…</div>
HTMLを動かせないときは、次のどちらかで対応できます。
- アニメーションが終わったら transform を外す(
animation-fill-modeをforwardsにせず、最終状態を transform なしにする)。 <dialog>とshowModal()、またはpopover属性を使う。これらは最上位レイヤーという特別な層に表示され、祖先の transform の影響を受けません。
原因の祖先を探すには、DevTools の Console で次のスニペットを実行します。$0 は Elements パネルで選択中の要素です。
for (let el = $0.parentElement; el; el = el.parentElement) {
const s = getComputedStyle(el);
const hit = ['transform', 'filter', 'backdropFilter', 'perspective', 'contain', 'willChange', 'overflow']
.filter(p => !['none', 'auto', 'visible', 'normal'].includes(s[p]));
if (hit.length) console.log(el, hit.map(p => `${p}: ${s[p]}`).join(' / '));
}
fixed がずれるときは transform・filter・contain・willChange の行を、sticky が止まらないときは overflow の行を見ます。このスニペットの Console での実行は今回は未実行で、同じ判定を Playwright のスクリプトで行った結果が上の表です。
どの値を選ぶかの判断表
| やりたいこと | 選ぶ値 | 一緒に書くもの |
|---|---|---|
| 要素をほんの少しずらす | relative | top などに数px |
| 子を重ねるための基準を作る | relative | 何も書かない |
| 画像の上に文字や膜を重ねる | absolute | 親に relative、inset |
| 角にバッジや閉じるボタンを付ける | absolute | 親に relative、top・right |
| ツールチップ・ドロップダウンを出す | absolute | 親に relative、top: 100% など |
| ヘッダーや追従ボタンを画面に固定する | fixed | inset、z-index、本文側の余白 |
| モーダルを画面中央に出す | fixed(または <dialog>) |
inset: 0; margin: auto |
| サイドバーや表の見出しを途中から固定する | sticky | top、親の align-items: start |
| 単に中央へ寄せる・横に並べる | position は使わない | flex・grid |
迷ったら「重ねたいかどうか」で分けます。 重ねないなら flex や grid、重ねるなら position、という順で考えると、position の出番は意外と絞られます。absolute を多用して全体を組むと、文字数や画面幅が変わったときに重なりや切れが起きやすくなるため、並べるのは flex・grid、重ねるのは position と役割を分けておくと保守がしやすくなります。
書いたあとのチェックリスト
| 確認すること | 見るところ |
|---|---|
| absolute の基準は意図した親か | 親に position: relative があるか |
| 文字数が増えても重なりが崩れないか | 長い文字列・2行になる場合を試す |
| 画面幅を狭めてもはみ出さないか | 幅320px程度で確認 |
| fixed の要素が本文を隠していないか | padding-top・scroll-padding-top |
| z-index の数値が増え続けていないか | カスタムプロパティで段階を決める |
| キーボード操作で、隠れた要素にフォーカスが当たらないか | Tab キーで一巡する |
よくある質問
Q. relative と absolute はどちらを親に、どちらを子に付けますか? A. 親に relative、子に absolute です。子の absolute は「position が static 以外の、いちばん近い祖先」を基準にするので、親に relative を付けて基準にします。親は relative でなくても、absolute・fixed・sticky であれば基準になります。
Q. absolute と fixed の違いは何ですか? A. 基準が違います。absolute は祖先の要素が基準で、ページをスクロールすると一緒に流れます。fixed は画面が基準で、スクロールしても同じ場所にとどまります。実測でも、300pxスクロールしたとき absolute は y が 121 から -179 に動き、fixed は 20 のままでした。
Q. inset はどのブラウザでも使えますか?
A. Chromium 141 では inset: 0 と top: 0; right: 0; bottom: 0; left: 0 が同じ結果になることを確認しました。ほかのブラウザでの動作は今回は未実行です。一般には主要ブラウザの現行版で広く使われている書き方です。
Q. position を使わずに重ねる方法はありますか?
A. grid を使う方法があります。親に display: grid を指定し、重ねたい子すべてに grid-area: 1 / 1 と書くと、同じマスに重なります。この方法は親の高さが子の大きさに合わせて決まるため、中身の量が変わる場面に向いています。
まとめ
- position の違いは「基準」「元の場所が残るか」「スクロール時の動き」の3点で整理できる。
- relative は自分の元の位置、absolute は static 以外のいちばん近い祖先、fixed は画面が基準。sticky はしきい値に達すると貼り付く。
- 重ねるときの基本形は「親に relative、子に absolute」。位置は
insetでまとめて書ける。 - 中央配置は
inset: 0; margin: auto(大きさ指定あり)か、top/left: 50%+translate: -50% -50%。 - z-index は同じグループの中でしか比べられない。前に出ないときは、子ではなく親の順番を見直す。
- sticky は祖先の
overflow: hidden・autoで止まらなくなる。overflow: clipに替えると貼り付く。 - fixed は祖先の transform・filter・will-change で基準が変わる。固定したい要素はその外へ出す。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS positionの使い方|relative・absolute・fixed」
株式会社RJCでは、CSSのpositionを使った配置の考え方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。