CSS :near() — 마우스가 '가까이' 오면 반응하는 새로운 의사 클래스
:hover는 요소 위에 마우스가 정확히 올라가야 반응한다. 1px이라도 벗어나면 끝이다. 하지만 실제 사용자 경험을 생각해보면, "근처에 왔을 때" 미리 반응하는 게 더 자연스러운 경우가 많다. 툴팁이 마우스가 다가오면 슬쩍 나타나거나, 숨겨진 버튼이 포인터가 근처에 오면 드러나는 식이다. CSS Working Group에 제안된 **:near()**는 이 "근접성"을 CSS에서 직접 다룰 수 있게 하는 의사 클래스다.
:near()가 뭔가
:near()는 포인터가 요소로부터 지정한 거리 이내에 있을 때 스타일을 적용하는 CSS 의사 클래스다. 핵심은 유클리드 거리(직선 거리) 계산이다. 비유하면 :hover가 "문을 열어야 불이 켜지는 센서"라면, :near()는 "복도를 걸어오는 것만으로 불이 켜지는 모션 센서"다.
button:near(3rem) {
opacity: 1;
transform: scale(1);
}
button:not(:near(3rem)) {
opacity: 0.3;
transform: scale(0.9);
}소괄호 안의 <length> 값이 활성화 반경이다. 3rem이면 요소 경계에서 3rem 이내에 포인터가 들어왔을 때 매칭된다.
[💡 잠깐! 이 용어는?]
유클리드 거리(Euclidean Distance): 두 점 사이의 직선 거리를 뜻한다. :near()는 포인터 좌표와 요소의 가장 가까운 경계 사이의 직선 거리를 계산해서 지정한 반경과 비교한다.
기존 선택자와 뭐가 다른가
| 선택자 | 트리거 조건 | 반경 설정 | 용도 |
|---|---|---|---|
:hover | 포인터가 요소 위에 있을 때 | 불가능 (요소 경계 고정) | 기본 상호작용 |
:focus | 키보드/탭으로 포커스 | 해당 없음 | 접근성 |
:has() | 자식/형제 상태 기반 | 해당 없음 | 구조적 선택 |
:near() | 포인터가 근처에 있을 때 | 사용자 지정 가능 | 근접 기반 UI |
중요한 설계 원칙이 하나 있다. :near()는 :hover나 :focus-visible을 암시하지 않는다. "가까이 왔다"와 "위에 올렸다"는 다른 상태이고, 이 둘을 혼동하면 사용자가 클릭 가능한 상태와 단순 시각 피드백을 구분할 수 없게 된다.
어디에 쓸 수 있나
조건부 가시성
이미지 위에 공유 버튼이 있는데, 평소에는 숨겨두고 마우스가 근처에 오면 드러나는 패턴이다. 기존에는 부모 요소에 :hover를 걸어야 했는데, 부모 영역이 넓으면 의도치 않게 버튼이 나타나는 문제가 있었다. :near()는 버튼 자체에 반경을 설정하므로 더 정밀하게 제어할 수 있다.
.share-button {
content-visibility: hidden;
contain-intrinsic-size: auto none;
}
.share-button:where(:near(3rem), :hover, :focus-visible) {
content-visibility: visible;
background: black;
color: white;
}contain-intrinsic-size: auto none이 핵심이다. 버튼이 숨겨져 있어도 레이아웃 공간은 유지되므로 포인터가 그 영역에 접근할 수 있다. 비유하면 투명 망토를 쓴 사람이 여전히 자리를 차지하고 있는 것과 같다.
[💡 잠깐! 이 용어는?]
content-visibility: 요소의 렌더링을 지연하거나 숨기는 CSS 속성이다. hidden으로 설정하면 display: none과 달리 레이아웃 공간은 유지하면서 렌더링만 건너뛴다.
프리페치 트리거
링크에 마우스가 가까이 오면 해당 페이지를 미리 로드하는 용도다. Speculation Rules API와 결합하면 :near()가 프리페치/프리렌더의 트리거 역할을 할 수 있다. 사용자가 실제로 클릭하기 전에 페이지를 준비해두는 셈이다.
툴팁·힌트 조기 표시
:hover로 툴팁을 띄우면 정확히 요소 위에 올려야 나타난다. :near()를 쓰면 포인터가 접근하는 순간 툴팁이 먼저 페이드인되어 체감 반응 시간이 줄어든다.
접근성 고려 사항
:near()의 가장 큰 우려는 거짓 어포던스(false affordance) 문제다. 시각적으로 활성화된 것처럼 보이지만 실제로는 클릭 가능한 상태가 아닐 수 있다. WCAG 2.4.7(Focus Visible)과의 관계도 명확하지 않다.
| 우려 사항 | 설명 |
|---|---|
| 거짓 어포던스 | 근접 시 시각 변화가 "클릭 가능"으로 오인될 수 있음 |
| 타겟 크기 모호성 | :near() 반경이 WCAG 2.5.8(Target Size) 계산에 포함되는지 불명확 |
| 남용 가능성 | 히트맵 수집, 광고 트래킹에 악용될 소지 |
| 열악한 UX 은폐 | 좋지 않은 레이아웃을 :near()로 가리려는 시도 가능 |
CSS-Tricks 원문에서도 강조하는 원칙은 이것이다. :near()는 선제적(preemptive) 힌트여야 하지, 추정적(presumptive) 상태 표시여서는 안 된다.
지금 쓸 수 있나
결론부터 말하면, 아직 쓸 수 없다. :near()는 W3C CSS Working Group에 제안(GitHub issue #13271)된 단계이며, 어떤 브라우저에서도 구현되지 않았다. CSS-Tricks 원문의 데모도 패딩과 :hover를 이용한 시뮬레이션이다.
하지만 제안 자체가 의미 있는 이유가 있다. 현재 이 효과를 구현하려면 pointermove 이벤트 리스너로 매 프레임 좌표를 계산해야 하는데, 성능 비용이 크고 구현도 복잡하다. 브라우저 네이티브로 이 계산을 처리하면 메인 스레드 부담 없이 더 효율적으로 동작할 수 있다.
document.addEventListener('pointermove', (e) => {
const button = document.querySelector('.target');
const rect = button.getBoundingClientRect();
const distance = Math.hypot(
Math.max(rect.left - e.clientX, e.clientX - rect.right, 0),
Math.max(rect.top - e.clientY, e.clientY - rect.bottom, 0)
);
button.classList.toggle('near', distance < 48);
});이런 JavaScript 코드가 CSS 한 줄로 대체될 수 있다는 게 :near()의 핵심 가치다.
[💡 잠깐! 이 용어는?] CSS Working Group(CSSWG): W3C 산하에서 CSS 명세를 작성하고 관리하는 워킹 그룹이다. 새로운 CSS 기능은 보통 GitHub issue로 제안되고, 논의를 거쳐 명세로 발전한다.
정리
:near()는 포인터가 요소 근처에 왔을 때 스타일을 적용하는 새 CSS 의사 클래스 제안이다- 유클리드 거리 기반으로 활성화 반경을
<length>값으로 지정할 수 있다 - 조건부 가시성, 프리페치 트리거, 툴팁 조기 표시 등에 활용할 수 있다
- 아직 어떤 브라우저에서도 구현되지 않았지만, 기존 JavaScript 구현을 대체할 가능성이 있다
- 거짓 어포던스와 접근성 문제에 대한 신중한 설계가 필요한 제안이다
참고:
- CSS-Tricks: https://css-tricks.com/potentially-coming-to-a-browser-near-you/
- CSSWG Proposal: https://github.com/w3c/csswg-drafts/issues/13271
관심 있을 만한 포스트
CSS @property — 커스텀 속성에 타입을 부여하는 방법
CSS @property at-rule로 커스텀 속성에 타입 정의, 상속 제어, 폴백을 추가해 렌더링 안정성과 애니메이션 가능성을 확보하는 방법을 다룬다.
CSS만으로 커스텀 셀렉트 박스 — JavaScript 150줄이 사라지는 순간
Chrome 135에 도입된 appearance: base-select와 sibling-index()로 JavaScript 없이 완전한 커스텀 드롭다운을 구현하는 방법을 분석한다.
sibling-index()로 만드는 CSS 스크롤 소용돌이 — JavaScript 없이 수백 개 요소 애니메이션
CSS sibling-index()와 scroll-driven animations를 결합해 순수 CSS만으로 텍스트 보텍스 효과를 구현하는 기법을 다룬다.
Interop 2026 — 브라우저 전쟁이 끝나고 표준 전쟁이 시작됐다
Chrome, Safari, Firefox가 합의한 20개 웹 표준 집중 영역과 프론트엔드 개발자가 주목해야 할 핵심 기능을 정리한다.
CSS Stacking Context — z-index: 99999를 줬는데 왜 안 올라올까
CSS 쌓임 맥락의 생성 조건, z-index의 실제 작동 원리, 그리고 레이아웃 버그를 디버깅하는 실전 전략을 정리한다.
Next.js 블로그 만들기 — 정적 블로그에 맞춤 추천 포스트 기능 추가
localStorage에 조회 이력을 저장하고, 태그 가중치 스코어링으로 정적 블로그에서도 개인화 추천을 구현하는 방법.
Next.js 블로그 만들기 — 정적 블로그에 검색 기능 추가
빌드 타임 검색 인덱스 생성과 클라이언트 사이드 필터링으로 정적 블로그에 검색 기능을 구현하기. Cmd+K 단축키, 오버레이 UI까지.
Next.js 블로그 만들기 — 스크롤 프로그레스 바와 Canvas 렌더링 이슈 해결
스크롤 진행률 프로그레스 바 구현과 Canvas 커서 효과가 GNB backdrop-blur와 충돌하며 발생한 깜빡임 이슈 해결기.