BEM은 유지보수 및 확장이 용이하고 가독성이 좋은 스타일링을 하는 방법론이다. html의 class명을 사용하므로 시작이 간단하지만, 정확한 사용방법을 알고 사용해야 오류를 방지할 수 있다.
📔 방법
bem에 따라 작성하는 클래스명은 block__element--modifier의 구조를 따라야 한다.
1) block(컴포넌트)
block은 레이아웃 상에서 컴포넌트를 가리킨다. 이들은 이 자체로 독립적인 기능과 역할을 가지고 여러 곳에서 사용된다. 예컨대, header, navbar, footer이 있고, 특정한 form 등이 해당된다.
2) element(종속적 요소)
element는 컴포넌트에 종속된 요소다. 예컨대 위 예시에서 header 내부의 title이나 버튼은 오로지 header에서만 사용된다. block과 element의 차이는 바로 독립적으로 사용가능한가이다.
자주 헷갈리는 점1
block 내부에 위치한 요소라도 독립적으로 사용가능하다면 block으로 취급한다. 예컨대, 위 예시의 form에서 input이 독립적으로 다른 곳에서도 사용가능하다면 컴포넌트로 취급한다.
3) modifier(특별한 상태)
modifier은 block이나 element가 특별한 상태에 있음을 의미한다. 예컨대, 특정한 탭이 토글된 상태는 modifier이 적용된다.
위 예시에서 email | phone 탭은 email이 토글된 상태다.
4) 규칙1. block은 단일한 클래스명으로 작성되고 element는 이중 언더바로 연결한다.
<div class="tab">
<p class="tab__email">email</p>
<p class="tab__phone">phone</p>
</div>
<form class="login">
<input class="login__input" />
<button class="login__submit">submit</button>
</form>
위 예시에서는 tab 컴포넌트와 login 컴포넌트(block)가 있다. 두 컴포넌트의 클래스명은 특별한 방법 없이 작성한다. 반면, 컴포넌트 내에 있는 종속적인 요소(element)는 block__element의 형태로 이중 언더바를 사용해서 작성한다.
block은 재사용을 위해 margin 또는 padding을 적용하지 않는다
5) 규칙2. BEM은 깊이를 표현하지 않는다.
일반적으로 컴포넌트의 내부는 요소들이 중첩되는 계층관계를 가지고 있다. 그럴 때 bem은 어떻게 표현할까?
bem은 block__element의 형식으로만 작성되어야 하는 반면, block__element__element나 element__element의 형식으로 작성되어선 안된다.
<!-- 옳은 예시 -->
<ul class="link">
<li class="link__button">
<img class="link__icon" />
<p class="link__title">홈</p>
</li>
</ul>
<!-- 잘못된 예시 -->
<ul class="link">
<li class="link__button">
<img class="link__button__icon" />
<p class="link__button__title">홈</p>
</li>
</ul>
그러면 아주 깊은 중첩관계를 가지는 요소들은 어떻게 해야 할까?
위 방식처럼 하다보면, link__title이나 link__info같은 흔한 이름들은 겹칠 수 밖에 없다.
5) 규칙3. 단일 하이픈과 언더바는 BEM과 별도로 제약없이 사용가능하다.
아래 예시에서는 자주 사용되는 클래스명 간에 중복을 피하기 위해 하이픈으로 수식언을 꾸며주었다.
예컨대, profile__img-main과 profile__img-sub로 구분하였다.
<div class="profile">
<div class="profile__box-img">
<img class="profile__img-main" />
<img class="profile__img-sub" />
</div>
<div class="profile__info">
<h3 class="profile__info-title"></h3>
<p class="profile__info-description"></p>
</div>
<ul class="profile__reviews">
<li class="profile__review">
<h3 class="profile__review-title"></h3>
<p class="profile__review-description"></p>
</li>
</ul>
</div>
잠깐, 하이픈이나 언더바는 종속관계와 특별한 상태를 의미한다고 안했나?
아니다. 이중 하이픈과 이중 언더바다. 단일한 하이픈과 언더바는 케이스 스타일처럼 단어와 단어를 연결할 때 사용가능하다. 단, 프로그래밍에서 변수명을 선언할 때와 마찬가지로, 너무 길고 복잡해서는 안된다. 그렇지 않으면, 가독성이 아주 나빠진다.
그래서 가능하면 컴포넌트 내부는 재사용 가능한 하위 컴포넌트로 구성하는게 좋다.
<div class="profile">
<div class="profile__box-img">
<img class="profile__img-main" />
<img class="profile__img-sub" />
</div>
<!-- info와 reviews는 독립적인 컴포넌트로 분리했다. 이러면 클래스명이 더 간단해진다. -->
<div class="info">
<h3 class="info__title"></h3>
<p class="info__description"></p>
</div>
<ul class="reviews">
<li class="reviews__li">
<h3 class="reviews__title"></h3>
<p class="reviews__description"></p>
</li>
</ul>
</div>
5) 규칙3. modifier은 이중 하이픈으로 연결하며, 독립적으로 사용해서는 안된다.
아래 예시에서 tab__email은 토글로 선택된 상태다. 이럴 때는 기존 클래스명에 토글됨을 명시한 클래스명을 추가로 작성한다. 토글된 클래스명(modifier)만을 독립적으로 사용해서는 안된다. 이 클래스명은 특수한 상태에서만 사용되기 때문이다.
<!-- 옳은 예시 -->
<div class="tab">
<p class="tab__email tab__email--toggled">email</p>
<p class="tab__phone">phone</p>
</div>
<!-- 틀린 예시 -->
<div class="tab">
<p class="tab__email--toggled">email</p>
<p class="tab__phone">phone</p>
</div>
이는 자바스크립트로 특정한 클래스명을 빼거나 추가하는 방식으로 스타일을 변경할 때 유용하다.
만약 한 부모 아래의 모든 자식을 modifier 대상으로 삼을 때는, 부모에 modifier를 추가하고 복합 선택자로 자식을 선택하자. 명시도는 높아지지만 클래스명은 간결해진다.
<div class="tab tab--selected">
<p class="tab__email">email</p>
<p class="tab__phone">phone</p>
</div>
<!-- .tab--selected p[class *="tab"] {//styling//}; -->
modifier의 형태
bollean 타입
--focused처럼 이 단순히 존재/미존재로 구분 가능한 타입
key-value 타입
info--color-gray, info--color-red 처럼 속성과 값을 명시하는 타입
6) 규칙4. BEM은 구조나 태그명이 아니라 기능과 목적을 명시한다.
클래스명은 대상의 목적과 기능을 알려주는 역할이다. 따라서, div나 section,p ,element같은 이름보다는
tab, title, profile, hashtag 같은 이름을 지어주자.
🔥 장점
- 낮은 명시도(10~20)를 유지하므로 !important 같은 문법을 피할 수 있다.
- 클래스명만으로도 컴포넌트와 종속관계를 파악가능하다.
- 클래스명의 중복을 피하기 쉬워서 유지 보수 및 확장이 용이하다.
'CSS' 카테고리의 다른 글
[CSS] SVG 레이아웃 잡기 (0) | 2024.02.28 |
---|---|
[CSS] backdrop-filter:blur() 사용 시 주의점 (0) | 2024.02.27 |
[CSS] CSS trick: 배경 그라디언트에 애니메이션 넣기 (0) | 2024.02.27 |
word-break, overflow-wrap(word-wrap) 정리 - 텍스트 오버플로우 관리법1 (6) | 2023.01.27 |
[css]선택자(selector) 우선순위, 명시도(specificty) (0) | 2022.12.27 |