SMACSS - 유연하고 모듈화된 CSS 방법론 학습 - 4. 기초 스타일 쓰지 않기, 아이콘 모듈, 복잡한 상속, 코드 조판(Code Formatting)
기초 스타일 쓰지 않기
자주 쓰이지 않거나 용도가 다양하지 않은 요소라도, 기초 스타일로 스타일하겠다는 생각은 버려야 한다. <button>
, <table>
, <input>
이 그런 예다. 특정한 목적을 위한 스타일은 모듈로 만들어라. 그래야 내다보지 못한 일이 벌어졌을 때, 기존에 쓴 스타일을 덮어쓰느라 고생할 필요가 없다.
아이콘 모듈
CSS 스프라이트를 쓰면 HTTP 요청을 줄일 수 있고, 필요한 이미지를 미리 적재할 수 있다. 스프라이트 이미지는 배경으로 들어간다.
.menu li {
background: url(/img/sprite.png) no-repeat 0 0;
}
.menu .menu-inbox {
background-position: 0 -20px;
}
.menu .menu-drafts {
background-position: 0 -40px;
}
위와 같이 스프라이트를 이용할 경우 다음과 같은 문제가 생긴다.
<li>
이라는 특정한 HTML 요소에 의존하게 된다- 다른 모듈에서 스프라이트를 쓰려면 위 규칙을 다시 정의해야 한다.
- 이미지를 보이는 자리로 쓰는 요소 자체가 굉장히 불안정하다. 예를 들어 글꼴을 키우거나 할 경우 해서 스프라이트의 다른 부분이 드러날 수도 있다.
이러한 어려움을 해결하기 위해 아이콘을 모듈로 만들자.
<li><i class="ico ico-16 ico-inbox"></i> Inbox</li>
여기서 <i>
를 쓴 이유는 짧고, 말뜻이 없는(mostly devoid of semantics) 요소이기 때문이다. 아이콘이 홀로 쓰이는 경우 title
어트리뷰트를 매겨서 스크린 리더가 알아볼 수 있게 한다. <i>
을 쓰는 것이 마음에 안들면 <span>
을 쓰든지.
한편 여기서 클래스를 세 개 쓴 이유는 무엇일까?
.ico {
display: inline-block;
background: url(/img/sprite.png) no-repeat;
line-height: 0;
vertical-align: bottom;
}
.ico-16 {
height: 16px;
width: 16px;
}
.ico-inbox {
background-position: 20px 20px;
}
.ico-drafts {
background-position: 20px 40px;
}
스프라이트를 조밀하게 짤 수록 성능은 높아진다. 이미지 파일을 최적화해주는 Smush.it이나 ImageOptim과 같은 서비스를 이용해도 좋다.
복잡한 상속
예를 들어 달력을 만들어보자.
<table class="cal">
<tr>
<td>1 <td>2
<td>3 <td>4
<td>5 <td>6 <td>7
.....
</table>
.cal td {
background-color: #EFEFEF;
color: #333;
}
그런데 오늘 날짜를 강조하고 싶다.
.cal td.cal-today {
background-color: #F33;
color: #000;
}
여기서 선택자를 그냥 td.cal-today
라고만 썼다면? 이 규칙이 적용되려면 반드시 기본 규칙 다음에 나와야 한다. .cal td
와 .cal td.cal-today
는 선택자의 점수가 같기 때문이다. 두 점수가 같으면 뒤에 나오는 규칙이 채택된다.
.cal td = 클래스 + 요소 = 10 + 1 = 11
td.cal-today = 요소 + 클래스 = 1 + 10 = 11
한편 .cal-today
만 선택자로 쓴다면 !important
를 붙이지 않는 이상 먹히지 않을 것이다.
프로젝트를 진행하려면 언제나 이런 선택의 기로에 놓이게 된다. 위 예에서 채택한 선택자 .cal td.cal-today
는 .cal-today
가 .cal
이 거느리는 <td>
에만 적용된다는 것을 뜻한다.
이번에는 선택한 주를 강조하고자 한다.
<tr class="is-selected">
<td>1
<td class="cal-today">2
<td>3
...
여기서 .is-selected
는 어플리케이션 전반에 걸쳐서 사용되는 클래스다. 이 클래스는 어떻게 생겼을까?
.is-selected {
background-color: #FFD700;
color: #000;
}
보통은 <tr>
의 배경색이 <td>
로 상속되지만, 여기서는 그 주가 노랗게 되지 않는다. .cal td
와 .cal td.cal-today
가 상속받은 스타일을 덮어쓰기 때문이다.(그러나 저자의 말과는 달리 background-color
는 상속되지 않는 프로퍼티다. 물론 이 경우에도 td에 걸린 규칙이 .is-selected
가 지정한 배경을 가리는 것은 매한가지다.)
!important
를 붙여도 소용이 없다. !important
는 상속을 덮어쓰지 않으며, 선택자 점수만 높여주기 때문이다.
그래서 선택자를 다음과 같이 고쳤다.
.is-selected td {
background-color: #FFD700;
color: #000;
}
이 규칙은 .cal td
다음에 나와야 제대로 적용될 것이다.
만일 !important
를 썼다면?
.is-selected td {
background-color: #FFD700 !important;
...
}
그럼 더이상 .cal td.cal-today
스타일이 적용되지 않는다. 이 스타일을 먹이려면 상태 규칙과 모듈 규칙을 뒤섞을 수밖에 없다.
.is-selected td.cal-today {
background-color: #F33 !important;
...
}
이처럼 상속은 얘기치 않은 문제를 낳을 수 있고 완전한 해결책은 없다.
코드 조판(Code Formatting)
1줄 vs 여러줄
1줄로 쓰면 선택자를 빠르게 훑어볼 수 있었다. 규칙이 짧은 시기에는 한 번에 모든 규칙을 다 볼 수 있었다.
그러나 CSS3와 브라우저별 특수 규칙이 쓰이면서 1줄짜리 규칙은 점점 보기에 어려워졌다. 예:
.module {
display: block;
height: 200px;
width: 200px;
float: left;
position: relative;
border: relative;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
-moz-box-shadow: 10px 10px 5px #888;
-webkit-box-shadow: 10px 10px 5px #888;
box-shadow: 10px 10px 5px #888;
font-size: 12px;
text-transform: uppercase;
}
위 예에서 눈여겨 볼 다른 점들
- 콜론 다음에 공백을 넣었다.
- 탭이 아니라 공백 4개를 써서 들여썼다.
- 프로퍼티를 종류에 따라 묶었다.
- 선택자와 같은 줄에서 괄호를 열었다.
- 색깔은 짧은 형식으로 썼다(예:
#888
)
스타일 묶기
저자는 주관적인 중요도에 따라 다음과 같은 순서로 선언 블록을 쓴다고 한다.
- 박스:
display
,float
,position
,left
,top
,height
,width
- 테두리:
border
,border-image
,border-radius
- 배경
- 텍스트:
font-family
,font-size
,text-transform
,letter-spacing
- 기타: 나머지