CSSで、区切りを斜めに切ったセクションを作る方法

​​CSS​HTML,Playwright

セクションの区切りを斜めにするには、そのセクションに clip-path: polygon() を付けて、右下の角だけを上に寄せます。画像も擬似要素も要りません。ただし、切っただけでは切った部分にページの背景が見えるので、次のセクションを下に重ねる必要があります。斜めの辺で文字が切れないようにする余白と、画面の幅が変わっても角度が変わらない書き方も確かめました。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS のページを Playwright で開き、getComputedStyle() の値と、画面を撮って色の変わる位置を記録しました。Firefox と Safari、スマートフォンの実機、スクロールバーが幅を取る画面は試していません。

clip-path の polygon() で、下端を斜めに切る

HTML は、セクションを2つ並べただけです。上のセクションに slant クラスを付けます。

html
<section class="slant">…</section>
<section>…</section>

指定するのは clip-path の1行だけです。

css
/* セクションの下端を、右上がりの斜めに切る */
.slant {
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 60px), 0 100%);
}

clip-path は、要素のうち、指定した形の内側だけを表示します。polygon() には、多角形の頂点を「横 縦」の組で並べます。この例の4つの頂点は、左上、右上、右下から60px上、左下です。右下の角だけが60px上がるので、下の辺が左下から右上へ上がる斜めの線になります。

幅800pxの画面で試したところ、左端では上のセクションの水色が下端まで続き、右端では下端から60px上で終わりました。ただ、右下の三角形の部分(右端から10px、下端から10px上)は白でした。切り取られた部分から、後ろにあるページの背景(白)が見えています。

上下2段の比較。上の「clip-path だけを付けた場合」は、水色のセクションの右下が斜めに切られ、切られた三角形の部分が白く抜けている。下の「次のセクションを 60px 上に重ねた場合」は、同じ部分に下のセクションの薄い水色が見え、2つのセクションが斜めの線で区切られている
切っただけでは白く抜ける(上)。次のセクションを重ねると、その色が見える(下)

次のセクションを、切った高さの分だけ上に重ねる

切った部分に次のセクションの色を出すには、次のセクションに負の margin-top を付けて、切った高さ(60px)の分だけ上に重ねます。重ねた分、次のセクションの文字も上に動くので、padding-top を同じだけ増やします。

css
/* 切った部分に、次のセクションの背景が見えるようにする */
.slant {
  position: relative;
  z-index: 1;
}
.slant + section {
  margin-top: -60px;
  padding-top: calc(60px + 32px);
}

さきほどの三角形の部分は、次のセクションの色(rgb(238, 250, 253))になりました。次のセクションの見出しの上端は、上のセクションの下端より32px下で、上のセクションには隠れていません。

position: relative と z-index: 1 は、次のセクションに position: relative が付いているときのために入れています。外して試すと、次のセクションに position がなければ、上のセクションは隠れずに表示されました。ところが、次のセクションに position: relative を付けると、上のセクションの斜めの部分が、次のセクションの背景に隠れました(調べた3か所とも、次のセクションの色でした)。上のセクションに position: relative と z-index: 1 を付けると、次のセクションに position: relative があっても、上のセクションが手前に描かれました。

斜めの辺にかかる文字は切られるので、下の余白を足す

clip-path は、中の文字も一緒に切ります。セクションの最後の段落を右寄せにして試すと、右下の文字が斜めの辺で途中から消えました。段落の範囲で clip-path なしのときと比べると、文字の画素2,386個のうち1,038個の色が変わっていました。

上下2段の比較。上の「padding-bottom が 32px のまま」では、右寄せの文「右下に置いた文字は、斜めの辺にかかります。」の後ろ半分が、斜めの辺で切られている。下の「padding-bottom を 60px + 32px にした場合」では、文が最後まで表示され、その下に斜めの辺がある。どちらも次のセクションを重ねていないため、右下は白く抜けている
下の余白を、切った高さの分だけ増やすと、文字が切れない(下)。この図では次のセクションを重ねていない

padding-bottom を、切った高さの分だけ増やすと直ります。

css
/* 斜めに切る高さの分だけ、下の余白を足す */
.slant {
  --slant: 60px;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--slant)), 0 100%);
  padding-bottom: calc(var(--slant) + 32px);
}

色が変わった文字の画素は0個になりました。切る高さを カスタムプロパティ の --slant にまとめておくと、clip-path と余白で同じ値を使えます。

切る高さを % で書くと、中身の量で角度が変わる

polygon() の縦の位置は、% でも書けます。たとえば 100% 80% にすると、右下の角はセクションの高さの80%の位置になります。

css
/* 切る高さを % で書く(セクションの高さに比例する) */
.slant-percent {
  clip-path: polygon(0 0, 100% 0, 100% 80%, 0 100%);
}

この書き方では、切る高さがセクションの高さで決まります。幅800pxの画面で、高さ137pxのセクションでは、斜めの部分の高さは27pxでした。段落を6つ足して高さが341pxになると、斜めの部分は68pxになりました。セクションごとに文章の量が違うと、区切りの角度もばらばらになります。

画面の幅が変わっても、角度を同じにする

--slant: 60px のように高さを固定すると、セクションの中身の量には左右されません。ただ、画面の幅が変わると角度が変わります。画面に写った斜めの辺から角度を測ると、幅375pxでは約9.0度、幅800pxでは約4.2度、幅1280pxでは約2.6度でした。幅の狭い画面では急に、広い画面ではなだらかになります。

角度を保つには、切る高さを「画面の幅 × tan(角度)」にします。CSS の tan() は、角度を受け取って正接(タンジェント)を返す関数で、calc() の中で長さに掛けられます。ここまでの書き方をまとめると、次のようになります。

css
/* 斜めの角度を 5deg に保ち、文字が切れず、次のセクションが下に入るようにする */
:root {
  --slant: calc(100vw * tan(5deg));
}
.slant {
  position: relative;
  z-index: 1;
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--slant)), 0 100%);
  padding-bottom: calc(var(--slant) + 32px);
}
.slant + section {
  margin-top: calc(-1 * var(--slant));
  padding-top: calc(var(--slant) + 32px);
}

--slant を :root に書いているのは、次のセクション(.slant + section)でも同じ値を使うためです。カスタムプロパティは親から子へ受け継がれるので、.slant に書くと、きょうだいの次のセクションからは読めません。

幅375pxの画面では、clip-path の計算値は polygon(0px 0px, 100% 0px, 100% calc(100% - 32.8083px), 0px 100%) で、切る高さは32.8pxになりました。幅800pxでは70.0px、幅1280pxでは112.0pxです。画面に写った斜めの辺から測った角度は、4.89度から4.96度で、どの幅でもほぼ5度でした。どの幅でも、段落の下端は斜めの辺より上にあり、次のセクションの見出しは上のセクションの下端より下にありました。

4段の比較。「60px 固定」の幅375pxと720pxでは、幅の狭い375pxのほうが斜めの線が急になっている。「tan(5deg) で計算」の幅375pxと720pxでは、どちらの斜めの線も同じ傾きで、幅の広い720pxのほうが、斜めの部分の高さが大きい
60px 固定では幅で角度が変わる。tan(5deg) で計算すると、同じ角度になる(画像の幅に収めるため、広いほうは720pxで撮影)

100vw は、セクションの幅ではなく、画面(ビューポート)の幅です。そのため、この書き方で角度が5度になるのは、セクションが画面の幅いっぱいに広がっている場合です。幅800pxの画面で、2つのセクションを幅600pxの箱に入れて試すと、切る高さは画面の幅で計算した70.0pxのままで、斜めの辺の角度は約6.6度になりました。

対応しているブラウザ

MDN の互換性データ(@mdn/browser-compat-data 8.1.8)で、この記事で使っている機能を調べました。

機能 Chrome Firefox Safari
clip-path(接頭辞なし) 55 3.5 9.1
polygon() 37 54 10.1
tan() 111 108 15.4

表の値は機能ごとのものです。Firefox の 3.5 は clip-path プロパティそのものの値で、polygon() で切り抜けるのは 54 からです。この記事の最後の CSS は tan() を使うので、そのまま動くのは Chrome 111、Firefox 108、Safari 15.4 からです。

​Posted by 86