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
위와 같은 패턴으로 작성하는 것으로,
focused
와 disabled
와 같이 상태를 나타내는 것은 보통 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
Blockheader__search-from
Elementsearch-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
)
- References - BEM Quick-start