상태 바꾸기

클래스 이름으로 바꾸기

버튼을 눌러서 요소를 숨기는 방법.

$('.btn-close').click(function(){
    $(this).parents('dialog').addClass('is-hidden');
});

버튼을 눌러서 메뉴를 보이는 방법(버튼을 누르면 버튼이 눌린 상태가 되고, 메뉴는 보이는 상태가 된다) 세가지.

작명 규칙(naming-convention)을 활용한 방법

마크업

<div id="content">
    <div class="toolbar">
        <button id="btn-new" class="btn" data-action="menu">New</button>
    </div>
</div>
<div id="menu-new" class="menu">
    <ul>...</ul>
</div>
  • data-action 어트리뷰트가 어떤 자바스크립트 코드를 호출할지 결정한다.
  • 버튼을 클릭하면 버튼의 아이디를 토대로 메뉴를 찾는다.

      $('#btn-new').click(function(){
          $(this).addClass('is-pressed');
          $('#menu-' + $(this).id.substr(4)).removeClass('is-hidden');
      });
    

메뉴와 버튼을 이웃하게 하기

마크업

<div id="content">
    <div class="toolbar">
        <button id="btn-new" class="btn" data-action="menu">New</button>
        <div id="menu-new" class="menu".
            <ul>...</ul>
        </div>
    </div>
</div>
  1. div.toolbar에 ‘.is-active’를 추가하고 스타일을 매기는 방법

     .is-active .btn { color: #000; }
     .is-active .menu { display: block }
    

    메뉴와 버튼이 같은 요소 안에 있어야만 하고, 다른 항목이 툴바에 들어가도 안된다.

  2. 형제 선택자(+)를 이용하는 방법

     .btn.is-active { color: #000; }
     .btn.is-active + .menu { display: block; }
    

    여전히 HTML 구조에 의존한다. 언제건 버튼과 메뉴가 이웃한다는 일관성을 지킬수만 있다면야 상관 없다.

“버튼 메뉴”를 위한 위 규칙들은 어디에서 선언해야 할까? 버튼? 메뉴? 버튼에 다는 게 낫다.

어트리뷰트 선택자로 상태 바꾸기

이점

  • 레이아웃 클래스나 모듈 클래스와 구분돼 보인다.
  • 여러가지 상태로 쉽게 전환할 수 있다.

예시:

.btn[data-state=default] { color: #333; }
.btn[data-state=pressed] { color: #000; }
.btn[data-state=disabled] { opacity: .5; pointer-events: none; }

HTML5는 data-로 시작하는 어트리뷰트를 허용한다.

CSS 애니메이션이 있는 클래스 기반 상태 변화

딜레마: CSS는 모습, 자바스크립트는 페이지의 작동을 규정한다. 그러나 애니메이션은 주로 자바스크립트로 처리돼왔다.

나타났다 사라지는 메시지의 예:

JS:

function showMessages (s) {
    var el = document.getElementById('message');
    el.innerHTML = s;
    el.className = 'is-visible';
    setTimeout(function() {
        el.className = 'is-hidden'
    }, 3000);
}

CSS:

@keyframes fade {
      0% { opacity:0; display:block; }
    100% { opacity:1; display:block; }
}

.is-visible {
    display: block;
    animation: fade 2s;
}

.is-hidden {
    display: none;
    admination: fade 2s reverse;
}

자바스크립트는 상태를 바꾸고, 상태에 따른 모습은 CSS가 담당한다. 그러나 많은 브라우저가 이를 구현해주지 않는다. 다행히도 CSS3 애니메이션 권고에 따라 브라우저들이 바뀌고 있는 중이다. 당분간은 윗 부분을 다음과 같은 방식으로 써야 하고, is-removed 클래스를 추가해야한다.

@-webkit-keyframes fade {
    ...
}

...

.is-removed { display: none; }

자바스크립트는 다음과 같이 쓴다.

function showMessages (s) {
    var el = document.getElementById('message');
    el.innerHTML = s;
    el.className = 'is-visible';
    setTimeout(function() {
        el.className = 'is-hidden'
        setTimeout(function(){
            el.className = 'is-removed';
        }, 2000);
    }, 3000);
}

의사(pseudo) 클래스 이름으로 상태 바꾸기

예시:

.btn { background-color: #333; }
.btn:hover { background-color: #336; }
.btn:focus { box-shadow: 0 0 3px rgba(48,48,96,.3); }

하위 모듈을 만들시 규칙을 다시 맞춰서 써야할 수도 있다.

.btn { background-color: #333; }
.btn:hover { background-color: #336; }
.btn:focus { box-shadow: 0 0 3px rgba(48,48,96,.3); }

.btn-special { background-color: #DEDB12; }
.btn-special:hover { background-color: #B8B50B; }

여기에 클래스로 상태를 나타내면 더 복잡해진다.

.btn { ... }
.btn:hover { ... }
.btn:focus { ... }

.btn-special { ... }
.btn-special:hover { ... }

.btn.is-pressed { ... }
.btn.is-pressed:hover { ... }

.btn-special.is-pressed { ... }
.btn-special.is-pressed: hover { ... }

미디어 쿼리

예시:

@media screen and (max-width: 400px) {
    #content { float: none; }
}

SMACSS에서는 특정 모듈에 적용되는 스타일을 분리해야하므로, 분기점을 하나만 만들기보다는 미디어 쿼리를 모듈의 상태와 함께 둬야한다.

.nav > li { float: left; }
@media screen and (max-width: 400px) {
    .nav > li { float: none; }
}

미디어 쿼리 선언을 여러번 해야할테지만, 모듈에 대한 정보를 한 곳에 다 모을 수 있다. 그러면 테스트하기에도 좋고, 모듈화해서 템플릿과 CSS를 따로 불러와도 된다.

적용 깊이(Depth of Applicability)

: 규칙에 영향을 받는 세대의 수. “body.article > #main > #content > #intro > p > b“는 6. 같은 페이지에서 “.article #intro b“도 6.

적용의 깊이가 크면 HTML 구조에 더 많이 의존하게 된다. 그러면 컴포넌트를 이동시키기 어려워진다. 사이드바에 있는 모듈을 푸터에도 만드려는 경우:

#sidebar div, #footer div { border: 1px solid #333; }
#sidebar div h3, #footer div h3 { margin-top: 5px; }
#sidebar div ul, #footer div ul { margin-bottom: 5px; }

이 모듈의 루트는 div 요소이고, 여기서 모듈을 만들면 된다.

.pod { border: 1px ... }
.pod > h3 { ... }
.pod > ul { ... }
  1. 모듈을 쓸 때마다 클래스에 pod를 써줘야 하지만, 이렇게 하면 적용 깊이를 줄일 수 있는 이점이 있다. 그러면 동적 콘텐츠를 위한 템플릿을 자유자재로 쓸 수 있다.

     <div class="pod">
         <h3></h3>
         <ul>
             \{\{#items}}
             <li></li>
         </ul>
     </div>
    
  2. 더 유연한 구조가 필요치 않다면, h3ul에 클래스를 매길 필요는 없다. 그러나 더 유연하려면 반드시 클래스를 매겨야 한다. 가령 ul 대신에 ol, div가 들어가는 경우:

     .pod > ul, .pod > ol, .pod > div { ... }
    

    이렇게 스타일을 매길 수도 있지만, 클래스를 붙이면 다음과 같이 축약할 수 있다.

     .pod-body { ... }
    

선택자의 성능

Steve Souders의 연구에 따르면 CSS 선택자의 성능은 기껏해야 0.05초 밖에 나지 않는다. 그러므로 너무 신경쓸 필요는 없지만 알아두긴 하자.

선택자는 오른쪽부터 계산된다. .module h3의 경우 브라우저는 페이지 내에 있는 모든 h3를 먼저 찾아낸다. 그 다음 .module에 속한 것을 찾는다. 그러므로 오른쪽에 요소 선택자를 두면 성능이 떨어진다. 그래서 구글에서는 모든 요소에 클래스를 붙여 쓰지만, 저자는 실용적으로 접근하자고 한다.

HTML5와 SMACSS

HTML5이든 HTML4이든 SMACSS가 효과적인 이유

  1. HTML 페이지에 말뜻(semantic value)을 더해준다.
  2. HTML 구조에 덜 의존한다.

HTML5에는 좀더 말뜻이 분명한 요소가 추가됐다.(e.g. section, header, aside)

그러나 태그가 모듈을 대체할 수는 없다. 다음 nav가 같은 nav일까?

<nav class="nav-primary">
    <h1>Primary Nav</h1>
    <ul>...</ul>
</nav>

<nav class="nav-secondary">
    <h1>External Links</h1>
    <ul>...</ul>
</nav>

nav.nav-primary는 페이지 상단에 있는 수평 네비게이션이겠지만, nav.secondary는 아마 사이드바에 들어갈 네비게이션 같다.

nav.nav-primary   li { display: inline-block; }
nav.nav-secondary li { display: block; }

근데 클라이언트가 와서 수평 메뉴에 수직 메뉴를 추가해달라고 한다.

<nav class="nav-primary".
    <h1>Primary Nav</h1>
    <ul>
        <li>About Us
            <ul>
                <li>Team</li>
                <li>Location</li>
            </ul>
        </li>
    </ul>
</nav>

그럼 하위 메뉴에 있는 li가 수평으로 나열되지 않게 하려면?

  1. ul<nav class="nav-secondary">요소로 감싼다.
  2. 다음과 같이 CSS를 고친다.

     nav.nav-primary   li { ... }
     nav.nav-secondary li,
     nav.nav-primary   li li { display: block; }
    
  3. nav 선택자를 버리고 클래스에만 의존한다.

     <nav class="l-inline">
         <h1>Primary Nav</h1>
         <ul>
             <li>About Us
                 <ul class="l-stacked">
                     <li>Team</li>
                     <li>Location</li>
                 </ul>
             </li>
         </ul>
     </nav>
     .l-inline  li { display: inline-block; }
     .l-stacked li { display: block; } 
    

이게 SMACSS 스타일. 한 술 더떠서 이 레이아웃 규칙을 더 일반화할 수 있다(선택자의 성능은 희생되겠지만):

    .l-inline > *  { display: inline-block; }
    .l-stacked > * { display: block; }