backdrop-filter에 SVG 필터를 섞으면 선언 전체가 죽는다

메인 페이지 팝업에 유리 효과를 넣었다. 개발 중에 쓰는 미리보기 창에서는 잘 보였다. 크롬으로 열었더니 아무것도 없었다. 굴절도, 흐림도, 밝기 보정도 전부.

결론부터 쓰면, 브라우저가 그 효과를 지원하지 않아서가 아니었다. CSS 가 값 하나를 못 읽으면 선언 전체를 버린다는 규칙 때문이었다.

어디까지 만들어져 있었나

팝업은 뒤가 비쳐 보이는 유리다. 흐리기만 해서는 성에 낀 유리지 액체 유리가 아니다. 두께가 보이려면 뒤 배경이 실제로 휘어야 한다. 그래서 SVG 변위 맵을 만들어 backdrop-filter 에 물렸다.

backdrop-filter: url(#lg) blur(1px) contrast(1.06) brightness(1.08) saturate(1.12);

url(#lg) 가 굴절, 나머지가 흐림과 색 보정이다.

증상

미리보기 창에서는 유리가 보인다. 크롬에서는 팝업이 그냥 반투명한 판이다. 흐림조차 없다.

아니었던 것

배포를 의심했다. 이게 제일 그럴듯했다. 방금 올린 게 안 갔거나, 캐시가 옛것을 주고 있거나.

라이브 HTML 을 받아서 로컬 빌드와 해시로 비교했다. 바이트 단위로 같았다. CSS 파일도 받아서 열어봤다. 규칙이 그대로 있었다.

backdrop-filter:url(#lg)blur(1px)contrast(1.06)brightness(1.08)saturate(1.12)

배포는 정상이었다. 파일도 정상이었다. 그런데 화면에는 없었다.

원인

그럼 브라우저가 url() 을 못 받는 건가. 그렇다 치면 굴절만 빠지고 흐림은 남아야 한다. 그런데 흐림도 없었다.

여기서 걸렸다. CSS 는 선언 단위로 유효성을 판정한다. 값 목록 중 하나라도 파싱하지 못하면 그 선언을 통째로 버린다. 일부만 살려 쓰지 않는다.

backdrop-filter 에서 url() 을 못 받는 브라우저는 저 줄 전체를 무시한다. 굴절만 사라지는 게 아니라 흐림, 대비, 밝기까지 같이 사라진다. 그래서 “효과가 아예 없는” 것처럼 보였다.

미리보기 창은 Chromium 148 이라 url() 을 받았다. 그래서 정상으로 보였다. 두 환경이 달랐던 이유가 이거다.

고친 방법

먼저 층을 나눠봤다. 부모에 흐림, 자식에 굴절. 못 받는 브라우저는 자식만 버리면 되니 안전해 보였다.

이건 실패했다. 지원하는 환경에서도 굴절이 사라졌다. backdrop-filter 가 걸린 요소는 backdrop root 가 되고, 그 안쪽 요소가 보는 배경은 페이지가 아니라 그 요소 내부로 한정된다. 자식이 왜곡한 건 페이지가 아니라 팝업의 거의 투명한 5% 면이었다. 왜곡할 그림 자체가 없었다.

결국 @supports 로 갈랐다.

.sheet {
  backdrop-filter: blur(2px) contrast(1.06) brightness(1.08) saturate(1.12);
}

@supports (backdrop-filter: url(#lg)) {
  .sheet {
    backdrop-filter: url(#lg) blur(2px) contrast(1.06) brightness(1.08) saturate(1.12);
  }
}

선언을 버리게 만든 원인이 파싱 실패다. @supports 도 파싱으로 판정한다. 같은 기준으로 갈라내니 지원 여부와 정확히 맞아떨어진다. 못 받는 브라우저는 블록을 건너뛰고 위의 유리를 쓴다.

곁들여 하나 더

프리픽스를 손으로 쓰고 있었다. backdrop-filter-webkit-backdrop-filter 를 나란히.

빌드된 CSS 를 열어보니 @supports 안에서 표준 속성이 지워지고 프리픽스만 남아 있었다. 미니파이어가 둘을 같은 것으로 보고 하나를 정리한 결과다. 크롬은 프리픽스도 받으니 당장은 돌아가지만, 표준 속성이 사라진 CSS 를 남기고 싶지는 않았다.

프리픽스를 손으로 쓰는 걸 그만뒀다. 미니파이어가 알아서 붙인다.

남은 것

어느 버전부터 backdrop-filter 에서 url() 을 받는지는 안 찾아봤다. 폴백이 있으니 급하지 않다.

교훈은 버전 얘기가 아니다. 한 선언에 지원이 갈리는 값과 안 갈리는 값을 같이 쓰면, 갈리는 쪽이 안 갈리는 쪽까지 데리고 죽는다. 폴백을 만들 생각이었다면 애초에 줄을 나눠 썼어야 했다.

← 블로그 목록