CSS clip-path のブラウザ差 — Safari で崩れるケースと実践的な対策
CSS clip-path は、要素を任意の形に切り抜ける便利なプロパティです。Chrome/Edge/Firefox では素直に動きますが、Safari(特にiOS Safari)では複数の"崩れポイント"があります。本記事では、実務で引っかかる代表的なケースと、影(box-shadow)やアニメーションと組み合わせる際の実用的なワークアラウンドを解説します。
clip-path の基本おさらい
clip-path は、要素を多角形・円・楕円・SVGパス等の形状で切り抜きます。
.hexagon {
clip-path: polygon(
25% 0%, 75% 0%, 100% 50%,
75% 100%, 25% 100%, 0% 50%
);
}
モダンブラウザはすべて対応していますが、Safari は長らく -webkit-clip-path プレフィックスを必要としていた歴史があり、古いiOSをサポートする場合は併記が推奨されます。
.hexagon {
-webkit-clip-path: polygon(/* ... */);
clip-path: polygon(/* ... */);
}
崩れケース1: transform との合成でレンダリングが乱れる
iOS Safari では、clip-path を当てた要素に transform: rotate() や scale() を組み合わせると、描画が一瞬消えたりギザギザしたりする現象が報告されています。
/* 不安定なパターン */
.card {
clip-path: polygon(0 0, 100% 0, 100% 90%, 0 100%);
transform: rotate(-2deg);
transition: transform 0.3s;
}
.card:hover { transform: rotate(0deg) scale(1.05); }
対策: GPU合成レイヤーに載せることで改善するケースが多いです。
.card {
clip-path: polygon(0 0, 100% 0, 100% 90%, 0 100%);
transform: rotate(-2deg);
will-change: transform; /* or */
backface-visibility: hidden; /* GPU 層を強制 */
-webkit-transform: translateZ(0); /* Safari の暗黙の合成ヒント */
}
will-change は万能ではなく、過剰に指定するとメモリを食います。1ページに数か所までにとどめるのが原則です。
崩れケース2: box-shadow が clip-path で切られてしまう
これはSafari固有ではなく仕様通りの挙動ですが、実務でハマりやすいポイントです。clip-pathで切り抜いた要素のbox-shadowは常にクリップされてしまい、影が表示されません。
/* 影が消える */
.badge {
clip-path: polygon(/* 六角形 */);
box-shadow: 0 4px 12px rgba(0,0,0,.2); /* 見えない */
}
対策: filter: drop-shadow() を使う
filter: drop-shadow() はクリップされた後の形状に対して影を落とすため、box-shadowの代替として機能します。
.badge {
clip-path: polygon(/* 六角形 */);
filter: drop-shadow(0 4px 12px rgba(0,0,0,.25));
}
ただしfilter: drop-shadow は box-shadow より描画コストが高い点には注意してください。大量に使うページではパフォーマンスを計測することを推奨します。
崩れケース3: polygon() のアニメーションで頂点数が違うと崩れる
clip-pathのアニメーションで、開始状態と終了状態で polygon の頂点数が異なると、ブラウザは補間できず、カクついたり最終状態に瞬間スナップしたりします。
/* NG: 4頂点 → 6頂点 */
.el { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
.el:hover { clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%); }
対策: 開始・終了で同じ頂点数にする。変化させたくない頂点は同じ座標に2つ重ねて配置する方法もあります。
/* OK: 両方 6頂点 */
.el {
clip-path: polygon(0 0, 100% 0, 100% 0, 100% 100%, 0 100%, 0 100%);
}
.el:hover {
clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%);
}
Chrome と Firefox は頂点数不一致でも"ジャンプ"で終わるのに対し、Safari は中間状態で polygon が破綻した表示になることがあるため、特に注意が必要です。
崩れケース5: clip-path と CSS Grid/Flex の IntrinsicSize
Grid/Flex内の要素にclip-pathを当てると、IntrinsicSize(要素の自然サイズ)の計算に clip-path は影響しません。つまりクリップされた"見えない部分"もレイアウト計算上は存在します。
視覚的に小さく見えるアイコンが、実際は周囲に余白を作ってしまう現象です。
対策: width/height を明示するか、aspect-ratio で縦横比を固定すると予測可能になります。
.icon {
width: 32px;
height: 32px;
aspect-ratio: 1 / 1;
clip-path: polygon(/* ... */);
}
デバッグのコツ
- Safari Web Inspector で表示 — Chrome DevTools で動くものが Safari で動かない場合、Safari で直接見るのが最速
- transform: none; でアニメを切って確認 — 静的な形状は正しいか? transformとの合成が原因か切り分ける
- clip-path: none; でレイアウトを確認 — クリップなしで要素がどこにあるか確認してから戻す
- CanIUse で機能単位の対応状況を確認 —
clip-path: path()(SVGパス)などは対応が遅れているケースあり
まとめ
- Safariは
-webkit-clip-pathを併記すると安全 - clip-path + transform は will-change や translateZ(0) でGPU合成を促す
- box-shadow は切られる → filter: drop-shadow() に置換
- polygonアニメーションは開始と終了で頂点数を揃える
- 親のスタッキングコンテキストが不確実なら position: relative + isolation: isolate を付ける
- 本質的にレイアウト計算は"クリップ前のサイズ"で行われるため、width/height/aspect-ratioで明示する
参考文献・ソース
本記事は NanToo(ナンツー)運営の株式会社ヨネマスが編集・公開しています。 ツールの開発現場で得た知見をもとに、実務で役立つ内容を発信しています。