SMACSS & OOCSS & BEM
OOCSS(Object Oriented CSS)
OOCSS의 2가지 원칙
oocss는 객체 지향에 따라 고안된 설계 방식이다.
- 구조와 외형의 분리
- 컨테이너와 내용 분리
구조와 외형의 분리
기본적인 구조와 반복 정의되는 외형은 따로 정의해둔다. (공통 스타일 추상화)
- 구조: width, height, border, padding, margin
- 외형: color, border-color, background-color
OOCSS Style
기본 구조가 독립적으로 지정되어 있기 때문에 향후 다른 색의 버튼이 추가되더라도 외형 스타일만 추가로 정의한다.
예를 들어 설명해보자.
1 | <a href="#" class="btnbase cart">장바구니</a> |
1 | /* 버튼 구조: 공통적인 구조 지정 */ |
이러한 방법은 객체 지향형 CSS로, 공통적인 버튼의 구조를 만들어두고 외향은 따로 정의해주는 방법이다.
그렇다면 의미론적 CSS를 살펴보자.
1 | <a href="#" class="cartbtn">장바구니</a> |
1 | .cartbtn{ |
클래스 이름을 구체적으로 설정했고, 구조와 외형을 분리하지 않고 css를 작성했다. 의미론적 css는 중복되는 코드가 많아진다. 또, 재사용이 어려워 어떤 요소를 추가로 삽입할때 처음부터 모든 스타일링을 다시 입혀줘야 한다.
컨테이너와 내용의 분리
컨테이너와 내용은 분리되어야 한다. 이 둘을 분리하려면 다음을 만족해야 한다.
- 위치에 의존하지 않는 스타일 정의
- 어떤 태그라도 동일한 외형 제공
- 어디에서나 재사용이 가능한 클래스 기반 모듈 구축
무슨 말인지 예를 들어 자세히 설명해보도록 하자.
위치에 의존하지 않는 스타일 정의란 무엇일까?
먼저, 위치에 의존하는 스타일을 예를 들어 살펴보자.
(안좋은 예)장소를 한정한 스타일 지정
1 | .header .logo { |
header의 logo, footer의 logo를 직접 스타일링해주는 방법이다.
만약 로고를 다른 영역에서 사용하게 된다면 그때 또 다시 클래스를 지정해서 배경이미지와 크기를 설정해줘야 한다. 중복되는 코드들이 남발하고 있다.
그렇다면 위치에 의존하지 않은 스타일이란?
(좋은 예)장소를 한정하지 않은 스타일 정의
1 | .logo-large { |
header, footer라는 장소를 한정해주지 않아 클래스 이름만 조립해주면 어디서든 로고 이미지를 이용할 수 있다.
그렇다면, 어떤 태그라도 동일한 외형을 제공하려면 어떻게 해야할까?
(안좋은 예) 태그에 스타일 지정
1 | <h2>부제목입니다.</h2> |
1 | h2 {font-size: 1.6rem;} |
태그에 직접 스타일링을 해주게 되면 또 코드가 중복되는 단점이 생긴다.
만약 span태그를 사용했는데 h2와 같은 스타일링을 해주길 원하면 또 span태그를 지정하고 font-size:1.6rem
을 입혀줘야 한다.
(좋은 예) 클래스명을 부여하고 스타일 지정
1 | <h3 class="subtitle"> 부제목입니다. </h3> |
1 | .subtitle { font-size:16rem; } |
태그 이름을 직접 선택하지 않고 클래스 명에 스타일링을 입히면 원하는 부분에 .subtitle만 삽입해주면 간단히 꾸며줄 수 있다.
OOCSS 클래스 이름 짓기
- 되도록 짧게
- 스타일과 작동 방식이 한눈에 들어오도록
- 대부분의 사이트에서도 적용되도록
OOCSS의 장단점
장점: 앞에서도 계속 설명했듯이, 코드를 재사용하기 용이하다. 이로 인해 스타일시트의 용량축소로 속도가 향상된다.
단점: 원하는 스타일을 입히기 위해 여러 가지 클래스명을 가져다 써야하기 때문에 html에서 클래스 이름 부분이 길어질 수 있다.
또, 같은 맥락이지만 어떤 태그에 스타일링을 추가하고 싶으면 마크업에 클래스를 추가해야 한다.
BEM(Block Element Modifier)
- Block, Element, Modifier로 나누어 클래스 이름을 기술한다.
- class만 사용할 수 있다. (id선택자는 사용할 수 없다.)
Block
- 코드의 구조적 덩어리
- 재사용할 수 있는 컴포넌트
- 클래스명은 하나의 단어 사용, 길어질 경우 단일 하이픈(-)으로 구분
- ex) logo/ login-form/ navigation 등등
1 | <div class="stick-man"> ...내용...<div> |
1 | .stick-man {...} |
Element
- 블록 외부에서 사용할 수 없는 블록 내의 구성 요소
- 블록 맥락에서만 의미가 있어야 한다.
- 클래스 명은 해당 블록 이름에 밑줄 두개(__) 추가 후 작성
1 | <div class="stick-man"> |
1 | /* Good */ |
깊이 최소화
요소의 요소를 만들어야 한다면(block__element__elemet)
DOM 트리를 모방 하려고 하지 마세요
1 | <!-- Bad --> |
DOM 트리는 보통 container > header > navigation > ul > li
이런 방식으로 요소 안의 요소 안의 요소로 이루어져 있다.
BEM 방식은 DOM 트리 같은 방법을 지양한다. 그렇다면 어떻게 해야할까?
새로운 블록 만들기
1 | <!-- Good --> |
stick-man 안에 있는 arms를 하나의 블록으로 만들었다.
요소 안에 요소를 만드는 경우, 블록 안에 또다른 블록을 만들어 해결할 수 있다.
만약, DOM 트리 형식을 모방할 경우 발생하는 문제점에 대해 알아보자.
1 | <!-- Bad --> |
부연 설명을 하자면, 앞서 블록은 재사용 가능한 컴포넌트여야 한다고 설명했다.
그 개념에 따라 stick-man-arms를 iron-man 블록에서 재사용 하려고 하자 iron-man 블록 안에 stick-man-arms가 들어있는 이상한 구조가 완성되었다.
이 경우, 유지 보수 단계나 시간이 지난 후에 코드를 봤을 때 한눈에 파악할 수 없게 된다.
Modifier
- 블록, 요소에 대해 추가 변형을 제공한다. (외형, 상태)
- 클래스 명은 블록 또는 요소 이름 옆에 하이픈 두개(–)를 추가 후 작성한다.
Block Modifier
1 | <div class="stick-man stick-man--blue"> ... </div> |
1 | .stick-man--blue { ... } |
블록에서 수정을 한 경우
Element Modifier
1 | <div class="stick-man"> |
1 | .stick-man__head--small { ... } |
요소에서 수정을 한 경우
SMACSS (Scalable and Modular Architecture for CSS)
CSS를 모듈화하고 확장 가능하게 만드는 것을 목표
OOCSS, BEM의 핵심 컨셉을 차용하고 좀 더 자세한 사항 추가
클래스명을 통한 예측, 재사용, 쉬운 유지보수, 확장성을 통해 스타일 체계화
SMACSS의 핵심은 분류
5개의 구분된 카테고리로 CSS 코딩 기법 제시.
어떤 카테고리에 스타일이 속하는지 결정하는데 숙고 요구
- Base : 기본 규칙
- Layout : 레이아웃 규칙
- Module : 모듈 규칙
- State : 상태 규칙
- Theme : 테마 규칙
Base 기본 규칙
- 각 브라우저의 스타일을 초기화 시키는 목적으로 사용
- 요소 스타일의 기본값 지정(reset.css, normalize.css)
Layout 레이아웃 규칙
- 큰 틀의 레이아웃, 페이지의 다양한 요소를 배치, 구별하는데 사용
- 주요 컴포넌트 : header, footer, content, aside …etc, id 선택자로 스타일 작성
- 하위 컴포넌트 : 주요 컴포넌트 내에 있는 컴포넌트 (nav, item list, form … etc), class 선택자 사용
- 클래스명은 접두사로
l-
,layout-
명시 요구
ex)l-fixed
유무에 따라 가변 폭으로 할지 고정 폭으로 할지 결정하는 레이아웃
1 | #content { width: 80%; float: left;} |
Module 모듈 규칙
- 페이지에서 재사용 가능한 구성 요소 (버튼, 위젯, 배너 등)
- 모듈은 레이아웃 구성 요소 안에 존재하지만 다른 모듈에도 존재할 수 있음
- 각 모듈은 독립형으로 존재하도록 설계
- 재사용을 위해 css선택자로 id선택자와 태그선택자를 사용하지 않음
1 | <div class="box"> |
1 | .box { ... } |
State 상태 규칙
- 요소의 상태 변화를 표현하는 스타일 (툴팁, 아코디언)
- 주로 javascript로 조작되는 클래스 지정
- 클래스명은 접두사로
is-
등을 명시 (is-hidden, is-collapsed) - 모듈과 레이아웃 둘 다 적용 가능
1 | <!-- 레이아웃 요소, 접힌 상태 --> |
Theme 테마 규칙
- 사용자가 테마를 선택할 수 있는 경우 사용
- 색상, 이미지를 불변하는 스타일과 분리 (background, color, border…)
- 메인 스타일 뒤에 읽어 들이게 하고 덮어쓰기를 하거나 기존 스타일을 재선언하여 사용
theme-
등의 접두어를 명시 또는theme/
과 같은 디렉토리로 계층 분리- 자주 사용하지는 않음
1 | /* main.css */ |
1 | /* theme.css - main.css 뒤에서 읽도록 */ |
정리
이러한 접근법 중 완벽하게 이상적인 것은 없다. 각 방법론 중 유용한 방법을 가져와 적절히 섞어 사용할 수 있다.