CSS講座 / positionの基礎:relative・absolute・fixed・sticky

positionは通常の配置を基準にずらす方法や、別の基準へ固定する方法を選ぶプロパティです。relativeとstickyは元の配置上の領域を保ち、absoluteとfixedは通常の配置から外れます。

何を基準に動くかを区別する

staticは通常の配置で、topなどのずらす指定は効きません。relativeは元の位置から見た目をずらします。absoluteは包含ブロックを基準にし、fixedは通常ビューポートに対して固定されます。stickyは通常の配置から始まり、指定した位置で留まります。

同じtop: 16pxでも基準が違います。relativeで下へ動かす16pxと、absoluteで基準の上端から離す16pxを同じ意味と考えないでください。MDNのpositionで各方式の条件を確認できます。

relativeは元の場所を残す

relativeで要素を下へ動かしても、後ろの要素の配置は元の箱を基準にしたままです。上に空きが残ったり、下の内容へ重なったりします。要素同士を本当に離したいならmargin・padding・gapなどを検討します。

位置をずらさずposition: relativeだけを指定する使い方もあります。これはabsoluteの子に対して、近くの位置基準を用意する場合です。

absoluteの基準と親の高さ

absoluteの包含ブロックは、一般的には最も近いstatic以外の祖先のpadding領域です。ただしtransformなど、別の指定で包含ブロックが作られる場合もあります。必ず直近の親が基準になるとは限りません。

absoluteの子だけでは親の通常の高さを押し広げません。本文まで絶対配置にすると、文章量や画面幅の変化で重なりやすくなります。バッジのように重ねたい部分だけに限定し、通常の文章は流れに残す方法が扱いやすくなります。

通常の配置とバッジを比較する

次をposition-flow.htmlとしてUTF-8で保存します。前・対象・後という同じ構造でstatic、relative、absoluteを比べます。カードのバッジは上端から12pxの位置に置き、本文と重ならない余白を用意しています。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>position-flow.html</title>
  <style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.flow { border: 2px dashed #789; margin-bottom: 40px; margin-right: 24px; }
 .flow > div { height: 56px; padding: 12px; background: #e8f5de; }
 .flow .target { background: #dceefa; }
 .relative .target { position: relative; top: 16px; left: 20px; }
 .absolute { position: relative; }
 .absolute .target { position: absolute; top: 16px; left: 20px; }
 .card { position: relative; padding: 64px 20px 20px; background: #dceefa; max-width: 600px; }
 .badge { position: absolute; top: 12px; right: 12px; padding: 4px 12px; background: #ffe4b5; }
  </style>
</head>
<body>
<h1>Does the original space remain?</h1>
<h2>static</h2><div class="flow static"><div>Front element</div><div class="target">Target</div><div class="after">Back element</div></div>
<h2>relative</h2><div class="flow relative"><div>Front element</div><div class="target">Target</div><div class="after">Back element</div></div>
<h2>absolute</h2><div class="flow absolute"><div>Front element</div><div class="target">Target</div><div class="after">Back element</div></div>
<h2>Badge inside the card</h2><div class="card"><span class="badge">News</span><p>The badge sits at the top right of the card. Top padding separates it from the text.</p></div>
</body>
</html>

relativeのtopを16pxから0へ変え、元の領域が同じまま見た目が戻ることを確認して復元します。absoluteをstaticへ変える比較では、親の高さと後続要素の位置が変わるかを見て戻します。

.cardのrelativeだけを外すと、バッジの基準が別の祖先へ移る場合があります。確認後は戻してください。バッジの文が長くなれば固定のpaddingでは足りなくなるため、翻訳や文字拡大でも本文に重ならない設計を確認します。

fixedとstickyの条件を理解する

fixedは通常スクロールしてもビューポートの同じ場所に残ります。ただし祖先のtransformやfilterなどで基準が変わる場合があります。本文を覆うなら固定要素の分の空きを確保します。

stickyにはtopなど、留まる位置を決めるauto以外の指定が必要です。最初から画面の上に固定されるとは限らず、通常の位置が境界へ達した後に留まり、親の終端による制約も受けます。overflow: auto・scroll・hiddenなどを持つ祖先が、想定外のスクロール基準になることもあります。親に移動できる高さがなければ、差は見えにくくなります。

スクロールする完全例を動かす

次をposition-scroll.htmlとして保存します。導入部分に十分な高さを作り、その下の章にsticky見出し、画面上部にfixedリンクを置いています。章の後にも内容があります。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>position-scroll.html</title>
  <style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
body { margin: 0; padding-bottom: 80px; }
 .intro { min-height: 320px; padding: 24px; background: #eef2f6; }
 .chapter { min-height: 760px; background: #e8f5de; }
 .sticky { position: sticky; top: 0; margin: 0; padding: 16px; background: #dceefa; border-bottom: 2px solid #789; }
 .chapter p { padding: 16px; }
 .tail { min-height: 900px; padding: 24px; background: #fff2df; }
 .fixed { position: fixed; right: 16px; bottom: 16px; padding: 12px; background: #fff; border: 2px solid #145f8f; }
  </style>
</head>
<body>
<div class="intro" id="top"><h1>A heading that sticks while scrolling</h1><p>The heading below starts in normal flow. It sticks when it reaches the top of the screen and remains within its parent at the end of the chapter.</p></div>
<section class="chapter"><h2 class="sticky">Chapter heading</h2><p>This content is inside the chapter. The extra height lets you observe how long its heading remains sticky.</p></section>
<section class="tail"><h2>Another section</h2><p>Check whether the previous chapter's heading stays above this section.</p></section>
<a class="fixed" href="#top">Back to top</a>
</body>
</html>
  1. 最初は章見出しが導入の下にあり、固定リンクは初めから上にあることを確認します。
  2. 章を読み進め、sticky見出しが上端で留まることを見ます。章の終端を越えると見出しは一緒に去りますが、fixedリンクは残ります。
  3. stickyのtopだけをautoへ変え、留まらなくなることを確認して0へ戻します。
  4. 章のoverflowをhiddenにする比較では、スクロール基準の変化を確認します。追加した指定は削除します。

位置が合わない原因を順に調べる

元の空きが残るか、親の高さがなくなるかで、通常の配置との関係を確認します。次に最終的なpositionとtop・right・bottom・left、メディアクエリーによる上書きを見ます。absoluteでは包含ブロック、fixedでは祖先のtransformなども調べます。

stickyは位置指定、スクロールする祖先、親と自分の高さの順に確認します。位置は正しいのに見えない場合は、overflowによる切り取りや重なり順とスタッキングコンテキストが別の原因です。大きなz-indexを付けるだけですべて解決するわけではありません。