기초 스타일 쓰지 않기

자주 쓰이지 않거나 용도가 다양하지 않은 요소라도, 기초 스타일로 스타일하겠다는 생각은 버려야 한다. <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)

스타일 묶기

저자는 주관적인 중요도에 따라 다음과 같은 순서로 선언 블록을 쓴다고 한다.

  1. 박스: display, float, position, left, top, height, width
  2. 테두리: border, border-image, border-radius
  3. 배경
  4. 텍스트: font-family, font-size, text-transform, letter-spacing
  5. 기타: 나머지