1. BEM 기본 구조
B : Block
E : Element
M : Modifier
각각은 __ 와 -- 으로 구분한다.
.header__navigation--navi-text {
color: red;
}
CSS
복사
→ header : Block
→ navigation : Element
→ navi-text : Modifier
BEM은 기본적으로 ID를 사용하지 않으며, class만 사용한다.
어떤 목적인가로 이름을 짓는다.
이름을 연결할 때는 하이픈 하나만으로 연결한다.
The BEM approach ensures that everyone involved in website development works with a single codebase and speaks the same language. By using BEM's proper naming conventions, you can better prepare your website for design changes.
BEM 접근 방식은 웹사이트 개발에 참여하는 모든 사람이 단일 코드베이스로 작업하고 동일한 언어를 사용하도록 합니다. BEM의 적절한 명명 규칙을 사용하면 웹사이트의 디자인 변경 사항에 더 잘 대비할 수 있습니다.
2. Block / Element / Modifier
Block
독립적이고 분리할 수 있음
일종의 컨테이너. 같은 기능을 위해서 필요한 것들을 모아두고 레이아웃 배치의 역할.
재사용가능한 기능적으로 독립적인 페이지 컴포넌트
A functionally independent page component that can be reused
종속된 것이 아니라 어딘가에 사용될 수 있다.
중첩 가능 : Block은 Block을 감쌀 수 있다.
Element
Block을 구성하는 단위
Element는 의존적인 형태이므로 자신이 속한 Block 내에서만 의미를 가지기 때문에 떼어내서 어딘가에 사용할 수 없다.
<form class="search-form">
<input class="search-form__input"/>
<button class="search-form__button">Search</button>
</form>
HTML
복사
중첩 가능 : Element는 Element을 감쌀 수 있다.
.block > .block__element1 > .block__element2
여기서 block_element2를 block_element2의 하위 Element로 보지 않고 똑같이 block의 Element로 취급한다.
className에 cascading을 여러번 표시할 필요가 없다. (?)
캐스캐이딩?
스타일 적용 범위가 좁을수록 우선순위가 높아짐
// 틀린 예
<form class="search-form">
<div class="search-form__content">
<input class="search-form__content__input"/>
<button class="search-form__content__button">Search</button>
</div>
</form>
HTML
복사
<form class="search-form">
<div class="search-form__content">
<input class="search-form__input"/>
<button class="search-form__button">Search</button>
</div>
</form>
HTML
복사
Modifier
Block이나 Element의 속성을 담당한다.
조금 다르게 생겼거나 다르게 동작하는 Block, Element를 만들 때 쓴다.
<ul class="tab">
<li class="tab__item tab__item--focused">탭 01</li>
<li class="tab__item">탭 02</li>
<li class="tab__item">탭 03</li>
</ul>
HTML
복사
—focused는 불리언(boolean) 타입으로 ture라고 가정해서 사용한다.
<div class="column">
<strong class="title">일반 로그인</strong>
<form class="form-login form-login--theme-normal">
<input type="text" class="form-login__id"/>
<input type="password" class="form-login__password"/>
</form>
</div>
<div class="column">
<strong class="title title--color-gray">VIP 로그인 (준비중)</strong>
<form class="form-login form-login--theme-special form-login--disabled">
<input type="text" class="form-login__id"/>
<input type="password" class="form-login__password"/>
</form>
</div>
HTML
복사
--theme-normal는 key-value 타입으로 성질-내용으로 작성한다.
--color-gray는 key-value 타입으로 성질-내용으로 작성한다.
데모가 있음 bem-style-mdn
<div class="header">
<div class="header__inner">
<div class="tabzilla"></div>
<div class="header__logo"></div>
<div class="header__auth"></div>
<div class="nav"></div>
<div class="header__search"></div>
</div>
</div>
HTML
복사
→ .header__ 로 시작하는 태그는 모두 Element다.
→ tabzilla와 nav는 다른 곳에도 독립적으로 쓰일 수 있어서 Block이다.
<div class="header__logo">
<div class="logo">
<a href="#" class="logo__link">
<h1 class="blind">MDN</h1>
</a>
</div>
</div>
HTML
복사
→ .header__logo는 헤더 내에서 로고 위치를 잡는 데 쓰고,
→ .logo는 로고 이미지를 지정, 이 로고를 footer에서도 써야 한다면 .footer__logo> .logo 로 마크업하고 .footer__logo 에 css로 위치만 잡는다.
Sign in with 부분 작성하기
<div class="header__auth">
<div class="auth">
<div class="auth-means"></div>
<div class="auth-picker"></div>
</div>
</div>
HTML
복사
.header__auth부분입니다. 이 부분도 auth라는 블럭으로 떼어내겠습니다.
한편, 마우스를 오버하면 아래 리스트가 나오기 때문에 auth를 크게 위/아래로 나눠주었습니다. 이때, __를 써서 요소로 나눈 게 아니라 -를 이용해 블럭으로 나누셨더라구요!
<div class="auth-means">
<p class="auth-means__text">Sign in with
<ul class="auth-means__list">
<li class="auth-means__item auth-menas__item--persona">
<a href="#">
<i class="fa fa-user"></i>
<span class="blind">Persona</span>
</a>
</li>
<li class="auth-means__item auth-means__item--github">
<a href="#">
<i class="fa fa-github"></i>
<span class="blind">Github</span>
</a>
</li>
</ul>
</div>
HTML
복사
-persona와 -github 따로따로 스타일을 지정
<div class="auth-picker">
<ul class="auth-picker__list">
<li class="auth-picker__item">
<a href="#">
<i class="fa fa-user"></i>
<span>Persona</span>
</a>
</li>
<li class="auth-picker__item">
<i class="fa fa-github"></i>
<span>Github</span>
</li>
</ul>
</div>
HTML
복사
.auth-picker__item은 아이콘만 다르고 서로 동일하기 때문에 굳이 모디파이어를 지정해주지 않음
Search 부분 작성하기
이 검색 영역은 별도로 분리될 수 있다고 판단해 .search라는 블럭 아래에서 작성합니다. 참고로 이 input은 focus 시 확장되어야 해서, --extend라는 모디파이어를 별도로 만든 다음 클릭했을 때 추가되는 스크립트도 필요합니다.
<div class="header__search">
<div class="search">
<form>
<div class="search__inner">
<div class="search__title">
<label for="main-search">
<i class="fa fa-search"></i>
<span class="blind">Search</span>
</label>
</div>
<input type="text" id="main-search" class="search__input"/>
<!--focus시 .search__input--extend 클래스를 추가합니다-->
<input type="submit" class="search__button" value="Search"/>
</div>
</form>
</div>
</div>
HTML
복사
Navigation 부분 작성하기
<div class="nav">
<ul class="nav__list">
<li class="nav__item">
<a href="#" class="nav__link">
<span>Web Platfrom</span>
<i class="fa fa-caret-down"></i>
</a>
<div class="nav__submenu nav__submenu--col2"></div>
<div class="nav__submenu"></div>
<div class="nav__submenu"></div>
</li>
</ul>
</div>
HTML
복사
서브메뉴가 2줄이라서 --col2라는 모디파이어를 추가
<div class="nav__submenu">
<div class="nav-submenu">
<div class="nav-submenu__column"></div>
<div class="nav-submenu__column"></div>
<button class="nav-submenu__close">
<span class="blind">Close submenu</span>
<i class="fa fa-times-circle"></i>
</button>
</div>
</div>
HTML
복사
<div class="nav-submenu__column">
<strong class="nav-submenu__title">Technologies</strong>
<ul class="nav-submenu__list">
<li class="nav-submenu__item">
<a href="#" class="nav-submenu__link">HTML</a>
</li>
</ul>
</div>
HTML
복사
.nav__submenu 는 nav 아래의 엘리먼트이기 때문에 네비 안에서의 위치를 잡아주는 용도로 씁니다.
한편, .nav-submenu 는 블럭이기 때문에 스타일링(배경색 지정, 폰트색 지정 등)을 하는 용도로 사용하면 되겠습니다.
// ================
// _common.scss
// ================
* {margin: 0;
padding: 0;
list-style: none;
text-decoration: none;
box-sizing: border-box;}
.blind {display: none;}
body {background-color: #fafafa;}
// ================
// _header.scss
// ================
.header {width: 100%;
background-color: #eaeff2;
&__inner {position: relative;
width: 100%;
max-width: 1200px;
min-width: 800px;
height: 120px;
margin: 0 auto;
padding: 0 20px;}
&__logo {margin-bottom: 30px;}
&__auth {position: absolute;
top: 20px;
right: 180px;
&--opened {
.auth-means {border-radius: 8px 8px 0 0;}
.auth-picker {display: block;}
}
}
&__search {position: absolute;
top: 60px;
right: 0;
.search__input {width: 60px;
&[class$=--extend] {width: 160px;}
}
}
}
Scss
복사
원본
4. 정리
좋았던 점
1. 클래스네임만으로 마크업 구조를 알 수 있습니다.블럭과 엘리먼트로 구분되기 때문에 어디서부터 떼어다 쓸 수 있는지, 어디부터 종속되는지 알 수 있습니다.
2. SASS의 부모참조자(&)와 찰떡궁합!쓰기가 무척 편해요!!
3. 작성된 SASS에서 요소를 쉽게 찾을 수 있습니다.예를 들어, .header 아래에 &__logo, &__search로 작성하기 때문에 "아, 저게 헤더 아래 로고고 저건 헤더 아래 검색이구나"란 걸 바로 알 수가 있어요.
4. SASS 작성 시, 늘어지는 셀렉팅을 막아줍니다.기존에 nested 방식으로 SASS를 작성하면, 컴파일 시 셀렉팅이 끝도 없이 길어지는 경우가 있었거든요 (.header .nav .list .item .link )처럼요.그런데 BEM 방식을 쓰면, 너도나도 엘리먼트라서 굳이 깊게 nested할 필요가 없어집니다.
/*기존에 쓰던 방식*/
.header {
.nav {
position: absolute;
.list {
list-style: none;
}
.item {
/*너무 길어진다 싶을 때만 밖으로(근본없는 들여쓰기!)*/
outline: 0;
.link {
color: red;
}
}
}
}
/*BEM*/
.header {
&__nav {
position: absolute;
}
&__list {
clor: red;
}
&__item {
/*여기 있는 모든 요소들은 블럭 내의 엘리먼트이기 때문에 캐스케이딩과 무관하게 병렬 작성*/
outline: 0;
}
&__link {
color: red;
}
}
Scss
복사
아쉬웠던 점
1. 클래스네임이 너무 길어요ㅠ.ㅠ마크업이 한눈에 들어오지 않는다는 단점이 있습니다.특히 스크립트로 모디파이어를 변경해야할 때, classList.add("block-name__element-name--modifier")처럼 길게 작성해야하는 건 불편해보입니다.
2. 더블클릭 선택이 불편해요.하이픈과 언더바가 혼재되어 있어, 더블클릭해서 클래스네임을 선택할때 한 번에 선택이 안 되는 문제가 있어요.
위의 이미지를 보시면
노란 박스는 Block,
녹색 박스는 __Element,
빨간박스와 보라색 박스는 --Modifier 를 나타 내는데 보라색 박스의 .popup_button--yes 의 클래스명을 갖고 잇는 버튼의 스타일은 파란 배경 의 스타일을 갖고 있고, 빨간 박스의 .popup_button--no의 클래스명을 갖고 있는 버튼에다가는 회색 보더의 스타일 있습니다.
bem 스타일을 학습하지 않은 내가 코딩을 했다면 파랑색 버튼의 클래스명을 .btn_blue 이런식으로 작명을 했을 것입니다.
하지만 .btn_blue 의 버튼의 색이 빨강으로 바뀔 수가 있습니다. 그렇게 때문에 스타일을 기준으로 하는 작명법은 좋지 않다고 이야기 하고 있습니다.
출처:
[Work and Study Diary]