SMACSS - 유연하고 모듈화된 CSS 방법론 학습 - 1. 스타일의 다섯가지 유형
SMACSS(이하 ‘스맥스’)는 대규모 프로젝트를 위한 스타일링 지침이다.
스타일의 다섯가지 유형
스맥스는 스타일을 다섯가지로 분류하고, 각 유형에 맞는 선택자(selector)와 작명법(naming convention), 코딩 기법을 제시한다.
- 기초(Base)
- 레이아웃(Layout)
- 모듈(Module)
- 상태(States)
- 테마(Theme)
(1) 기초 스타일
흔히 말하는 reset 스타일류를 말하는 것이다.
- 기능: 요소(elements) 스타일의 기본값 지정
- 선택자
- 주로 요소 선택자(ex.
input[type=text]
같은 것) - 필요에 따라서 자식(child) 선택자, 자매 선택자, 어트리뷰트(attribute) 선택자 등
!important
를 쓸 이유가 없다.
- 주로 요소 선택자(ex.
예
html, body, form { margin: 0; padding: 0; }
input[type=text] { border: 1px solid #999; }
a { color: #039 }
a:hover { color: #03C; }
(2) 레이아웃 스타일
- 기능: 페이지를 구획하는 스타일(예: 헤더, 푸터, 그리드 …)
- 선택자: 아이디나 클래스
- 아이디는 CSS에서 클래스와 성능 차이가 없으며, 오히려 선택자 우선순위 점수(specificity) 때문에 함정에 빠질 우려가 있으므로, 신중하게 써야 한다.
- 작명법
- 아이디에는 이름 공간(namespace)을 할당하지 않아도 무방(이름 공간은 SMACSS에서 사용하는 접두어를 말한다. 예컨대 layout은
l-
이라는 접두어를 붙인다.) - 클래스는 ‘
l-
‘을 앞에 붙여서 다른 유형의 클래스와 구분
- 아이디에는 이름 공간(namespace)을 할당하지 않아도 무방(이름 공간은 SMACSS에서 사용하는 접두어를 말한다. 예컨대 layout은
레이아웃 스타일은 재사용성에 따라 큰 레이아웃 스타일과 작은 레이아웃 스타일로 구분된다.
큰 레이아웃 스타일
예시:
#header, #article, #footer {
width: 960px;
margin: auto;
}
#article {
border: solid #CCC;
border-width: 1px 0 0;
}
작은 레이아웃 스타일
960.gs
와 같은 레이아웃 프레임워크를 쓸 경우다. 아이디가 아니라 클래스를 매겨서 요소를 재사용한다.
재사용성
저자는 아이디를 매기기 전에 해당 요소의 재사용성을 숙고하라고 한다. 예시:
<div id="featured">
<h2>Features</h2>
<ul>
<li><a href="...">...</a></li>
<li><a href="...">...</a></li>
...
</ul>
</div>
div#featured ul {
margin: 0;
padding: 0;
list-style-type: none;
}
div#featured li {
float: left;
height: 100px;
margin-left: 10px;
}
위 코드는 다음과 같은 가정이 깔려있다.
- 격자가 딱 한번만 사용된다.
- 항목은 왼쪽부터 띄운다(float).
- 항목의 높이는 100픽셀이다.
위 코드를 아래와 같이 개선하면
.l-grid {
margin: 0;
padding: 0;
list-style-type: none;
}
.l-grid > li {
display: inline-block;
margin: 0 0 10px 10px;
/* IE7 hack */
*display: inline;
*zoom: 1;
}
다음과 같은 이점이 있다.
- 어느 박스에서나 그리드 레이아웃을 만들어낼 수 있다
- depth of applicability(나중에 다룰 것임)를 1로 줄였다
- 선택자의 우선순위 점수를 줄였다
- 가장 높은 항목의 높이에 따라 열의 높이가 바뀐다
다음과 같은 단점이 있긴 하지만
- 자식 선택자(
>
) 때문에 IE6이 배제된다(자식 선택자를 쓰지 않는 식으로 이 문제를 해결할 수 있다). - CSS의 크기와 복잡성이 증가했다.
재사용성이 증가했기 때문에, 코드를 중복하지 않아도 되고, 선택자의 우선순위 점수를 줄였기 때문에 레이아웃을 확장하기에 좋다.
(3) 모듈 스타일
- 기능: 레이아웃 요소 안에 들어가는 더 작은 부분들(예: 네비게이션 바, 말풍선, 대화 상자, 위젯 …)
- 선택자
- 클래스
- 모듈에 포함된 요소는 후손 선택자나 자식 선택자로. (그냥 띄어쓰는 게 후손 선택자고,
>
로 연결하는 게 자식 선택자다. 후손 선택자는.my-module li
, 후손 선택자는.my-ul>li
) - 요소 선택자(
a
같은 태그 선택자)는 피한다.(충돌을 피할 자신이 없다면) - 내용을 반영하는 선택자일수록 좋다.
하위 클래스 만들기(sub-classing)
모듈의 변종은 언제나 하위 클래스로 스타일링한다.
나쁜 예:
.pod {
width: 100%;
}
.pod input[type=text] {
width: 50%;
}
#sidebar .pod input[type-text] {
width: 100%;
}
위 예에서 새로운 하위 클래스를 추가하면 선택자가 번잡해진다.
.pod {
width: 100%;
}
.pod input[type=text] {
width: 50%;
}
#sidebar .pod input[type=text] {
width: 100%;
}
.pod-callout {
width: 200px;
}
#sidebar .pod-callout input[type=text],
.pod-callout input[type=text] {
width: 180px;
}
다음은 좋은 예다:
.pod {
width: 100%;
}
.pod input[type=text] {
width: 50%;
}
.pod-constrained input[type=text] {
width: 100%;
}
.pod-callout {
width: 200px;
}
.pod-callout input[type=text] {
width: 180px;
}
하위 클래스 적용하기:
.pod.pod-callout { }
<div class="pod pod-callout"> ... </div>
(4) 상태별 스타일
- 기능: 다른 스타일에 덧붙이거나 덮어 씌워서 상태를 나타냄(예: 펼침과 접힘, 성공과 실패 등)
- 선택자
- 클래스 선택자 한개
!important
를 쓸 수도 있다(최소화 할 것)- 같은 모듈에 두 상태를 적용하지 말 것
- 작명법
- ‘
is-
‘를 앞에 붙인다 - 특정 모듈에 한정된 상태는 모듈 이름도 그 뒤에 붙인다.(예:
is-tab-active
)
- ‘
- 유의: 전역 상태 스타일은 모듈 상태 스타일과 구분한다. 모듈 상태 스타일은 모듈 스타일과 병기한다.
예시
<div id="header" class="is-collapsed">
<form>
<div class="msg is-error">
There is an error!
</div>
<label for="searchbox" class="is-hidden">Search</label>
<input type="search" id="searchbox">
</form>
</div>
#header
- 레이아웃 요소임을 알 수 있다.
.is-collapsed
로 접힌 상태임을 알 수 있다.
.msg
- 모듈이다.
.is-error
로 오류 상태임을 알 수 있다.
#searchbox
- 연관된 라벨은 숨겨져 있다.
- 라벨이 마크업 된 이유는 스크린 리더기를 위한 것
모듈과 다른 점
- 모듈과 레이아웃 둘 다에 적용할 수 있다
- 페이지가 로드된 이후의 상태 변화를 나타내므로 자바스크립트에 의존한다.
(5) 테마 스타일
사용자가 테마를 선택할 수 있는 경우를 염두에 두고, 색상이나 이미지를 불변하는 스타일과 분리하는 것이다. 거의 사용할 일은 없지만 필요할 때가 있다.
예시
/* 모듈 스타일 */
.mod {
border: 1px solid;
}
/* 테마 스타일 */
.mod {
border-color: blue;
}
- 적용 범위가 넒은 테마는
theme-
를 접두어로 붙여서 혼란을 피한다 i18n
등을 염두에 두고 글꼴도 사용자가 바꿀 수 있게 하기 위해 글꼴 스타일도 따로 분류할 수 있다. 그러나 굳이 글꼴 별로 클래스를 만들 것 까지는 없다