CSSのoverflowで、角丸の親から画像がはみ出すのを防ぐ方法

​​CSS​HTML,Playwright,アクセシビリティ

親の要素に border-radius を付けて角を丸くしても、中に入れた画像の角は四角いまま、丸みの外にはみ出して表示されます。親に overflow: hidden か overflow: clip を付けると、画像は親の丸みに沿って切り取られます。記事一覧やブログのカード、商品のサムネイル付きのカードなど、上半分に写真を置いた角丸の枠を作るときの直し方です。ふつうのカードなら、どちらの値でも見た目は同じです。違いが出るのは、カードの高さを決めているとき、片方の向きだけに書いたとき、角の外にバッジを出したいときです。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、ヘッドレスの Chromium 153 です。検証用のページを Playwright で開き、画面を撮った画像の色と、getComputedStyle() などの値を読みました。カードの画像には、検証のために描いた SVG を使っています。Firefox と Safari、スマートフォンの実機は試していません。

親に border-radius を付けただけでは、子の画像は切れない

幅 240px のカードに、角の半径 16px の border-radius を付けました。中には幅いっぱいの画像と、本文の段落を入れています。

html
<div class="card">
  <img src="photo.jpg" alt="">
  <p>カードの本文</p>
</div>
css
.card {
  width: 240px;
  border-radius: 16px;
  background: #ffe9a8;
  overflow: hidden; /* または clip */
}

.card img {
  display: block;
  width: 100%;
  height: auto;
}

下の画像は、overflow を付けないカード、hidden を付けたカード、clip を付けたカードを上から並べたものです。右側には、それぞれの左上の角を拡大して並べました。

3枚のカードを縦に並べた比較。上の「overflow を付けない」カードは、本文の背景の下側だけが丸く、上側は青い画像の角が四角いまま。拡大した左上の角も、全面が青。中の overflow: hidden と下の overflow: clip のカードは、画像の上の角も丸く切れていて、拡大した角では白い背景と青い画像の境目が丸い線になっている
左がカード、右がピンクの枠で囲んだ左上の角(20×20px)の拡大。上から、overflow なし、hidden、clip

overflow を付けないカードでは、本文の背景の色は下の角で丸くなりますが、画像の角は四角いままでした。左上の角から斜めに 2px 入った点を調べると、画像の青でした。hidden と clip のカードでは、同じ点がページの背景の白になり、7px 入った点から画像の青になりました。角を切り取る形は、2つの値で同じでした。

CSS Backgrounds and Borders の仕様では、overflow が visible 以外の値のとき、はみ出しを切り取る線は、丸みの付いたパディングの外側の線(padding edge)になると決められています。border-radius は、背景や枠線の形を丸くするだけです。中の要素を丸く切り取らせるには、overflow が必要です。

カード自身に付けた box-shadow は、overflow では消えません。box-shadow: 0 0 0 6px でカードの外に線の影を描いたところ、hidden でも clip でも、カードの外側 3px の点に影の色が出ていました。overflow が切り取るのは、カードの中身だけです。

枠線を付けたカードは、枠線の内側の丸みで切れる

カードに 6px の border を付けると、画像は枠線の外側の丸みではなく、枠線の内側の線で切り取られます。

検証環境では、hidden でも clip でも、左上の角から斜めに 2px の点はページの背景の白、7px の点は枠線の色、10px の点で画像の色になりました。画像が枠線の上に重なることはありませんでした。

2つの値で結果が変わる場面

高さを決めたカードでは、hidden の中身がフォーカスで動く

MDN の overflow のページによると、hidden の要素は、中身がはみ出したときにスクロールバーのないスクロールコンテナーになり、タブ移動などでスクロールします。clip の要素はスクロールコンテナーになりません。

カードの中身が多くて枠に収まらないときに、この違いが出ます。グリッドで高さをそろえたカードのように、高さを 200px に決め、本文の最後の「続きを読む」リンクが枠の下にはみ出すカードで試しました。ページを開いて Tab キーを1回押し、リンクにフォーカスを移した結果です。

overflow: hidden overflow: clip
カードの scrollTop 36 0
画像の上端 カードの上端より 36px 上へずれた 動かなかった
フォーカスしたリンク カードの中に見えた カードの外(見えない位置)のまま

hidden のカードは、リンクが見える位置まで中身がスクロールし、画像の上の 36px が隠れました。clip のカードは中身が動きませんが、フォーカスしたリンクは見えないままです。どちらの値でも、フォーカスできる要素を枠の外にはみ出させないのが確実です。スクロールとフォーカスの違いは、「CSSのoverflowで、clipとhiddenをどう使い分けるか」の記事でも詳しく確かめています。

片方の向きだけに書くと、clip では角が丸くならない

overflow-x か overflow-y の片方だけに書いたときは、結果が変わります。検証環境で、計算値と、左上の角から斜めに 2px の点の色を調べました。

指定 計算値(x / y) 角の点
overflow-x: hidden hidden / auto 背景の白(丸く切れた)
overflow-x: clip clip / visible 画像の青(切れなかった)
overflow-y: clip visible / clip 画像の青(切れなかった)
overflow: clip visible clip / visible 画像の青(切れなかった)

overflow-x: hidden だけを書くと、もう片方の overflow-y は auto になりました。両方の向きが visible 以外になるので、角は丸く切れます。ただし、カードはスクロールコンテナーになります。clip を片方だけに書いた場合は、もう片方が visible のままで、角は丸くなりませんでした。

CSS Overflow Module Level 3 の仕様にも、片方の向きが clip で、もう片方が visible のときは、切り取る範囲を丸くしないと書かれています。角の丸みで切りたいときは、overflow: clip のように両方の向きに指定します。

角の外にバッジを出したいなら、overflow-clip-margin より包む要素を分ける

カードの右上に、件数などを示す丸いバッジを、角から 10px はみ出させて置いてみました。overflow: hidden や clip を付けたカードの中に置くと、バッジのはみ出した部分も切り取られます。バッジの色の画素を数えると、何も切り取らないときの 406 画素のうち、見えたのは 83 画素でした。

clip には、切り取る線を外へ広げる overflow-clip-margin があります。overflow: clip に overflow-clip-margin: 12px を足すと、バッジは 406 画素のうち 353 画素まで見えるようになりました。ところが、今度は画像の角が丸く切れなくなりました。左上と左下の角から 2px の点が、どちらも画像の青でした。

3枚のカードを縦に並べた比較。上の overflow: hidden のカードは、画像の角は丸いが、右上のピンクのバッジがほとんど切れて、端だけが見える。中の overflow: clip と overflow-clip-margin: 12px のカードは、バッジの数字の 3 が見えるが右上が少し欠け、画像の4つの角が四角い。下の、画像だけを包む要素に overflow: clip を付けたカードは、バッジが丸く全部見え、画像の角も丸い
角から 10px はみ出すバッジを、3つの書き方で比べた画面。上から、overflow: hidden、overflow-clip-margin を足した clip、画像だけを包む書き方

切り取る線が 12px 外へ広がると、角の丸みも一緒に外へ移ります。仕様では、広げた線の角の形は、同じ距離だけ広げた box-shadow と同じになると決められています。画像はカードの内側にあるので、外へ移った丸みには掛かりません。バッジの右上が少し欠けたのも、広げた線の角が丸いためです。なお、overflow-clip-margin は hidden には効かず、hidden に同じ指定を足しても、バッジは 83 画素のままでした。

角の外にバッジを出したいときは、切り取る要素とバッジを分けるほうが確実です。画像だけを包む要素に border-radius と overflow: clip を付け、バッジはその外側の要素に置きます。

html
<div class="card">
  <div class="card-media">
    <img src="photo.jpg" alt="">
  </div>
  <span class="badge">3</span>
</div>
css
.card {
  position: relative; /* バッジの位置の基準 */
  width: 240px;
}

.card-media {
  border-radius: 16px;
  overflow: clip;
}

.badge {
  position: absolute;
  top: -10px;
  right: -10px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
}

この書き方では、バッジは 406 画素すべてが見え、画像の左上の角から 2px の点も背景の白でした。

overflow を使わずに、画像に角丸を付ける方法もある

画像がカードのいちばん上にあるだけなら、親の overflow を使わず、画像に上の2つの角だけの border-radius を付けても同じ見た目になります。

css
.card img {
  border-radius: 16px 16px 0 0; /* 左上・右上だけを丸くする */
}

検証環境では、カードの overflow は visible のままで、画像の左上の角から斜めに 2px の点と、右上の角の近く(右端から 3px、上端から 2px)の点は背景の白でした。画像の下の角は四角いままでした。カードの角の半径を変えたときは、画像の値も一緒に変える必要があります。中身に画像以外の要素がはみ出すことがあるなら、親に overflow を付けるほうが、まとめて切り取れます。

対応しているブラウザー

@mdn/browser-compat-data 8.1.9 の互換性データでは、overflow: clip は Chrome と Edge が 90、Firefox が 81、Safari(macOS・iOS)が 16 から対応しています。overflow-clip-margin は、Chrome と Edge では両方の向きが clip のときだけ効く一部対応で、Safari は対応していないとされています。

​Posted by 86