WHAT HAPPENS TO CSS WHEN WE LOAD UP A WEBPAGE?
LOAD HTML
user가 웹페이지를 오픈했을 때, html은 initial html file을 load하는 starting point가 된다.parse HTML
이 단계에서 html head를 포함한 stylesheet를 찾아서 load하고 parse하기 시작한다. 즉 html 코드가 load되면 parse하기 시작하고, 코드를 line by line으로 decode한다. 그리고 css를 load하고 parse 한다.- LOAD CSS & PARSE CSS
- cascade: 충돌하는 css 선언을 해결한다. (resolving conflicting css declaractions)
- process final css
- LOAD CSS & PARSE CSS
RENDER TREE = DOM + CSSOM
위의 과정을 거쳐 브라우저는DOM을 build하고 전체 web document가 생성된다.- DOM(Document Object Model): decoding 된 전체 html code가 저장되는 곳이다.
- CSSOM(Css Object Model): css parse가 끝나면 final css가 tree-like structure인 이 곳에 저장된다.
CSS PARSING PHASE
1 | .my-class { |
- selector: .my-class 부분
- Declaration Block: css rules.
color: blue;와 같이Declaration으로 구성되어 있다.Declaration은Property인color와Declared value인20px로 구성되어 있다.
CASCADE
서로 다른 stylesheets[different css sources: author, user, browser(user agent)]와 css rules와 declarations의 충돌을 해결하고 결합하는 과정으로 어떤 속성이 가중치가 있는지 체크하는 것이고, 특정 element에는 1가지 이상의 rule이 적용된다. cascade를 체크할 때 가장 중요한 것은 css rule이 어떠한 source를 기반으로 하는 것인지와 관련되어 있다.
different css sources
User-Agent: 브라우저가 elemets에 제공하는 기본 스타일이며, 브라우저 마다 스타일이 다소 다르게 보여질 수 있다. css resets를 사용하여 user-agent styles를 override하는 방법을 쓰는 것이 이러한 차이를 상쇄시키는 방법 중 하나이다.
User: 브라우저 사용자에 의해 정의되고 제어된다. 보통 style을 override하거나 웹사이트 접근성을 추가하기 위한 것으로, 모든 사람이 가지고 있는 것은 아니다.
Author: HTML document에 선언된 css를 말한다. 일반적으로 프론트엔드 개발자가 작성하여 제어할 수 있는 것을 말한다.
그런데 css rules와 declarations의 충돌을 어떻게 해결한다는 것일까?
which one takes precedence?
importance(weight),specificity,source order
- importance(weight): 선언된 위치(css source)에 근거하여 각 선언(declaration)에 서로 다른 중요성을 부여하는 것으로 시작한다.
1 | 1. user `!important` declarations |
그런데 만약 importance가 모두 같다면?
- specificity: 선언된 selector의 specificity를 비교하고 계산한다.
1 | 1. inline styles |
우선순위인 1번부터 차례로 다음과 같이 (inline, IDs, classes, elements)의 순서로 비교한다. 만약 비교한 결과가 (0,0,0,1), (0,1,0,1)이라면 전자가 specificity가 더 높은 것이다.
그런데 만약 specificity가 모두 동일하다면?
- source order: 코드의 마지막 선언은 다른 모든 선언을 override하고 적용된다. 만약 3rd-party stylesheets를 사용한다면 author stylesheet는 늘 가장 마지막에 위치해야한다.
내가 React project를 했을 때의 경험을 예시로 들어보겠다. 당시 SlidingPane이라는 라이브러리를 사용하여 팝업창을 구성했는데, 기본 설정된 배경색을 다른 색으로 변경했어야 했다. 그러나 이 라이브러리는 몹시 간결한 것이어서 그러한 변경에 대한 방법이 따로 설명되어 있지 않았다. 그래서 선택한 방법은 css파일에 변경해야 할 부분을 작성한 후 원래 있던 코드를 background: #D9DBE0;로 작성한 후 override하는 것이다.
1 | /* 원래 코드: react-sliding-pane.css */ |
1 | /* 내가 작성한 author 코드: Cart.css */ |
이렇게 작성 후 react로 작성된 Cart.js 파일의 상단에서 다음과 같은 순서로 import한다.
import ‘react-sliding-pane/dist/react-sliding-pane.css’;
import ‘./Cart.css’;
이렇게 하면 마지막에 import 된 Cart.css가 원래 존재하던 파일을 override하게 되기 때문에 배경색이 #fff에서 내가 설정한 #D9DBE0으로 변경된다.
CSS Selector Specificity
CSS Selector Specificity를 비교하는 과정을 예시를 통해 보다 자세히 알아보겠다.
1 | <nav id="nav"> |
예시에서 .button과 a는 specificity가 낮은 편이기 때문에 다른 속성에서 우선순위에 밀려 배경색이 나타나지 않는다. 하지만 specificity가 낮더라도 !important를 선언하면 다른 어떤 속성보다도 우선순위를 갖고, 즉각 배경색이 버튼에 표시된다. 하지만 !important는 최소 한 번 사용하거나 되도록 쓰지 않는 것이 권장된다.
1 | body { |
위의 코드에서 button에 마우스를 hover하면 색깔이 yellow로 바껴야 하지만, 아무런 변화가 없다. 그 이유는 pseudo class인 hover는 specificity에서 1으로 카운트되기 때문이다. 그러나 #nav div.pull-right a.button 이 경우 specificity는 id 1개, class 2개를 가지고 있다. #nav a.button:hover도 사실은 id 1개, class 2개를 가지고 있는 것인데, hover는 class가 아니라 class인 것으로 여기는 pseudo class이다. 따라서 전자는 2개의 elements를 가지고 있고, 후자는 1개의 elements를 가지고 있다. 결과적으로 #nav div.pull-right a.button가 훨씬 specificity가 높은 것이다. 따라서 hover는 적용되지 않는다. 만약 hover했을 때 색깔이 변경되도록 설정하고 싶다면 아래와 같이 코드를 바꾸어야 한다.
1 | #nav div.pull-right a.button { |
이렇게 코드를 작성하면 #nav div.pull-right a.button에 pesudo class인 hover가 추가되어 hover가 붙지 않은 elements와 비교했을 때 specificity가 높아진다. 따라서 hover했을 때 색깔이 green으로 바뀌는 것이다.
References
How CSS works: Understanding the cascade
Advanced css and sass