
CSS :hover로 이미지 오버레이 만들기, 그 전에 생각해야 할 것들
JavaScript 없이 이미지 위에 설명을 띄울 수 있다는 말, 처음엔 반신반의했습니다. 포트폴리오 페이지를 만들면서 mouseenter 이벤트 리스너를 카드마다 붙이던 저도 결국 CSS :hover 한 줄로 갈아탔고, 코드가 절반 이하로 줄었습니다. 그런데 직접 써보고 나서 느낀 건, 구현 방법을 배우는 것보다 그 전에 생각해야 할 것들이 더 많다는 점이었습니다.
opacity, translateY, clip-path: 세 방식의 실질적인 차이
가장 흔하게 쓰이는 방법은 position:absolute와 opacity를 조합하는 방식입니다. 컨테이너에 position:relative를 주고, 오버레이를 absolute로 얹은 뒤 :hover 시 opacity를 0에서 1로 바꾸는 구조입니다. opacity는 요소의 불투명도를 제어하는 속성이고, transition과 함께 쓰면 페이드 인·아웃 효과를 만들 수 있습니다.
제가 이 방식을 처음 완성했을 때 오버레이가 이미지 밖으로 삐져나오는 문제가 있었습니다. 원인은 컨테이너에 overflow:hidden을 빠뜨린 것이었습니다. 이 속성은 자식 요소가 컨테이너를 벗어나면 잘라내는 역할을 합니다. 알고 보면 단순한 문제지만 처음엔 원인을 찾는 데만 한참이 걸렸습니다. 이후로는 이 속성을 컨테이너 스타일 첫 줄에 항상 넣는 습관이 생겼습니다.
transition의 타이밍 함수 차이도 실제로 써보기 전엔 얼마나 중요한지 몰랐습니다. ease, linear, ease-in-out을 직접 교체하며 비교했을 때 체감 차이가 꽤 컸습니다. ease-in-out은 시작과 끝에서 속도가 부드럽게 변하면서 자연스러운 느낌을 줬고, linear는 기계적으로 일정해서 오히려 어색했습니다. 픽셀 단위 차이가 아니라 시간 흐름 방식의 차이인데, 실제로 클릭해보면 분명히 느껴집니다.
translateY 방식은 오버레이를 아래로 숨겨뒀다가 :hover 시 끌어올리는 기법입니다. opacity보다 역동적인 느낌을 주는데, 여기서 한 가지 함정이 있었습니다. transition 속성에 transform을 명시하지 않으면 애니메이션이 동작하지 않고 즉시 나타납니다. 이 문제를 처음 만났을 때 CSS 자체 문제인지 브라우저 캐시 문제인지도 구분을 못 했습니다. transition: transform 0.3s ease-in-out처럼 대상 속성을 정확히 지정해야 한다는 걸 그때 제대로 배웠습니다.
clip-path 방식은 오버레이가 특정 방향에서 열리듯 나타나는 효과를 만듭니다. 저도 실험하면서 꽤 재미있게 가지고 놀았는데, 문제는 실무에 쓰기가 망설여진다는 점입니다. 구형 브라우저 지원이 제한적이라 클라이언트 프로젝트에서는 opacity나 translateY 방식을 선택하게 됩니다. 개성 있는 연출이 가능하지만, 접속 환경을 통제할 수 없는 서비스에는 리스크가 있습니다.
접근성: 시각 효과가 가리는 것들
:hover 기반 오버레이가 기술적으로 깔끔하다고 느끼는 순간, 저는 거기서 멈추면 안 된다는 걸 나중에야 알았습니다. 마우스를 쓰는 환경에서는 잘 동작하지만, 터치 기기에서는 hover 상태가 제대로 유지되지 않기 때문입니다. 탭 한 번으로 hover를 트리거하는 동작이 브라우저마다 달라서 UX가 일관되지 않는 문제도 있습니다. 모바일 사용자가 절반을 넘는 페이지라면 이 문제는 무시할 수 없습니다.
더 근본적인 문제는 텍스트 가독성입니다. 오버레이 안의 텍스트가 이미지 위에 얹히는 구조이기 때문에 명암 대비를 충분히 확보해야 합니다. WCAG 2.1에서는 일반 텍스트에 대해 최소 명암비 4.5:1을 요구합니다. 저도 처음엔 이 부분을 간과했습니다. 짙은 이미지에서는 충분히 읽혀도, 밝은 배경의 이미지에서는 텍스트가 묻혀버리는 상황이 생겼습니다. 반투명 오버레이 색상을 고정해두면 이 문제가 생각보다 자주 발생합니다. 비주얼이 예쁘다고 생각했던 배치가 실제로는 일부 이미지에서 텍스트를 완전히 삼키고 있었던 겁니다.
키보드 접근성 문제도 빠뜨리기 쉽습니다. :focus-visible 가상 클래스를 함께 사용하면 키보드로 탭 이동 시에도 오버레이가 표시되도록 할 수 있습니다. :focus-visible은 마우스 클릭이 아닌 키보드 입력으로 포커스를 받은 요소에만 선택적으로 스타일을 적용하는 속성입니다. :hover와 함께 :focus-visible을 병기하는 것, 저는 이걸 처음 프로젝트에 적용하고 나서야 그동안 키보드 사용자를 얼마나 배제하고 있었는지 실감했습니다.
이미지 오버레이 효과는 포트폴리오나 갤러리 페이지에서 분명히 인상적인 역할을 합니다. 그런데 제 경험상 시각 효과에 먼저 집중하다 보면 접근성은 나중으로 밀리기 쉽습니다. 이 순서를 바꾸는 게 결국 더 탄탄한 결과물을 만들어냅니다. 기본 구조와 명암 대비, 키보드 접근성을 먼저 잡아두고, 그다음에 translateY나 clip-path로 개성을 더하는 순서가 맞습니다. 순서가 바뀌면 효과는 화려하지만 실제로 쓸 수 없는 UI가 됩니다.
참고:
MDN — CSS :hover
MDN — CSS transition
W3C — WCAG 2.1
CSS-Tricks — Image Hover Effects