요소 흐리게 하기
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>Master CSS the hands-on way
Interactive lessons, real projects, and instant feedback.
START NOW
filter는 이미지뿐 아니라 모든 요소에서 동작해요. 흐리게 한 div는 텍스트, 테두리, 자식도 흐려져요. 흐리게 한 이미지의 부드러운 가장자리를 보세요. 흐림이 그림을 바깥으로 퍼뜨리고 둘레의 투명한 영역과 섞어요. 해결 방법은 아래 "흐림 가장자리 문제"에 있어요.
배경 흐리게 하기: 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)11보다 작으면 어둡게, 크면 밝게 brightness(0.7)contrast(n)11보다 작으면 밋밋하게, 크면 선명하게 contrast(1.3)grayscale(n)01(또는 100%)이면 완전한 회색 grayscale(100%)sepia(n)0따뜻한 갈색 톤 sepia(0.6)saturate(n)10이면 회색, 1보다 크면 더 선명하게 saturate(1.5)hue-rotate(a)0deg모든 색상을 색상환에서 옮겨요 hue-rotate(90deg)invert(n)01이면 완전한 색 반전 invert(1)opacity(n)1opacity 속성과 같아요opacity(0.5)drop-shadow(x y blur color)모양을 따라가는 그림자 drop-shadow(2px 4px 6px rgb(0 0 0 / 0.3))
숫자는 퍼센트로도 쓸 수 있어요(grayscale(100%)는 grayscale(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>
첫 번째 별은 보이지 않는 경계 상자에서 네모난 그림자를 드리워요. 두 번째 별은 별 모양 그림자를 드리워요. box-shadow와 달리 drop-shadow()에는 spread 값도 inset도 없어요. 이는 box-shadow 페이지 에서 다뤄요.
흐림 가장자리 문제
흐리게 한 이미지는 둘레의 투명한 픽셀을 끌어들여서 가장자리가 흐려져요. 딱 떨어지는 가장자리를 유지하려면(예를 들어 흐린 히어로 배경에서) 이미지를 overflow: hidden인 컨테이너에 넣고, 이미지를 살짝 키워서 부드러운 가장자리가 잘려 나가게 하세요.
.hero { overflow: hidden; }
.hero img { filter: blur(8px); transform: scale(1.1); }
filter로 만드는 hover 효과
filter는 트랜지션할 수 있어서, 회색에서 컬러로 바뀌는 로고 모음이나 hover하기 전까지 어두운 카드가 규칙 두 개면 돼요.
<!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 선언 두 개. 두 번째가 첫 번째를 대체해요. 모든 함수를 한 값에 넣으세요.
투명한 아이콘에 네모난 그림자. box-shadow 대신 filter: drop-shadow()를 쓰세요.