CSS 방법론 - BEM

CSS 방법론은 스타일을 정의하는데 있어 엘리먼트의 class와 id를 어떻게 다루는지, 네이밍을 어떻게 하는지의 방법을 정의해 더 직관적인 스타일 코드를 작성하고 팀 단위의 개발을 원할하게 하는데 사용된다.
대표적으로는 BEM, OOCSS, SMASS가 있으며 여기서는 BEM에 대해 간단히 알아보겠다.

BEM의 공식 가이드 홈페이지는 https://en.bem.info로 영어와 러시아어가 지원된다.

BEM?

BEM은 Block, Element, Modifier 세개 단어의 약자이다.
크게 세가지를 통해서 네이밍을 하게 된다.

BEM은 기본적으로 id가 아닌 class만 사용하는 것을 지향하며, CSS tag도 쓰지 않는다.
세개(B,E,M)의 요소를 기준으로 클래스명을 작명하게 된다.

.error #test { ... } ❌ id 사용
.info h1 { ... } ❌ tag 사용

.info__text { ... } 👍

Block

Block을 기준으로 네이밍을 시작하게 되며 블록의 이름 뒤에 __로 연결하여 Element 이름을 작성하게 된다.

블록은 독립적으로 기능하는 단위이다.

<div class="big-text" /><div class="error" /> 👍

큰 텍스트(big-text)를 정의한 것은 텍스트라는 목적 또는 크다는 모양을 기술하였으므로 하나의 독립적 기능을 하는 단위라 볼 수 없다.
오류(error)라 정의한 것은 무엇을 하는가, 기능. 즉 오류라는 기능을 수행한다는 독립적인 단위가 될 수 있다.

블록에는 position, margin과 같이 외부적인 위치를 조정하는 스타일을 지정할 수 없다.

=> 독립성과 재사용성의 보장
무엇을 하는지 정의하고 외부적인 위치를 조정하는 position, margin과 같은 스타일을 넣지 않으므로 블록이 움직이거나 다른 곳에서 이 블록을 또 사용하더라도 독립성을 유지할 수 있게 되는 것이다.

블록은 중첩될 수 있으며, 여러 중첩을 거칠 수 있다.

<!-- header 블록 -->
<header class="header">
  <!-- 중첩된 logo 블록 -->
  <div class="logo" />
  <!-- 중첩된 serch-from 블록 -->
  <div class="search-form" />
</header>

Element

Element는 블록을 구성하는 단위로 블록과 분리될 수 없는 블록의 하위 요소이다.
따라서 블록 이름 없이 사용할 수 없으며,
블록 이름 뒤에 언더바 2개__로 연결하여 작성한다.

엘리먼트는 위에서 예를 든 big-text를 통해서 살펴보면,
big과 같은 모양을 나타내는 것은 옳지 않다.
text와 같은 목적을 나타내는 것은 사용할 수 있다.

<div class="search-form">
  <h1 class="search-from__big"></h1><span class="search-from__text"></span> 👍 
  <input class="search-from__input" /> 👍
</div>

엘리먼트 또한 블록과 같이 중첩될 수 있으며, 여러 중첩을 거칠 수 있다.

<form class="search-form">
  <div class="search-form__content">
    <input class="?" /> 👈
    <!--
			"search-form__content__input" ?
			"search-form__input" ?
    -->
  </div>
</form>

위에서 input 태그를 보면 계층이 블록>엘리먼트>엘리먼트이다. 그렇다고해서 Block search_form안의 Element content안의 input이라고 해서 search-form__content__input라고 네이밍을 하는 것은 옳지 않다.

엘리먼트의 이름은 이어서 계층구조를 가지게 작성되어서는 안된다.
따라서 search-form__input으로 작성되어야 맞다.

.block {
}
.block__elem1 {
}
.block__elem2 {
}
.block__elem3 {
}
<div class="block">
  <div class="block__elem1">
    <div class="block__elem2"></div>
    <div class="block__elem3"></div>
  </div>
</div>

<!-- Change -->
<div class="block">
  <div class="block__elem1">
    <div class="block__elem2"></div>
  </div>

  <div class="block__elem3"></div>
</div>

이러한 구조를 따르면 위의 코드를 통해 알 수 있듯 블록의 구조가 변경되더라도 해당 엘리먼트들의 규칙을 유지할 수 있게 된다.

여기까지 살펴보면 아래 코드를 보면 옳지 않다는 생각이 든다.

<div class="search-form">
  <input class="input" />
  <button class="button">Search</button>
</div>

하지만 input, button을 엘리먼트가 아닌 하나하나의 블록으로 살펴 볼 수 있기 때문에 틀린 코드라고 할 수 없다. 따라서 꼭 블록 안에 엘리먼트가 있어야 하는 것은 아니라고 할 수 있다.

Modifier

Modifier는 블록이나 엘리먼트의 모양이나 상태, 또는 행동을 정의합니다.
블록, 엘리먼트 뒤에 하나의 언더바_로 이어 기술합니다.

예를 들어 어떤 사이즈인지 size_m, 어떤 상태인지 disabled focused, 어떤 동작인지 directions_left-top를 적으면 된다.

Types of modifiers

Boolean

  • block-name_modifier-name
  • block-name__element-name_modifier-name 위와 같은 패턴으로 작성하는 것으로,

focuseddisabled와 같이 상태를 나타내는 것은 보통 boolean 값을 가질 것을 생각하는데 이럴때는 true라고 가정하고 작성하는 것을 원칙으로 한다.

<form class="search-form search-form_focused"></form>

search-form_focused_focused 부분이 true라고 가정, 포커스가 된 상태의 검색폼이라고 할 수 있다

Key-value

  • block-name_modifier-name_modifier-value
  • block-name__element-name_modifier-name_modifier-value

테마: 아일랜드 와 같이 키-벨류를 나타내는 듯한 이름을 말한다.

<form class="search-form search-form_theme_islands"></form>

theme_islands와 같이 키와 벨류를 하나의 언더바로 분리한 모양으로 작성하면 된다.

Mix

<div class="header">
  <div class="search-form header__search-form" />
</div>

클래스를 나눠보면 아래와 같다.

  • header Block
  • header__search-from Element
  • search-form Block

정리하면,

header Block에 search-from Block의 스타일과 동작을 Mix해 header 블록의 search-from이라는 header__search-from을 만들었다.

이는header__search-from를 이용해 header, search-from 각각의 Block의 독립성을 유지하면서 search-form 블록에 margin이나 position을 조정할 수 있게 된다.

File structure

BEM 방법론은 파일 구조에도 쓰일 수 있다. 프로젝트 구조를 작성할 때 폴더, 파일명에 규칙을 적용해 아래와 같이 작성할 수도 있다.

search-form/ #Block

    __input/ #Element
        search-form__input.css
        search-form__input.js

    __button/ #Element
        search-form__button.css
        search-form__button.js

    _theme/ #Modifier
        search-form_theme_islands.css
        search-form_theme_lite.css

    search-form.css
    search-form.js

파일 구조를 작성하기위한 규칙은 아래와 같다.

  • 하나의 Block은 하나의 디랙토리에 해당한다.
  • Block과 디렉토리의 이름은 동일해야한다.
  • Block의 구현은 별도의 파일로 나눈다. (header.js, header.css)
  • Block 디렉토리가 루트 디렉토리가 되며 아래에 해당 블록의 Element, Modifier 디렉토리가 있는다.
  • Element 디렉토리의 이름은 두개의 언더바 (__)로 시작한다 (header/__logo/, menu/__item/)
  • Modifier 디렉토리의 이름은 하나의 언더바 (_)로 시작한다.(header/_fixed/, menu/_theme_islands/)
  • Element와 Modifier의 구현은 별도의 파일로 나눈다. (header__input.js, header_theme_islands.css)