사내 공통 컴포넌트 프로젝트에 .css, .scss 에 대한 포맷팅에 대한 가이드가 없어 각 작업자마다 프리티어를 사용하는 사람, 사용하지 않는 사람 등 일관되지 않은 스타일을 작성하고 있었다.
일관성 있는 스타일 코드를 위해 Stylelint를 적용하기로 했다.
간단하게 설정을 적용하고 Stylelint를 돌려본 결과 no-descending-specificity 규칙이 warning 으로 대량으로 감지되었다.
no-descending-specificity 규칙이 무엇인지 알아보다가 그 동안 잊고 있었던 CSS 특수성에 대해 간단하게 정리해놓으면 리마인드할 겸 글을 작성하게 되었다.
가끔 CSS를 작성하다 보면 한 번쯤 이런 경험을 했을 수도 있다.
"분명 아래에 썼는데 왜 적용이 안 되지?"
범인은 특수성(Specificity)이다.
CSS의 스타일 적용은 "누가 더 구체적인가"로 우선순위를 정한다.
인라인 > ID > Class > Tag
(1,0,0,0) (0,1,0,0) (0,0,1,0) (0,0,0,1)숫자가 높을수록 우선순위가 높다. 만약 같으면? 나중에 선언된 게 이기게 된다.
/* 먼저 선언 */
.container .button { /* 특수성: 0,0,2,0 */
color: red;
}
/* 나중에 선언 */
.button { /* 특수성: 0,0,1,0 */
color: blue;
}.button이 아래에 있으니까 .button 의 color 는 blue가 적용되겠지?
하지만 red가 적용된다.
특수성은 선언 순서보다 강력하다. .container .button(클래스 2개)이 .button(클래스 1개)을 이깁니다.
코드를 읽을 때 우리는 위에서 아래로 읽는다. 자연스럽게 "아래에 있는 게 최종 상태"라고 기대하게 된다.
하지만 특수성이 뒤섞이면 다음과 같은 문제가 발생할 수 있다.
코드 순서와 실제 적용 순서가 다름
디버깅이 어려워짐
나중에 스타일 수정할 때 예상치 못한 곳이 깨짐
.button { /* 0,0,1,0 - 기본 스타일 */
color: blue;
}
.container .button { /* 0,0,2,0 - 특수한 경우 */
color: red;
}
낮은 특수성에서 높은 특수성 순서로 작성해야 위 문제를 방지할 수 있다.
위 특수성에서 발생할 수 있는 실수를 자동으로 잡아주는 린트 규칙이다.
{
"rules": {
"no-descending-specificity": true
}
}
높은 특수성 뒤에 낮은 특수성이 오면 경고를 띄워준다.
CSS에서 "나중에 쓴 게 이긴다"는 같은 특수성일 때만 성립한다.
특수성이 다르면, 선언 순서는 의미 없어진다.