CSS는 Cascading Style Sheets 를 나타낸다.
이 단어 중 Cascading이라는 단어는 아주 중요한 개념을 갖고있다.
css를 작성하다보면 내가 원하는 결과가 나오지 않을 때가 종종있다.
이는 같은 엘리먼트에 두 개 이상의 규칙이 적용되었기 때문이다.
Cascade와 specificity는 충돌이 일어날 때 제어해주는 메커니즘이다.
이러한 메커니즘을 이해할 필요가 있다.
또한 상속 개념도 중요하다.
일부의 CSS 속성은 기본적으로 상위 엘리먼트의 속성을 상속한다.
물론 상속되지 않는 속성도 존재한다.
이러한 애들 때문에 예상치 못한 동작들이 발생한다.
아래의 예제를 보자.
바깥 <ul>
에 보더, 패딩, 폰트 색깔을 설정했다.
color
속성은 상속되는 속성이다.
따라서 color
속성은 하위 자식 및 자손들에게 적용될 것이다.
그런 다음 바깥 <ul>
에 nested된 두번째 list에 special
이라는 클래스를 추가하고 다른 색깔을 적용했다.
이 또한 하위 자식 및 자손들에게 상속이 되었음을 알 수 있다.
<ul class="main">
<li>Item One</li>
<li>Item Two
<ul>
<li>2.1</li>
<li>2.2</li>
</ul>
</li>
<li>Item Three
<ul class="special">
<li>3.1
<ul>
<li>3.1.1</li>
<li>3.1.2</li>
</ul>
</li>
<li>3.2</li>
</ul>
</li>
</ul>
.main {
color: rebeccapurple;
border: 2px solid #ccc;
padding: 1em;
}
.special {
color: black;
font-weight: bold;
}
width
, margin
, padding
, border
와 같은 속성들은 상속되지 않는 속성이다.
CSS 속성 문서 페이지에서 상속이 되는지 안되는지 확인할 수 있지만, 직관적으로 추측이 가능하게끔 만들어 놨으니 매번 참고한답시고 문서를 들락날락하지 않아도 된다.
CSS는 상속을 제어하기 위해 5가지 특별한 속성 값을 제공한다.
inherit
부모 엘리먼트의 속성 값을 상속한다.
initial
해당 속성의 initial value로 설정한다.
initial value란?
사양의 정의 표에 나열된 기본 값을 나타낸다.
예: font-size의 Default value는 medium이다.
따라서 "font-size: initial"은 "font-size: medium"을 뜻한다
[주의]
브라우저의 기본 값과 혼동해서는 안된다.
revert
브라우저의 기본 값으로 설정한다.
revert-layer
앞선 cascade layer에서 설정된 값으로 설정한다.
cascade layer는 @layer
라는 CSS at-rule로 만들어진다.
간단한 예제를 보자.
<p>This example contains a list.</p>
<ul>
<li class="item feature">Item one</li>
<li class="item">Item two</li>
<li class="item">Item three</li>
</ul>
@layer base, special;
@layer special {
.item {
color: red;
}
.feature {
color: revert-layer;
}
}
@layer base {
.item {
color: blue;
}
.feature {
color: green;
}
}
맨 위에 선언한 순서에 따라 base layer가 먼저 적용되고 후에 special layer가 적용된다.
base layer에 따라 파란색과 초록색이 적용되었지만 뒤의 speical layer에서 빨강색과 revert-layer
색으로 덮어쓰기 되었다.
여기서 color : revert-layer
는 앞선 레이어, 즉 base layer의 color : green
을 뜻한다.
참고로 layer에 포함되지 않은 선택자들은 싸그리 모아져서 하나의 layer로 구성된 후, layer 선언의 가장 마지막에 위치된다.
또한, layer는 다른 layer의 cascade 및 specificity에 영향을 받지 않는다.
아무리 다른 레이어에서 아이디 선택자를 사용하여 specificity를 높이더라도 layer 순으로 덮어쓰기되기 때문이다.
unset
상속이 되는 속성의 값은 inherit
으로, 상속이 안되는 속성의 값은 initial
로 설정한다.
all
속성은 위의 상속 값들 ( inherit, initial ..., unset ) 을 모든 속성에 한 번에 적용시키는데 사용된다.
작성한 스타일을 취소하고자 할 때 편리하게 사용할 수 있다.
<blockquote>
<p>This blockquote is styled</p>
</blockquote>
<blockquote class="fix-this">
<p>This blockquote is not styled</p>
</blockquote>
blockquote {
background-color: red;
border: 2px solid green;
}
.fix-this {
all: unset;
}
두 번째 blockquote
의 all
속성에 unset
값을 설정했더니 스타일이 초기화가 된 것을 볼 수 있다.
cascade 적용되는 CSS의 규칙을 알아보자.
세 가지 고려사항이 있다.
1. Source order
2. Specificity
3. Importance
뒤의 번호가 더욱 중요하다.
따라서 Importance에서 정의한 것이 Source order에서 정의한 것을 덮어쓰기한다.
두 개이상의 규칙이 있다면 각각의 규칙의 무게는 동일하므로 소스 코드상 나중에 나오는 놈이 이긴다.
p {
color: red; // 적용 안됨
}
p {
color: green; // 나중에 나온 녀석이라 이 놈이 적용된다.
}
나중에 나온 규칙이 이전에 나온 규칙을 덮어쓰지 못하는 경우가 종종 발생한다.
이는 이전에 나온 규칙이 더 높은 specificity를 갖고 있기 때문이다.
예를 들어 클래스 선택자의 specificity가 엘리먼트 선택자 보다 높기 때문에 엘리먼트 선택자의 규칙이 더 나중에 오더라도 무시된다.
일반적으로 기본 엘리먼트를 스타일링한 후 변경할 부분을 클래스를 생성하여 속성과 값을 덮어쓰기하는 식으로 사용한다.
브라우저가 Specificity를 계산하는 방법을 알아보자.
선택자가 갖는 Specificity의 값은 아래 3가지 타입의 값을 사용하여 측정된다.
Identifiers: 전체 선택자 내부에 포함된 각 ID 선택자에 대해 개당 1점씩 매긴다.
예) #header #title {} // #header 1점 , #title 1점, 총 2점
Classes: 전체 선택자 내부에 포함된 각 class 선택자, 속성 선택자 또는 의사클래스(:어쩌구)에 대해 개당 1점씩 매긴다.
Elements: 전체 선택자 내부에 포함된 각 element 선택자 또는 의사요소(::어쩌구)에 대해 개당 1점씩 매긴다.
universal 선택자인 (
*
), 결합자(+
,>
등 ) 는 Specificity에 영향을 주지 않는다.
그리고:where()
는 본인과 파라미터 둘다 영향을 미치지 않는다.
하지만,:not()
,:has()
,:is()
는 그 자체로는 영향을 미치지 않지만 파라미터는 Specificity에 영향을 미친다.
아래의 예제를 통해 Specificity의 계산을 보자.
Identifiers가 제일 중요하고 Classes, Elements 순으로 중요도가 낮아진다.
각 선택자들의 Identifiers의 값을 평가한 후, 동일한 값이면 Classes 값을 평가한다.
그런 다음 거기서도 동일한 값이면 Elements 값을 평가한다.
이런 로직이기 때문에 Classes 값이 아무리 높아봐야 Identifiers가 0이면 Identifiers가 1인 선택자보다 Specificity가 낮다.
인라인 스타일은 Specificity에 관계없이 우선시 된다.
Identifiers가 아무리 높아봐야 인라인 스타일로 적용한 놈이 덮어쓴다.
위에 나온 모든 것들을 씹어먹는 필살기다.
오만떼만걸 다 덮어쓰기때문에 사용시 주의해야한다.
!important flag는 캐스케이드가 정상적으로 작동하는 방식을 변경하므로 특히 큰 스타일시트에서 디버깅 CSS 문제를 해결하기가 매우 어려울 수 있다.
정말 필요한 경우가 아니라면 사용하지 말자.
정말 필요한 경우는 언제일까?
!important 플래그를 사용해야 하는 한 가지 상황은 핵심 CSS 모듈을 편집할 수 없는 CMS에서 작업하는 경우이며, 다른 방법으로 재정의할 수 없는 인라인 스타일이나 중요 선언을 재정의하고 싶을 때이다.
CSS 선언의 우선순위는 그것이 어떤 스타일시트와 캐스케이드 레이어에 명시되어 있는지에 달려 있다.
사용자는 개발자의 스타일을 재정의하기 위해 사용자 지정 스타일시트를 설정할 수 있다.
예를 들어, 나는 눈이 침침해서 읽기 쉽도록 모든 웹 페이지의 글꼴 크기를 24px로 설정할 수 있다.
계단식 레이어에서 개발자 스타일을 선언할 수도 있다.
레이어에서 선언된 스타일을 재정의하거나 나중에 레이어에서 선언된 스타일을 이전에 선언된 레이어의 스타일을 재정의하도록 만들 수 있다.
예를 들어 개발자는 타사 스타일시트를 편집할 수 없지만 외부 스타일시트를 계단식 레이어로 가져올 수 있으므로 모든 스타일이 타사 선택기의 특수성에 대한 걱정 없이 가져온 스타일을 쉽게 재정의할 수 있다.
위에서 revert-layer
부분에서 cascade layer 얘기가 살짝 나왔는데 바로 그 얘기다.
cascade layer는 import 할 수 있다.
@import "어쩌구.css" layer(레이어이름);
단, 주의사항으로 @charset
및 @layer
rules를 제외한 다른 모든 유형의 rules보다 @import
가 먼저 나와야 한다.
충돌 선언은 다음 순서로 적용되며, 이후 선언은 이전 선언보다 우선된다
user agent 스타일 시트의 선언
(예: 브라우저의 기본 스타일, 다른 스타일이 설정되지 않은 경우 사용).
사용자 스타일 시트의 일반 선언
(사용자가 설정한 사용자 스타일).
작성자 스타일 시트의 일반 선언
(웹 개발자인 당사에서 설정한 스타일).
작성자 스타일 시트의 Important 선언.
사용자 스타일 시트의 Important 선언.
user agent 스타일 시트의 Important 선언.
!important로 플래그가 지정된 스타일의 경우 우선순위가 반전된다.
웹 개발자의 스타일시트는 사용자 스타일시트를 재정의하여 의도한 대로 설계를 유지하는 것이 타당하다.
그러나 때때로 사용자들은 웹 개발자 스타일을 재정의해야 할 필요성(위의 눈침침같은 경우)이 있으며, 이것은!important
를 사용함으로써 설정될 수 있다.
cascade layer에서도 important
플래그를 사용하면 우선순위가 반전된다.
예를 들어, 레이어가 a, b, c 순이라 가정하자.
a 레이어에 글꼴 색이 빨강,
b 레이어에 글꼴 색이 초록,
c 레이어에 글꼴 색이 파랑으로 설정했다.
결과는 파랑일 것이다.
하지만 각 레이어 모두 !important
플래그가 적용되었다고 할때,
결과는 빨강으로 나온다.
[참고] : MDN