Tech

filter: drop-shadowに気をつけて

公開

様々なものに「影を落とす」のに filter: drop-shadow(); は一見便利です。というのも、古より存在する box-shadow は個別の要素に適用するには必要十分なのですが、グループ化された要素に対してまとめて影を落とすことができません。
厳密には適用はできるのですが、それらグループを囲む「矩形」(box)に大して影がついてしまうので、思っている通りの影にはなりません。

いわゆる角の四角い画像や角丸 border-radius に合わせた型の影は box-shadow でも問題なく適用できるのですが、様々な要素が絡み合った複雑な形に対してシャドーを敷きたいとなると取れる手段は「影のためだけ」のレイヤーを裏に重ねるか、上記の drop-shadow フィルターを適用するしかなくなります。

ただしこのプロパティ、一度使うだけでありえないくらいiOS Safariでバグの温床になります。

理想と現実

“drop-shadow” | Can I use… Support tables for HTML5, CSS3, etc

みんな大好きCan I Useには2013年(iOS6)から完全に対応していると書いてあるので、なぁんだ安心して使える「枯れた」技術じゃんと捉えられておかしくありません。これが罠です。

この(嘘すぎる)「完全対応」ぶりを覆してほしいという議論(issues)が2022年から立てられ、未解決(Open)のまま今も続いているくらいにはまともに動きません。

types: filter-function: drop-shadow() — Not actually supported well by Safari (very buggy) · Issue #17726 · mdn/browser-compat-data

というのも、まったく動かない要素に使うならばある程度影ができます。個人的にはSVG画像にCSSという外から影を適用できる唯一の方法なので、場合によっては使うこともありますが……。

ただこのような複雑な形に影を付けたいような場面が求められるリッチコンテンツの制作において、まったく動かない=アニメーションすらしない要素が一体いくつありえるでしょうか。いや、ない。
つまり影のついた要素を自在に動かし始めるようなSafariのレンダリングを妨げるような現象が発生した際には、さらっと当たり前のように表示バグを起こしかねない原因になります。

上記のissuesにも in Safari (sometimes) とある通り、このプロパティを使うことで「必ずは起きないが稀に必ず起こる」リスクを背負うことになります。この「可能性」を防ぐには複雑な形にグループ化されている影を解きほぐし、個別の box-shadow にするしかありませんが、形の複雑さによっては限界があります。形は分かっているのに影専用レイヤーが必須になることも……。

現時点の状況

Safari(Webkit)サイドが完全に静観しているかというというと完全にそうともいえず、以下のようなバグ報告は2020年頃から上がっています。

219729 – filter won’t apply to child elements while they are animating

When a parent element has a filter, and the child element has an (active) animation, the child element will render without the filter applied.

訳すとすれば「親要素にフィルターがあり、子要素に (アクティブな) アニメーションがある場合、子要素はフィルターが適用されずにレンダリングされます。要素のアニメーションが停止すると、フィルターが子要素に適用されます。」という感じですが……。

これには親要素に filter がかかった要素がアニメーションした場合、停止するまで子要素の filter は無効化されるという明確なバグみたいな仕様が記載されています。それはつまり filter: drop-shadow の「グループ化した要素に影を落とせる」メリットに真っ向から反逆する挙動です。
この文章だけ見ても、(グループ化した)親要素に影をつけたとて、それを1pxでも動かすと(本来影をつけたい)子要素の影が動かしている間は効かなくなるということになってしまうので……。

といっても、実際は動かした要素に影の描写がついていかず残像のように残る現象になったりすることが多いです。せめて効かないなら消えてくれればわかりやすいのですがあくまでコーディング視点としてはデザインを再現したかっただけで、起こしたくて起こっているバグではないのが歯がゆい……。

また、この現象の本当に恐ろしい所は2020年に立ち上げられたバグ報告にも関わらずStatusが未だに「NEW」であることです。(2026/10/26現在)なおこのバグ報告自体は drop-shadow に限らず filter プロパティ全体の話で、より厄介……。(つまり静観してる……ってコト!?)

現状では決して動かないSVG画像とかに影を当てるだけの用途が良さそうです。SVGの内部で影を付ける方法もあるのですが、こちらについては話が長くなるので省略しますがこれまた(Safari関係が)厄介なので、動きさえなければベストなプラクティスといえるでしょう。

box-shadowとの違い

冒頭にも box-shadow との違いを話しましたが、適用できる範囲は違っても同じ「影」を設けるためのCSSとして両者の記述はとても似ています。

たとえば、X・Yの値は動かさずにぼかしが「12px」と設定されている影なら以下のようになります。

box-shadow: 0 0 12px #000;

これを諸般の理由で filter: drop-shadow() にする場合、「ぼかし」値にはどちらのプロパティにおいて3番目の値になるので比較的流用しやすく、以下のように書きたくなるでしょう。

filter: drop-shadow(0 0 12px #000);

ただしこの時「ぼかし」に与える値をそのままにしただけだと影の強さ(黒なら暗さ・白なら明るさ)が2倍程度に見えてしまいます。これはClaude曰く以下の違いがあるとのことでした。

  • box-shadow(Figmaの値もこれと同じ扱い)は、指定値の半分がぼかしの広がり(標準偏差)になります。
  • filter: drop-shadow() は、指定値がそのまま標準偏差になります。

すなわちbox-shadow側の「12px」ぼかしの例は、drop-shadow側では半分の「6px」ぼかしとして扱われています。そのため、それぞれの影の付け方に応じた指定をきちんと書くと以下のようになります。

box-shadow: 0 0 12px #000; /* 12px ÷ 2 = 6pxのぼかしが出る */

filter: drop-shadow(0 0 6px #000); /* 6px分のぼかしがそのまま出る */

つまり本来直感的な値は drop-shadow の方なのですが、歴史としては box-shadow の方が勿論古いため、FigmaやAiなどで指定されているぼかしは(filterのような使い方ができているのに) box-shadow と同じ値を採用しています。

単純に置き換えてしまうだけだと影が強く見えてしまうのでこういった点も踏まえて、使わざるを得ないような場面では気をつけて運用しなければならないプロパティだと言えるでしょう。