メインコンテンツまでスキップ
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

フィルターエフェクト

問題

AIエージェントはCSSフィルターを活用できていないことが多く、CSS filter でネイティブに処理できるエフェクトに対して画像編集ツールやJavaScriptに頼ってしまいます。フィルターを使う場合でも、filter: drop-shadow()box-shadow を混同したり、backdrop-filter: blur() のつもりで要素に blur() を適用したり、複数のフィルター関数を1つの宣言でチェーンできることを忘れたりします。drop-shadow()box-shadow の決定的な違い — シェイプ認識 — はほとんど考慮されません。

解決方法

CSS filter プロパティは、要素とそのすべてのコンテンツにグラフィカルなエフェクトを適用します。スペース区切りのフィルター関数リストを受け付け、順番に適用されます。主な関数には blur()brightness()contrast()saturate()grayscale()sepia()hue-rotate()invert()opacity()drop-shadow() があります。

最も重要な区別は、filter(要素自体に影響)と backdrop-filter(要素の背後に影響)です。filter 内では、drop-shadow() が要素のアルファチャンネルの形状に追従するのに対し、box-shadow は常に矩形としてレンダリングされます。

コード例

個別のフィルター関数

/* Blur */
.blurred {
filter: blur(4px);
}

/* Brightness — 1 is normal, >1 is brighter, <1 is darker */
.bright {
filter: brightness(1.3);
}

.dimmed {
filter: brightness(0.6);
}

/* Contrast — 1 is normal, >1 is more contrast */
.high-contrast {
filter: contrast(1.5);
}

/* Saturate — 1 is normal, 0 is grayscale, >1 is oversaturated */
.vivid {
filter: saturate(1.8);
}

.desaturated {
filter: saturate(0.3);
}

/* Grayscale — 0 is normal, 1 is fully gray */
.gray {
filter: grayscale(1);
}

/* Sepia — vintage photo tint */
.vintage {
filter: sepia(0.8);
}

/* Hue-Rotate — shifts all colors around the color wheel */
.hue-shifted {
filter: hue-rotate(90deg);
}

/* Invert — negative image */
.inverted {
filter: invert(1);
}

複数フィルターのチェーン

フィルターは左から右へ適用されます。順序が重要です。brightnesscontrast の前に置く場合と contrastbrightness の前に置く場合では結果が異なります。

/* Vibrant, slightly warm look */
.photo-enhance {
filter: contrast(1.1) saturate(1.3) brightness(1.05);
}

/* Muted vintage effect */
.photo-vintage {
filter: sepia(0.4) contrast(0.9) brightness(1.1) saturate(0.8);
}

/* Dramatic noir */
.photo-noir {
filter: grayscale(1) contrast(1.4) brightness(0.9);
}

drop-shadow() と box-shadow の違い

box-shadow は要素のバウンディングボックスの背後に矩形のシャドウをレンダリングします。drop-shadow() は透明な部分を含む要素の実際の形状に追従します(PNG/SVG画像のアルファチャンネル)。

/* box-shadow — rectangle behind the entire element */
.icon-box-shadow {
box-shadow: 4px 4px 8px hsl(0deg 0% 0% / 0.3);
}

/* drop-shadow — follows the icon's shape */
.icon-drop-shadow {
filter: drop-shadow(4px 4px 8px hsl(0deg 0% 0% / 0.3));
}
<!-- On a transparent PNG, the difference is dramatic -->
<img class="icon-box-shadow" src="star-icon.png" alt="Star" />
<img class="icon-drop-shadow" src="star-icon.png" alt="Star" />

最初の画像は矩形のシャドウを持ちます。2番目の画像は星のアウトラインに沿ったシャドウを持ちます。

drop-shadow の構文の違い

/* drop-shadow does NOT support: */
/* - inset keyword */
/* - spread radius */
/* Syntax: drop-shadow(offset-x offset-y blur-radius color) */

.shadow {
/* Valid */
filter: drop-shadow(2px 4px 6px hsl(0deg 0% 0% / 0.2));

/* Invalid — no spread value allowed */
/* filter: drop-shadow(2px 4px 6px 2px black); */

/* Invalid — no inset allowed */
/* filter: drop-shadow(inset 2px 4px 6px black); */
}

フィルターによるホバーエフェクト

.image-hover {
transition: filter 0.3s ease;
}

/* Brighten on hover */
.image-hover:hover {
filter: brightness(1.15);
}
/* Color to grayscale on idle, full color on hover */
.team-photo {
filter: grayscale(1);
transition: filter 0.4s ease;
}

.team-photo:hover {
filter: grayscale(0);
}
/* Subtle zoom + brightness for image cards */
.card-image {
overflow: hidden;
}

.card-image img {
transition:
filter 0.3s ease,
transform 0.3s ease;
}

.card-image:hover img {
filter: brightness(1.1) saturate(1.2);
transform: scale(1.03);
}

フィルターによる無効化状態

.disabled {
filter: grayscale(1) opacity(0.5);
pointer-events: none;
}

クリップされた要素の drop-shadow

drop-shadow()clip-path を尊重しますが、box-shadow は常に矩形としてレンダリングされます。

.clipped-with-shadow {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
filter: drop-shadow(4px 4px 8px hsl(0deg 0% 0% / 0.3));
/* Shadow follows the diamond clip shape */
}

注意:filter は要素自体に設定する必要があります。シャドウが clip-path でカットされる場合は、要素をコンテナでラップし、filter をコンテナに適用しましょう。

/* Shadow wrapper pattern */
.shadow-wrapper {
filter: drop-shadow(4px 4px 8px hsl(0deg 0% 0% / 0.3));
}

.shadow-wrapper .clipped-element {
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
background: white;
}
<div class="shadow-wrapper">
<div class="clipped-element">Diamond with shadow</div>
</div>

ダークモード用の画像フィルターショートカット

/* Quick dark mode adaptation for decorative images */
@media (prefers-color-scheme: dark) {
.decorative-image {
filter: brightness(0.85) contrast(1.1);
}
}

ライブプレビュー

フィルターエフェクトギャラリー
不規則な形状での drop-shadow vs box-shadow

AIがよくやるミス

  • 透明画像に box-shadow を使う — シャドウが矩形として表示され、画像の形状が無視されます。filter: drop-shadow() はアルファチャンネルに追従します。
  • filter: blur()backdrop-filter: blur() を混同するfilter: blur() は要素とそのすべてのコンテンツ(テキスト含む)をブラーします。backdrop-filter: blur() は要素の背後にあるものだけをブラーします。
  • drop-shadow() にスプレッド値を追加するdrop-shadow() はスプレッドパラメータをサポートしていません。AIエージェントが box-shadow の構文をそのまま drop-shadow() にコピーして無効なCSSを生成します。
  • 複数の個別の filter 宣言を適用する — 最後の filter 宣言だけが有効になります。1つの宣言で関数をチェーンしましょう:filter: blur(2px) brightness(1.2)
  • フィルターの順序を考慮しないbrightness(0.5) contrast(2)contrast(2) brightness(0.5) は異なる結果になります。チェーンされた関数の順序は重要です。
  • opacity プロパティで十分なのに filter: opacity() を使うfilter: opacity() 関数は他のフィルターとのチェーン用に存在しますが、単独のオパシティには opacity プロパティの方がシンプルで同等のパフォーマンスです。
  • クリップされた要素に直接 filter: drop-shadow() を適用する — シャドウもクリップされる可能性があります。代わりにラッピングコンテナにフィルターを適用しましょう。

使い分け

  • シェイプ認識シャドウ — 透明PNG、SVGアイコン、clip-path でクリップされた要素に drop-shadow() を使う場合
  • 画像処理 — 画像編集なしでのグレースケールのチーム写真、ヴィンテージフィルター、明るさ調整
  • ホバーエフェクト — インタラクション時に画像を明るくしたり、彩度を上げたり下げたりする場合
  • 無効化状態grayscale(1) opacity(0.5) を汎用的な無効化表現として使う場合
  • ダークモード調整 — ダークテーマでの画像の明るさ/コントラストの簡易調整
  • フィルターチェーン — CSSで直接Instagramのようなエフェクトを組み合わせる場合

Tailwind CSS

Tailwindは一般的なCSSフィルター関数のユーティリティクラスを提供しています:blur-*brightness-*contrast-*grayscalesepiahue-rotate-*invert。これらは1つの要素で組み合わせることができます。

フィルターエフェクトギャラリー

Tailwind: フィルターエフェクトギャラリー

ホバーフィルターエフェクト

Tailwind: ホバーフィルターエフェクト

参考リンク