要素をぼかす
filter: blur() は長さ、つまりぼかしの半径を受け取ります。大きいほど要素がぼやけます:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CSS blur</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.row { display: flex; gap: 16px; flex-wrap: wrap; }
img { display: block; width: 160px; height: 100px; }
.b2 { filter: blur(2px); }
.b6 { filter: blur(6px); }
</style>
</head>
<body>
<div class="row">
<figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 100'%3E%3Crect width='160' height='100' fill='%2393c5fd'/%3E%3Ccircle cx='120' cy='30' r='16' fill='%23f59e0b'/%3E%3Cpath d='M0 100 L0 70 Q40 40 80 70 T160 60 L160 100Z' fill='%2316a34a'/%3E%3C/svg%3E" alt="Hills and a sun"><figcaption>no filter</figcaption></figure>
<figure><img class="b2" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 100'%3E%3Crect width='160' height='100' fill='%2393c5fd'/%3E%3Ccircle cx='120' cy='30' r='16' fill='%23f59e0b'/%3E%3Cpath d='M0 100 L0 70 Q40 40 80 70 T160 60 L160 100Z' fill='%2316a34a'/%3E%3C/svg%3E" alt="Hills and a sun, slightly blurred"><figcaption>blur(2px)</figcaption></figure>
<figure><img class="b6" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 100'%3E%3Crect width='160' height='100' fill='%2393c5fd'/%3E%3Ccircle cx='120' cy='30' r='16' fill='%23f59e0b'/%3E%3Cpath d='M0 100 L0 70 Q40 40 80 70 T160 60 L160 100Z' fill='%2316a34a'/%3E%3C/svg%3E" alt="Hills and a sun, very blurred"><figcaption>blur(6px)</figcaption></figure>
</div>
</body>
</html>
filter は画像だけでなく、どんな要素にも使えます。ぼかした div は、文字、border、子要素もぼかします。ぼかした画像の縁が柔らかくなっていることに注目してください。ぼかしは画像を外側に広げ、周りの透明な部分と混ぜるからです。直し方は下の「ぼかしの縁の問題」にあります。
背景をぼかす: backdrop-filter
要素の 後ろ にあるものをぼかす (写真やページの上のすりガラスのパネル) には、backdrop-filter を使います。要素自身の内容はくっきりしたままです。ぼかしを見せるには、透ける背景が必要です:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>backdrop-filter</title>
<style>
body { font-family: system-ui, sans-serif; margin: 0; }
.scene {
position: relative;
height: 200px;
background:
radial-gradient(circle at 25% 40%, #f59e0b 0 40px, transparent 41px),
radial-gradient(circle at 70% 60%, #16a34a 0 55px, transparent 56px),
repeating-linear-gradient(90deg, #2563eb 0 16px, #93c5fd 16px 32px);
}
.glass {
position: absolute;
inset: 40px 60px;
display: grid;
place-items: center;
background: rgb(255 255 255 / 0.35);
-webkit-backdrop-filter: blur(12px); /* older Safari */
backdrop-filter: blur(12px);
border: 1px solid rgb(255 255 255 / 0.6);
border-radius: 12px;
color: #111827;
font-weight: 700;
}
</style>
</head>
<body>
<div class="scene"><div class="glass">Sharp text on frosted glass</div></div>
</body>
</html>
ストライプと円はパネルの周りではくっきりし、パネルの後ろではぼやけていますが、パネルの文字はそのままです。文字は読みやすく保ちましょう。ごちゃごちゃした画像の上では、背景色を少し明るく、または暗くすると効果があります。
すべての filter 関数を試す
どのスライダーでも動かしてください。画像の下の行が、そのままコピーできる正確な filter の値です:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Filter playground</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.controls { display: grid; grid-template-columns: repeat(2, max-content); gap: 4px 18px; }
.controls input { vertical-align: middle; }
#img { display: block; width: 220px; height: 138px; margin: 12px 0 8px; }
code { display: block; background: #f3f4f6; padding: 6px; }
</style>
</head>
<body>
<div class="controls" id="controls"></div>
<img id="img" alt="Hills and a sun" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 100'%3E%3Crect width='160' height='100' fill='%2393c5fd'/%3E%3Ccircle cx='120' cy='30' r='16' fill='%23f59e0b'/%3E%3Cpath d='M0 100 L0 70 Q40 40 80 70 T160 60 L160 100Z' fill='%2316a34a'/%3E%3C/svg%3E">
<code id="out"></code>
<script>
// name, min, max, step, default (the value that changes nothing), unit
const fns = [
['blur', 0, 10, 0.5, 0, 'px'], ['brightness', 0, 2, 0.05, 1, ''],
['contrast', 0, 2, 0.05, 1, ''], ['grayscale', 0, 1, 0.05, 0, ''],
['sepia', 0, 1, 0.05, 0, ''], ['saturate', 0, 3, 0.1, 1, ''],
['hue-rotate', 0, 360, 5, 0, 'deg'], ['invert', 0, 1, 0.05, 0, '']
];
const controls = document.getElementById('controls');
fns.forEach(([name, min, max, step, def]) => {
controls.insertAdjacentHTML('beforeend', '<label>' + name + ' <input type="range" data-name="' + name + '" min="' + min + '" max="' + max + '" step="' + step + '" value="' + def + '"></label>');
});
const update = () => {
const parts = fns.filter(([name, , , , def]) => Number(controls.querySelector('[data-name="' + name + '"]').value) !== def)
.map(([name, , , , , unit]) => name + '(' + controls.querySelector('[data-name="' + name + '"]').value + unit + ')');
const value = parts.join(' ') || 'none';
document.getElementById('img').style.filter = value;
document.getElementById('out').textContent = 'filter: ' + value + ';';
};
controls.addEventListener('input', update);
update();
</script>
</body>
</html>
値を短く保つため、中立の値から動かした関数だけが書き出されます。brightness(1) や blur(0px) は何も変えないので、並べる理由がありません。
filter の関数
| 関数 | 中立の値 | 働き | 例 |
|---|
blur(r) | 0 | 半径 r のガウスぼかし | blur(4px) |
brightness(n) | 1 | 1 未満で暗く、1 を超えると明るく | brightness(0.7) |
contrast(n) | 1 | 1 未満で平板に、1 を超えるとくっきり | contrast(1.3) |
grayscale(n) | 0 | 1 (または 100%) で完全に灰色 | grayscale(100%) |
sepia(n) | 0 | 暖かい茶色の色調 | sepia(0.6) |
saturate(n) | 1 | 0 で灰色、1 を超えると鮮やか | saturate(1.5) |
hue-rotate(a) | 0deg | すべての色相を色相環に沿ってずらす | hue-rotate(90deg) |
invert(n) | 0 | 1 で完全な色の反転 | invert(1) |
opacity(n) | 1 | opacity プロパティと同じ | opacity(0.5) |
drop-shadow(x y blur color) | | 形に沿った影 | drop-shadow(2px 4px 6px rgb(0 0 0 / 0.3)) |
数値はパーセントでも書けます (grayscale(100%) は grayscale(1) と同じ)。複数の関数は空白で区切って 1 つの値に書き、順番に適用されます。filter: grayscale(1) sepia(1) は茶色がかった色になり、sepia(1) grayscale(1) はただの灰色になります。
drop-shadow は形に沿う
box-shadow は常に長方形です (せいぜい角が丸いだけ)。drop-shadow() は見えているピクセルから描かれるので、透明な部分のあるアイコンにはアイコンの輪郭の影が付きます:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>drop-shadow</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.row { display: flex; gap: 40px; padding: 10px 20px; }
img { width: 90px; height: 90px; display: block; }
.box-shadow { box-shadow: 6px 6px 8px rgb(0 0 0 / 0.4); }
.drop-shadow { filter: drop-shadow(6px 6px 4px rgb(0 0 0 / 0.4)); }
</style>
</head>
<body>
<div class="row">
<figure><img class="box-shadow" alt="Star" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23f59e0b' d='M12 2l3 7h7l-5.5 4.5 2 7.5-6.5-4.5-6.5 4.5 2-7.5L2 9h7z'/%3E%3C/svg%3E"><figcaption>box-shadow</figcaption></figure>
<figure><img class="drop-shadow" alt="Star" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23f59e0b' d='M12 2l3 7h7l-5.5 4.5 2 7.5-6.5-4.5-6.5 4.5 2-7.5L2 9h7z'/%3E%3C/svg%3E"><figcaption>drop-shadow()</figcaption></figure>
</div>
</body>
</html>
1 つ目の星は、見えない外枠のボックスから四角い影を落とします。2 つ目は星形の影を落とします。box-shadow と違い、drop-shadow() には spread の値も inset もありません。それらは box-shadow のページ で解説しています。
ぼかしの縁の問題
ぼかした画像は、周りの透明なピクセルを取り込むので、縁が薄れていきます。くっきりした縁を保つには (たとえばぼかしたヒーローの背景)、画像を overflow: hidden のコンテナに入れ、画像を少し拡大して、柔らかい縁を切り取ります:
.hero { overflow: hidden; }
.hero img { filter: blur(8px); transform: scale(1.1); }
filter を使ったホバーの効果
filter は transition できるので、灰色からカラーに変わるロゴの一覧や、ホバーするまで暗いカードも、ルール 2 つで作れます:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Filter on hover</title>
<style>
.logo {
width: 120px;
height: 70px;
display: inline-block;
background: linear-gradient(135deg, #2563eb, #16a34a);
border-radius: 8px;
filter: grayscale(1) opacity(0.6);
transition: filter 0.3s ease;
}
.logo:hover, .logo:focus-visible { filter: none; }
</style>
</head>
<body>
<a class="logo" href="#" aria-label="Partner one"></a>
<a class="logo" href="#" aria-label="Partner two"></a>
</body>
</html>
タイルにホバーする (または Tab で移動する) と、色がフェードインします。
知っておきたいこと
- パフォーマンス。 広い面積に大きな半径のぼかしをかけるのが一番重く、特に動いたりスクロールしたりする要素の
backdrop-filter は負荷が大きくなります。スマートフォンで試しましょう。
- 読みやすさ。 読む必要のある文字は決してぼかさず、すりガラスのパネルの上の文字はコントラストを確認しましょう。
- 重なり順。
none 以外の filter は、transform と同じく新しいスタッキングコンテキストを作り、position: fixed の子孫要素の包含ブロックになります。
よくある間違い
filter: blur(5)。 半径には単位が必要です: blur(5px)。
- すりガラスのパネルに
filter: blur() を使う。 パネル自身の文字がぼやけます。透明な背景と backdrop-filter を使いましょう。
- 何も表示されない backdrop-filter。 要素の背景が少なくとも部分的に透明で、後ろに何かがある必要があります。
filter の宣言が 2 つ。 2 つ目が 1 つ目を置き換えます。すべての関数を 1 つの値に書きましょう。
- 透明な部分のあるアイコンに四角い影が付く。
box-shadow の代わりに filter: drop-shadow() を使いましょう。