CSS의 :not() 셀렉터 활용, 티스토리 BookClub li 디스크 텍스트 겹침 해결
웹 문서를 마크업하다 보면 “특정 요소만 빼고 전부 디자인하고 싶다”는 요구가 자주 등장합니다. 예를 들어 ol > li
에만 들여쓰기를 적용했더니 ol > li > ul > li
의 불릿 아이콘이 텍스트와 겹쳐 보이는 난감한 상황을 겪어 보셨을 겁니다. 이때 ‘선택적 제외(Selective Exclusion)’를 구현해 주는 것이 CSS :not()
셀렉터입니다.
HTML 구조가 복잡해질수록 하위 요소마다 새로운 클래스를 주는 방식은 관리 비용이 기하급수적으로 커집니다. :not()
을 적절히 결합하면 불필요한 클래스 증식을 막고, 유지보수성이 높은 스타일 시트를 작성할 수 있습니다. 이 글에서는 :not()
셀렉터의 문법적 특성과 구체적 사용법, 그리고 실전에서 만나게 되는 퍼포먼스 이슈까지 전부 다룹니다.
:not() 셀렉터 기본 개념
구조적 부정(Structural Negation)
:not()
은 “괄호 안 조건을 만족하지 않는 모든 요소”를 선택합니다.
- 문법
A:not(selector)
A
가 선택한 집합에서selector
에 해당하지 않는 노드를 남기는 식으로 동작합니다. - 예시
.btn:not(.primary)
→.btn
클래스를 가진 요소 중primary
클래스가 없는 버튼만 선택.
단일 인자 원칙(Selectors Level 3)
CSS Selectors Level 3에 정의된 :not()
은 괄호에 단 하나의 간단 선택자(Simple Selector)만 허용합니다. 콤마로 여러 조건을 나열하는 기능은 Level 4부터 지원됩니다. 따라서 브라우저 호환성을 넓게 가져가려면 복수 조건은 중첩해서 써야 합니다.
/* 구식 브라우저 호환 – 중첩 */
a:not(.disabled):not(.external) { ... }
:not() 셀렉터와 선택자 우선순위(Specificity)
Specificity 값 계산
:not()
자체는 0,0,0 값을 갖지만 괄호 안 선택자의 점수를 그대로 승계합니다..box:not(#hero)
는 클래스(0,0,1,0) + 아이디(0,1,0,0) → 최종 0,1,1,0.
우선순위 충돌 예방법
신중하지 않으면 :not()
에 넣은 복합 선택자가 전체 스타일 시트를 뒤엎을 수도 있습니다. 설계 초기 단계에서 우선순위를 표로 정리해 두고, :not()
을 이용할 땐 항상 “보다 낮은 점수의 선택자로 덮어쓰겠다”는 원칙을 세우면 안전합니다.
:not() 셀렉터 결합 테크닉
자식 결합자 >
리스트 들여쓰기 예처럼 부모 ol
만 타깃할 때 다음 패턴이 유용합니다.
.entry-content ol:not(ul) > li {
text-indent: -15px;
}
ol:not(ul)
:ul
의 하위ol
을 제외한 **루트ol
**만 남김>
: 직계li
에만 적용해 후손li
겹침 방지
형제 결합자 +
, ~
h2:not(.no-border) + p { border-top: 1px solid #ddd; }
no-border
클래스가 없는 h2
다음 절친 p
에만 보더를 주는 식으로, 챕터 타이틀 사이 간격을 자동화할 때 자주 쓰입니다.
:not() 성능 인사이트
렌더 트리 탐색 모델
브라우저는 선택자 우측부터 좌측 순으로 매칭합니다(Right-to-Left Matching). :not()
내 조건이 복잡해질수록 트리 검사가 늘어나므로:
- 작은 범위부터 제외하고,
- 동일한 필터가 반복될 경우 공통 클래스로 치환
이 두 가지 원칙으로 페인트 누수를 최소화할 수 있습니다.
리플로(Reflow)를 줄이는 패턴
/* Good */
.article *:not(img):not(video) { line-height: 1.6; }
/* Bad – 모든 요소 필터 후 속성 추가 */
.article *:not(.skip) { ... }
와일드카드 *
뒤에 :not()
을 걸면 DOM 전체를 한 번 훑어야 하므로, 특정 요소에만 긍정 선택자를 써서 범위를 먼저 줄이는 편이 훨씬 빠릅니다.
티스토리 BookClub 스킨 사례 분석
문제 진단
기본 스킨 코드:
.entry-content ol li {
position: relative;
margin-bottom: 10px;
list-style: inherit;
text-indent: -15px;
}
ol > li > ul > li
구조에서 disc 아이콘이 텍스트 위로 겹침- 절댓값
-15px
이 직계li
뿐 아니라 손자li
에도 적용된 탓
개선 코드
/* 1. 공통 초기화 */
.entry-content ol li {
position: relative;
margin-bottom: 10px;
list-style: inherit;
text-indent: 0; /* 초기화 */
}
/* 2. 직계 ol > li에만 음수 들여쓰기 */
.entry-content ol:not(ul) > li {
text-indent: -15px;
}
.entry-content ol:not(ul)
→ul
내부ol
제어 X>
로 직계li
에만 영향
이렇게 하면 하위ul > li
의 disc가 정상적으로 표시되고, 루트ol > li
만 들여쓰기가 적용됩니다.
자주 쓰이는 :not() 패턴 5가지
- 다크 모드 토글
body:not(.dark) .dark-only { display: none; }
- 모바일 전용 숨김
@media (max-width: 767px) { .nav-item:not(.mobile-keep) { display: none; } }
- 폼 검증 하이라이트
input:required:not(:valid) { border-color: red; }
- 데이터 속성 필터링
[data-role]:not([data-role="admin"]) { opacity: .4; }
- 접근성 포커스 링 제거
button:not(:focus-visible) { outline: none; }
접근성과 유지보수 전략
의미론적 클래스 우선
class="skip"
같은 중립 네이밍을 권장합니다. 부정을 정의하는 이름(not-this
)은 동료 개발자에게 혼란을 주기 쉬우므로, 의미론으로 그룹을 묶되 :not()
으로 원치 않는 상황을 제외하는 방식이 이해하기 쉽습니다.
스타일 문서화
스타일 가이드를 작성할 때 :not()
로직은 “예외 규칙” 챕터로 별도 구분하면 좋습니다. 코멘트만으로는 시간이 지나면 원래 목적을 잊기 쉽기 때문에, 마크다운 가이드 혹은 Storybook에 “적용 시나리오 – 제외 조건 – 우선순위”를 명시적으로 기록하세요.
:not() 셀렉터, Level 4 그리고 미래
CSS Selectors Level 4부터 :not()
은 콤마 구분 복합 조건을 허용합니다.
/* Level 4 문법 – 최신 브라우저만 지원 */
a:not([href^="http"], [href^="//"]) { color: red; }
ESR(Extended Support Release) 브라우저, 혹은 IE 잔존 환경까지 커버해야 한다면 트랜스파일(예: PostCSS)이나 폴리필 전략을 고려해야 합니다.
결론
:not()
은 “조건을 제외한다”는 단순 발상으로부터, 유지보수성·접근성·성능을 한 번에 챙길 수 있는 강력한 도구입니다. 특히 리스트·내비게이션·폼처럼 반복 구조가 깊어질수록 클래스를 남발하지 않고 구조적인 제어를 가능케 해 줍니다. 다만 Specificity와 렌더링 성능을 충분히 고려하여,
- 좁은 범위부터 제외하고,
- 요소 트리를 최소 탐색하도록 선택자를 설계한다면, :not()은 프론트엔드 생산성을 극적으로 끌어올려 줄 것입니다. 이제 프로젝트 스타일 시트에서 ‘제외하고 싶은 상황’이 생긴다면, CSS :not()을 먼저 떠올려 보세요.
'컴퓨터 인터넷 모바일 it' 카테고리의 다른 글
엑셀 빠른채우기 기능 예제 (0) | 2025.03.15 |
---|---|
엑셀 한꺼번에 그림, 사진 지우기, 체크박스 일괄 삭제, 하이퍼링크 제거 (0) | 2025.03.09 |
gta 치트키, GTA5 치트 쓰는법 (0) | 2025.03.04 |
페이스북 탈퇴하는 방법: 계정 영구 삭제 (0) | 2025.02.22 |
크롬 브라우저 휴대전화에서 비밀번호 공유하기 (0) | 2025.02.20 |
댓글