できない.dev

CSSでバッジを要素の右上に重ねるには

position: absolute のバッジを、親に position: relative を与えてカードの右上へ重ねる基本形を示す。
位置の基準になる祖先を明示しないと、バッジはページ全体を基準に飛んでいく。

公開: 更新:

プレビュー
<div class="card">
  <span class="badge">NEW</span>
  <p class="card__title">お知らせカード</p>
  <p class="card__body">右上のバッジはカードを基準に置いている。</p>
</div>
.card {
  position: relative; /* バッジの位置基準になる */
  width: 260px;
  padding: 16px;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
  background: #fff;
}
.badge {
  position: absolute;
  top: -10px;
  right: -10px;
  padding: 4px 10px;
  border-radius: 999px;
  background: #0f766e;
  color: #fff;
  font-size: 12px;
  font-weight: 700;
}
.card__title {
  margin: 0 0 6px;
  font-weight: 700;
}
.card__body {
  margin: 0;
  font-size: 14px;
  color: #475569;
}

position: absolute の要素は「最も近い位置指定済みの祖先」を基準に配置される。

重ねたい相手(ここでは .card)に position: relative を付けるのは、その基準を自分に引き寄せるためである。

つまずき

バッジが意図しない場所に飛ぶ原因のほとんどは、親に position が指定されていないことである。

position: absolute は、static 以外の position を持つ最も近い祖先を基準にする。
基準が見つからないと初期包含ブロック(実質ページ全体)まで遡り、top / right がビューポート基準として解釈される。

逆に、意図しない中間要素に position: relative が付いていると、そちらに基準を横取りされる。

まず開発者ツールで、基準にしたい要素に position: relative が当たっているかを確認するとよい。

できない例: 親に position が無い
.card {
  max-width: 200px;
  margin-top: 32px;
  padding: 16px;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
}
.badge {
  position: absolute;
  top: 4px;
  right: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #0f766e;
  color: #fff;
  font-size: 12px;
}
できる例: 親に position: relative
.card {
  position: relative;
  max-width: 200px;
  margin-top: 32px;
  padding: 16px;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
}
.badge {
  position: absolute;
  top: 4px;
  right: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #0f766e;
  color: #fff;
  font-size: 12px;
}

はみ出した分が切れるとき

上の例は top: -10px / right: -10px でカードの枠から少しはみ出させている。
このとき祖先のどこかに overflow: hidden があると、はみ出した部分が切り取られて見えなくなる。
角丸のために overflow: hidden を付けている親がいないかを疑う。

切り取らせたくない場合は、次のどちらかにするのが素直である。

  • overflow を持つ要素の外側にバッジを出す。
  • はみ出さない値(top: 8px / right: 8px など)に収める。
できない例: 親に overflow: hidden(はみ出しが切れる)
.card {
  position: relative;
  overflow: hidden;
  width: 160px;
  margin: 8px 0 0;
  padding: 16px;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
}
.badge {
  position: absolute;
  top: -8px;
  right: -8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #0f766e;
  color: #fff;
  font-size: 12px;
}
できる例: はみ出さない値に収める
.card {
  position: relative;
  overflow: hidden;
  width: 160px;
  margin: 8px 0 0;
  padding: 16px;
  border: 1px solid #e2e8f0;
  border-radius: 10px;
}
.badge {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #0f766e;
  color: #fff;
  font-size: 12px;
}

重なり順が思ったとおりにならないとき

バッジが隣の要素の下に潜る場合は、z-index ではなく、まず重ね合わせコンテキストを疑う。
次のプロパティは新しい重ね合わせコンテキストを作り、その内側の z-index は外へ効かなくなる。

  • position 指定と z-index の組み合わせ
  • transform
  • filter
  • 1 未満の opacity

バッジだけ z-index を上げても直らないときは、祖先にこれらのプロパティが付いていないかを見る。

できない例: カードに transform(z-index: 10 でも潜る)
.row { display: flex; gap: 8px; }
.card {
  position: relative;
  width: 110px;
  padding: 20px 12px;
  border-radius: 10px;
  background: #e0e7ff;
}
.a { transform: translateZ(0); }
.badge {
  position: absolute;
  z-index: 10;
  top: 22px;
  right: -30px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #0f766e;
  color: #fff;
  font-size: 12px;
}
できる例: transform を外す
.row { display: flex; gap: 8px; }
.card {
  position: relative;
  width: 110px;
  padding: 20px 12px;
  border-radius: 10px;
  background: #e0e7ff;
}
.badge {
  position: absolute;
  z-index: 10;
  top: 22px;
  right: -30px;
  padding: 2px 8px;
  border-radius: 999px;
  background: #0f766e;
  color: #fff;
  font-size: 12px;
}