pointer-events 속성을 사용하면 특정 HTML 요소에서 발생하는 마우스오버/호버(:hover) 이벤트를 제거하거나 무시할 수 있습니다. 이 글에서는 그 구체적인 해결 방법을 단계별로 소개합니다.
최근 클라이언트를 위해 팀 소개 갤러리 페이지를 제작했습니다. 각 갤러리 항목은 UI 카드 형태로 표현되며, 마우스를 올리면 색상이 변하는 오버레이 효과가 적용되어 있습니다. 그런데 개발 과정에서 예상치 못한 문제에 부딪혔고, 이 경험이 바로 이 튜토리얼을 작성하게 된 계기가 되었습니다.
UI 카드의 콘텐츠와 구조
각 갤러리 카드 항목은 다음과 같은 요소로 구성됩니다.
- 직원 사진 — 흑백 처리
- 직원 이름 — 흰색 텍스트
- 직원 직책 — 흰색 텍스트
- 은은한 어두운 배경 오버레이
사용자가 카드 위로 마우스를 올리면 배경 오버레이가 브랜드 컬러인 연한 반투명 노란색으로 자연스럽게 전환됩니다.
UI 카드 예시
이 튜토리얼은 특정 문제 해결에 집중하기 위해 디자인을 단순화한 버전의 UI 카드를 사용합니다. 아래 카드에 마우스를 올려보면 배경색이 연한 노란색으로 전환되는 것을 확인할 수 있습니다.

그런데 여기에 문제가 있습니다. 눈치채셨나요? 텍스트 영역 위로 마우스를 움직여 보세요. 무엇이 잘못되었는지 보이시나요?
마우스가 텍스트 요소 영역에 들어가는 순간 호버 이벤트가 취소되어, 노란색이 즉시 사라지고 기본 비활성 상태인 회색으로 되돌아갑니다. 이건 도저히 받아들일 수 없죠!
먼저 해당 HTML 코드부터 살펴보겠습니다.
<div class="card">
<div class="card-overlay"></div>
<img
class="card-image"
src="https://kr.wsxdn.com/UploadFiles_1288/202203/2022033109324298.jpg"
/>
<div class="card-info">
<div class="card-name">Employee Name</div>
<div class="card-role">Employee Role</div>
</div>
</div>
다음은 CSS 코드입니다.
.card {
max-width: 350px;
position: relative;
}
.card-overlay {
background-image: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.5));
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
transition: background-color 0.6s;
}
.card-overlay:hover {
background-color: rgba(255, 204, 7, 0.5);
}
.card-image {
max-width: 100%;
display: block;
}
.card-info {
position: absolute;
bottom: 2rem;
left: 2rem;
}
.card-name,
.card-role {
color: white;
font-size: 1.5rem;
line-height: 1.25;
padding: 0.25rem;
}
.card-name {
font-weight: bold;
}
호버 이벤트가 취소되는 이유
텍스트 영역 위로 마우스를 옮기면 왜 호버 이벤트가 취소될까요?
원인은 HTML의 스태킹 순서(stacking order) 때문입니다. 일반적으로 스태킹 순서는 CSS의 z-index 속성으로 제어하지만, CSS로 명시적으로 지정하지 않으면 기본 스태킹 순서가 적용됩니다.
z-index는 요소의 쌓임 순서를 결정하는 CSS 속성입니다.position값이 동일하다는 전제하에, z-index 값이 가장 높은 요소가 다른 요소보다 앞에 배치됩니다. 웹 페이지의 모든 요소는 기본적으로 z-index 값이auto이며, 이는0과 같습니다.
이 예제에서는 두 요소 모두 position: absolute로 지정되어 있고, 둘 다 기본값인 z-index: auto(0)를 가집니다. 그렇다면 두 요소 중 어느 쪽이 앞에 위치할까요?
HTML에서 나중에 등장하는 요소가 앞에 옵니다.
즉, 이름과 직책 텍스트를 담고 있는 <div class="card-info"> 컨테이너가 card-overlay 요소보다 뒤에 작성되어 있기 때문에 화면상에서 더 앞쪽에 배치됩니다.
<div class="card">
<div class="card-overlay"></div>
<img
class="card-image"
src="https://kr.wsxdn.com/UploadFiles_1288/202203/2022033109324298.jpg"
/>
<div class="card-info">
<div class="card-name">Employee Name</div>
<div class="card-role">Employee Role</div>
</div>
</div>
따라서 UI 카드에서 텍스트 요소 위로 마우스를 옮기는 순간, 더 이상 오버레이 요소를 호버하는 것이 아니라 :hover 스타일이 적용되어 있지 않은 .card-info 요소를 호버하게 됩니다. :hover는 오직 .card-overlay에만 정의되어 있습니다(위 CSS 코드 참조).
방법 1: HTML 구조 변경
그렇다면 어떻게 해야 할까요? 먼저 <div class="card-overlay"></div> 요소를 .card-info 요소 아래로 물리적으로 이동시켜 보겠습니다.
<div class="card">
<img
class="card-image"
src="https://kr.wsxdn.com/UploadFiles_1288/202203/2022033109324298.jpg"
/>
<div class="card-info">
<div class="card-name">Employee Name</div>
<div class="card-role">Employee Role</div>
</div>
<div class="card-overlay"></div>
</div>
이렇게 하면 스태킹 순서가 뒤바뀌면서 다음과 같은 결과가 나타납니다.

좋습니다! 이제 텍스트 영역 위로 마우스를 움직여도 오버레이의 색상 전환이 더 이상 끊기지 않습니다.
그런데 잠깐... 스태킹 순서가 바뀌면서 이제는 오버레이가 텍스트 요소보다 앞쪽에 위치하게 되어, 텍스트가 오버레이에 의해 회색으로 덮여 버립니다.
한 가지 문제를 해결했더니 새로운 문제가 생긴 겁니다.
이 접근 방식은 사용하지 않기로 하고, <div class="card-overlay"> 요소를 원래 위치로 되돌리겠습니다.
방법 2: z-index 조정
그렇다면 .card-overlay 클래스에 z-index: 1 값을 부여하면 어떻게 될까요?
이제 card-overlay는 여전히 z-index: 0인 card-info보다 높은 쌓임 순서를 가지므로 화면 앞쪽에 배치됩니다. 덕분에 호버 시 색상 전환 효과도 끊김 없이 정상적으로 작동합니다. 좋네요! 하지만...
안타깝게도 결과는 앞서 HTML 구조를 직접 변경했을 때와 완전히 동일합니다. 이번에는 CSS로 쌓임 순서만 바꿨을 뿐이죠. 텍스트 요소는 여전히 오버레이에 의해 회색으로 덮여 있습니다.
이것 역시 용납할 수 없습니다!
pointer-events가 구원자로 등장합니다!
문득 CSS에 pointer-events라는 속성이 있다는 것이 떠올랐습니다. 이 속성을 사용하면 특정 요소가 포인터 이벤트에 반응할지 여부를 결정할 수 있습니다.
포인터 이벤트(pointer events)란 클릭, 스크롤, 요소 위로 마우스 이동(호버) 등 마우스와 관련된 모든 동작을 의미합니다.
그래서 저는 .card-info 클래스에 pointer-events: none; 한 줄을 추가했습니다.
.card-info {
position: absolute;
bottom: 2rem;
left: 2rem;
pointer-events: none; /* 해결책! */
}
이제 텍스트 영역을 포함한 UI 카드 전체에 마우스를 올려도 호버 이벤트가 취소되지 않으며, 텍스트 요소도 100% 흰색을 그대로 유지합니다.

전체 코드는 CodePen에서 확인하실 수 있습니다.