웹을 떠받치는 기술은 모두 비슷한 사정이지만, 특히나 CSS 작성 결과물은 개개인의 코딩 습관과 숙련도 그리고 유동적인 브라우저 지원 상황에다 반갑지 않은 버그 등 여러 외적 요인이 모여서 큰 변수로 작용하기 마련이다. 표면적으론 단순해 보이지만, 자유의지에 맡겨진 properties 지정 작업은 픽셀 하나하나 차곡차곡 쌓아 올린 작용과 반작용의 만남이다. 이것은 잔잔한 브라우저 창에 파도의 물결을 일으키는 상상력의 실현이 될 수도, 혹은 의도치 않은 부작용 때문에 엉키고 뒤틀려버린 혼란의 골칫거리가 되버릴 수도 있다. 그래서 항상 집중과 주의가 요구되는 고난의도 작업.

이런 복잡 미묘한 상황에 조금이라도 외적 잡음을 줄이고 동시에 CSS 작성의 효율을 높이고자 OOCSS, SMACSS, BEM과 같은 여러 구조적 방법론들이 계속 등장하고 있으며, 또한 공동 작업의 편의를 위한 CSS 코딩 스타일 통합을 목적으로 properties 지정 순서에서부터 공백의 개수, 따옴표 스타일 등 아주 세세하고 민감한 부분까지 일관되게 정리해주는 CSS 빗질 도구까지 마련되어 있다.

여기서 한발 더 나아가 SASS, Less와 같은 일명 Pre-Processor의 힘을 빌려 CSS 작성 효율을 구조적으로 좀 더 끌어올리려는 노력도 활발한데, 최근엔 한술 더 떠서 CSS 후처리 과정까지 더해준 Post-Processor들도 덩달아 비 온 뒤 잡풀처럼 여기저기 생겨나고 있어서 이들의 태생 목적과 사용법을 소개하고자 한다.

CSS Pre-Processors vs. Post-Processors

CSS Pre-Processor는 기존 CSS 문법의 확장 성격으로 자기만의 syntax로 작성하고 parse/compile 과정을 거쳐 일반 CSS로 되돌려준다. 이에 반해 Post-Processor는 그냥 맨살의 CSS 문법으로 작성된 것을 해석/처리해서 다시 일반 CSS를 돌려주는 차이가 있다.

정의만 살펴보면, Post-Processor를 써서 얻을 수 있는 장점을 이해하기 힘든데, Post-Processor가 제공하고 있는 기능 중 가장 많이 애용되는 Autoprefixer의 사용 예를 보면 Post-Processor만의 장점이 뚜렷해진다.

CSS3 규칙 중 브라우저 지원 때문에 prefix를 일일이 붙여줘야 하는 상황이라면, Sass(SCSS)의 경우 mixin 기능으로 다음과 같이 적용해 줄 수 있다.

@mixin flexbox() {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}
 
.box {
  @include flexbox();
}

하지만 Autoprefixer의 힘을 빌리면 CSS 작성은 아주 단순해진다.

.box { display: flex; }

원래의 CSS 문법을 그대로 작성하면, 후처리 과정을 거쳐 골치 아픈 vender prefix를 더해 다음과 같이 돌려준다.

.box {
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}

특정 CSS property의 브라우저 지원 상황을 일일이 확인해 가면서 mixin을 써야 할지 말아야 할지 고민할 필요가 없는 것이다.
Post-Processor(PostCSS) 소개 슬라이드: PostCSS: the Future after Sass and Less

Post-Processor가 제공하는 후처리 기능

앞서 설명한 Autoprefixer와 같은 Post-Processor는 대표적으로 PostCSS 혹은 rework의 plugin 형태로 많이 개발되고 있는데, 이런 plugin들을 모아 놓은 일종의 plugins pack으로서 지금은 cssnext, Pleease, Stylecow가 대표적으로 눈에 띈다.

여기선 PostCSS 기반 cssnext가 제공하는 기능을 살펴보고자 한다. (어차피 어느 Post-Processor를 고르더라도, 지원하는 기능의 차이만 있을 뿐, CSS 표준 문법으로 작성한 코드를 해석해서 돌려주는 산출 코드는 거의 같으므로 실제 CSS 코드 작성 방식에는 영향을 주지 않는다. 이것도 Post-Processor만의 사용 장점 중 하나.)

Use tomorrow’s CSS syntax, today.

– cssnext

cssnext가 내건 슬로건으로, 브라우저 지원 따윈 신경 쓰지 말고 차기 CSS 스펙을 그대로 가져와 지금 당장 써보자는 문구를 표방하고 있다. JavaScript의 Babel compiler와 비슷한 격으로 CSS3/4 spec을 위한 polyfill들을 모아놓은 셈이다.

먼저, 설치는 npm module로 터미널에서 다음 한 줄을 입력한다.

$ npm install -g cssnext

CSS 작성 후 기본 compile은 다음과 같다.

$ cssnext input.css output.css

편의를 위해 CSS 파일 내용이 바뀔 때마다 자동 compile 되는 watch 기능도 제동한다.

$ cssnext --watch input.css output.css

더 자세한 설치와 사용 방법은 cssnext 설치/설정 페이지에서 확인할 수 있다.

앞서 설명한 vendor prefixes 자동 추가 기능 이외 나머지 기능도 살펴보자.

Custom Propertie & Var()

W3C의 CSS Custom Properties for Cascading Variables Module을 지원하는데, 후처리 과정의 한계로 :root selector에 지정된 custom properties만 사용할 수 있다.

사용 예, input.css :

:root {
  --color: red;
}
 
div {
  color: var(--color);
}

output.css :

div {
  color: red;
}

CSS Variables (Custom Properties)

Permits the declaration and usage of cascading variables in stylesheets.

W3C Candidate Recommendation

Supported from the following versions:

Desktop

  • 49
  • 31
  • No
  • 36
  • 9.1

Mobile / Tablet

  • 9.3
  • 122
  • 80
  • 122
  • 123

* denotes prefix required.

  • Supported:
  • Yes
  • No
  • Partially
  • Polyfill

Stats from caniuse.com

Reduced calc()

CSS3 calc() function의 expression 안에서 CSS variables를 사용할 수 있게 해준다. 다만, 서로 다른 수치의 단위가 expression에서 사용되었다면 브라우저가 알아서 해석하도록 그대로 놔둠.

input.css :

:root {
  --main-font-size: 16px;
}
 
body {
  font-size: var(--main-font-size);
}
 
h1 {
  font-size: calc(var(--main-font-size) * 2);
  height: calc(100px - 2em);
}

output.css :

body {
  font-size: 16px;
}
 
h1 {
  font-size: 32px;
  height: calc(100px - 2em);
}

calc() as CSS unit value

Method of allowing calculated values for length units, i.e. `width: calc(100% - 3em)`

W3C Candidate Recommendation

Supported from the following versions:

Desktop

  • 19*
  • 4*
  • 9
  • 15
  • 6*

Mobile / Tablet

  • 6.0*
  • 4.4
  • 80
  • 122
  • 123

* denotes prefix required.

  • Supported:
  • Yes
  • No
  • Partially
  • Polyfill

Stats from caniuse.com

Custom Media Queries

종종 같은 media query를 여러 번 반복 지정해서 사용할 경우 나중에 수정할 일이 생기면 관리적 측면에서 성가신 작업이 될 수 있다. 그래서 W3C CSS Custom Media Queries가 제정되었는데, 이것은 일종의 길고 복잡한 media query 대신 짧고 알아보기 쉬운 이름을 한 곳에 지정해 놓고 쉽게 여러 곳에서 사용할 수 있도록 해준다.

input.css :

@custom-media --small-viewport (max-width: 30em);
 
@media (--small-viewport) {
  /* styles for small viewport */
}

output.css :

@media (max-width: 30em) {
  /* styles for small viewport */
}

CSS3 Media Queries

Method of applying styles based on media information. Includes things like page and device dimensions

W3C Recommendation

Supported from the following versions:

Desktop

  • 4
  • 3.5
  • 5.5
  • 9.5
  • 3.1

Mobile / Tablet

  • 3.2
  • 2.1
  • 10
  • 122
  • 123

* denotes prefix required.

  • Supported:
  • Yes
  • No
  • Partially
  • Polyfill

Stats from caniuse.com

Media Queries Ranges

Media Queries Ranges는 media query 괄호 안 media feature type 중 어떤 범위나 수치를 지정 테스트할 때 쓰는 min-/max- 접두사 대신 읽고 쓰기 편한 <= 그리고 >= 기호를 써서 표현할 수 있게 해준다.

input.css :

@media screen and (width >= 500px) and (width <= 1200px) {
  .bar {
    display: block;
  }
}
 
@media screen and (500px <= width <= 1200px) {
  .foo {
    display: block;
  }
}

output.css :

@media screen and (min-width: 500px) and (max-width: 1200px) {
  .bar {
    display: block;
  }
}
 
@media screen and (min-width: 500px) and (max-width: 1200px) {
  .foo {
    display: block;
  }
}

Custom Selectors

Custom Selectors는 Selector들의 조합을 가지고 이를 대표하는 자기만의 이름을 지정해서 사용할 수 있게 해준다. 일일이 selector를 치는 타이핑의 수고를 절약할 수 있는 방법이다.

input.css :

@custom-selector :--heading h1h2h3h4h5h6;
 
@custom-selector :--any-link :link:visited;
 
article :--heading + p {
  margin-top: 0;
}
 
a:--any-link {
  color: blue;
}

output.css :

article h1 + p,
article h2 + p,
article h3 + p,
article h4 + p,
article h5 + p,
article h6 + p {
  margin-top: 0;
}
 
a:link,
a:visited {
  color: blue;
}

color()

color() function은 색상 지정을 좀 더 미세하게 조정할 필요가 있을 때 사용하는데, 기본 바탕 색에다 여러 color adjusters를 써서 표현하고 싶은 색을 조작할 수 있다. 산출물로 호환성을 지닌 rgba() 값을 돌려준다.

input.css :

body {
  color: color(red a(10%));
  background-color: color(red lightness(50%));
  border-color: color(hsla(12550%50%.4) saturation(+10%) w(-20%));
}

output.css :

body {
  color: #FF0000;
  color: rgba(255, 0, 0, 0.1);
  background-color: rgb(255, 0, 0);
  border-color: #00CC11;
  border-color: rgba(0, 204, 17, 0.4);
}

hwb()

CSS hwb() function은 hue color에다 white와 black의 조합을 가미해서 기존 hsl()보다 색상 조절 작업이 더 수월한 장점이 있다. 산출물로 호환성을 지닌 rgba() 값을 돌려준다.

input.css :

body {
  color: hwb(900%0%0.5);
}

output.css :

body {
  color: rgba(128, 255, 0, 0.5);
}

gray()

gray() function으로 색 빠진 회색 조절을 좀 더 섬세하게.

input.css :

.foo {
  color: gray(0);
}
.bar {
  color: gray(25550%);
}

output.css :

.foo {
  color: rgb(0, 0, 0);
}
.bar {
  color: rgba(255, 255, 255, 0.5);
}

rrggbbaa

alpha 값이 추가된 W3C RGBA hexadecimal notations으로 #RRGGBBAA 혹은 #RGBA 형식을 갖는다.

input.css :

body {
  background: #9d9c;
}

output.css :

body {
  background: rgba(153, 221, 153, 0.8);
}

rebeccapurple

CSS language에 많은 공헌을 한 Eric Meyer씨의 딸이 너무 일찍 세상을 떠나면서, 추념의 의미로 그녀가 좋아했던 자줏빛 색(#663399)에 이름을 넣어 CSS Color Level 4에 추가한 사연이 들어 있다.

color: rebeccapurplecolor: rgb(102, 51, 153)로 변환된다.

font-variant

W3C CSS font variant properties에 브라우저 지원 상황이 좀 더 양호한 font-feature-settings fallback을 추가.

input.css :

h2 {
  font-variant-caps: small-caps;
}
 
table {
  font-variant-numeric: lining-nums;
}

output.css :

h2 {
  font-feature-settings: "c2sc";
  font-variant-caps: small-caps;
}
 
table {
  font-feature-settings: "lnum";
  font-variant-numeric: lining-nums;
}

CSS font-feature-settings

Method of applying advanced typographic and language-specific font features to supported OpenType fonts.

W3C Recommendation

Supported from the following versions:

Desktop

  • 16*
  • 4*
  • 10
  • 15*
  • 4

Mobile / Tablet

  • 3.2
  • 4.4*
  • 80
  • 122
  • 123

* denotes prefix required.

  • Supported:
  • Yes
  • No
  • Partially
  • Polyfill

Stats from caniuse.com

filter

CSS filter property에 지정한 filter fuction에 대응하는 inline <svg> filter fallback을 추가.

input.css :

.blur {
  filter: blur(4px);
}

output.css :

.blur {
  filter: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"><filter id="filter"><feGaussianBlur stdDeviation="4" /></filter></svg>#filter');
  filter: blur(4px);
}

CSS Filter Effects

Method of applying filter effects using the `filter` property to elements, matching filters available in SVG. Filter functions include blur, brightness, contrast, drop-shadow, grayscale, hue-rotate, invert, opacity, sepia and saturate.

W3C Working Draft

Supported from the following versions:

Desktop

  • 18*
  • 3.6
  • No
  • 15*
  • 6*

Mobile / Tablet

  • 6.0*
  • 4.4*
  • 80
  • 122
  • 123

* denotes prefix required.

  • Supported:
  • Yes
  • No
  • Partially
  • Polyfill

Stats from caniuse.com

rem units

rem units을 지원하지 않는 브라우저를 위해 px fallback 추가. (root font-size의 기본값은 16px이며, html 혹은 :root에 지정된 font-size에 따라 그 기준 기본값이 바뀐다.)

input.css :

h1 {
  font-size: 2rem;
}

output.css :

h1 {
  font-size: 32px;
  font-size: 2rem;
}

rem (root em) units

Type of unit similar to `em`, but relative only to the root element, not any parent element. Thus compounding does not occur as it does with `em` units.

W3C Candidate Recommendation

Supported from the following versions:

Desktop

  • 4
  • 3.6
  • 9
  • 11.
  • 5

Mobile / Tablet

  • 4.0
  • 2.1
  • 12
  • 122
  • 123

* denotes prefix required.

  • Supported:
  • Yes
  • No
  • Partially
  • Polyfill

Stats from caniuse.com

pseudo-elements

pseudo-elements의 두 개짜리 콜론(::)을 한 개짜리(:)로 치환. (pseudo-classes를 위한 한 개짜리 콜론 용법과 구별하려고 콜론 두 개가 사용되는데, IE9 미만 버전에선 두 개짜리 CSS3 용법을 지원하지 않는다.)

input.css :

p::first-line {
  text-transform: uppercase;
}

output.css :

p:first-line {
  text-transform: uppercase;
}

:any-link pseudo-class

:any-link pseudo class 지원. 지금 쓰이는 :link는 이름에서 약간의 혼동 소지가 있는 게, 모든 하이퍼링크를 대표하는 게 아니라 그 중 방문 기록이 없는(unvisited) 링크만을 상징한다. 그래서, :link:visited를 모두 아우르는 :any-link가 제안되었다.

input.css :

nav :any-link {
  background-color: yellow;
}

output.css :

nav :link,
nav :visited {
  background-color: yellow;
}

:matches pseudo-class

:matches pseudo-class는 selector 리스트를 argument로 받아 해당의 각 selector를 모두 아우르는 대표로서 여러 selector를 한꺼번에 간편하게 지정할 때 사용되는데, 글보단 사용 예를 보면 이해가 쉽다.

input.css :

p:matches(:first-child.special) {
  color: red;
}

output.css :

p:first-childp.special {
  color: red;
}

:not pseudo-class

:not pseudo-class는 앞서 설명한 :matches pseudo-class의 반대 성격을 갖고 있으나, 사용법은 비슷하다.

input.css :

p:not(:first-child.special) {
  color: blue;
}

output.css :

p:not(:first-child)p:not(.special) {
  color: blue;
}

rgba() to hexadecimal color

CSS 3 Colors에 정의된 rgba color 값을 인식하지 못하는 브라우저를 위해 hexadecimal color fallback 추가.

input.css :

body {
  background: rgba(153, 221, 153, 0.8);
}

output.css :

body {
  background: #99DD99;
  background: rgba(153, 221, 153, 0.8);
}

CSS3 Colors

Method of describing colors using Hue, Saturation and Lightness (hsl()) rather than just RGB, as well as allowing alpha-transparency with rgba() and hsla().

W3C Recommendation

Supported from the following versions:

Desktop

  • 4
  • 2
  • 9
  • 9.5
  • 3.1

Mobile / Tablet

  • 3.2
  • 2.1
  • 10
  • 122
  • 123

* denotes prefix required.

  • Supported:
  • Yes
  • No
  • Partially
  • Polyfill

Stats from caniuse.com

@import

관리를 쉽게 하려고 따로 빼어 놔두었던 @import로 지정된 스타일을 통째로 불러들여 통합해 주는 기능.

input.css :

@import "layout.css";
 
@import "desktop.css" (min-width: 30em);
 
body {
  background: black;
}

output.css :

/* layout.css 내용 */
 
@media (min-width: 30em) {
  /* desktop.css 내용 */
}
 
body {
  background: black;
}

마무리

지금까지 소개한 차세대 CSS 기술들을, 이중 혹 일부만 쓰더라도, 브라우저 지원의 고민 없이 마음껏 써볼 수 있다는 것은 참 매력적이다. 결국, 이렇게 해서 얻어진 작업의 효율은 힘을 보태는 추진력이 되어 더 멋지고 단단한 결과물을 끌어낼 수 있기 때문이다.

전부터 이미 많은 개발자의 CSS 작성 관리 도구로 자리를 잡은 CSS Pre-Processor 그리고 여기서 살펴본 Post-Processor의 기능은 서로 겹치는 부분이 있지만, 활용에 따라 자기 영역에서 더 빛을 발하는 구석이 분명해서 서로 보완적인 관계라고 생각한다. 그래서 둘 만의 장점을 잘 골라서 활용한다면, 효율적이고 깔끔한 CSS 코드를 작성하는 데 좋은 시너지 효과를 불러올 수 있겠다.

관련된 주제의 글

“Post-Processor를 이용한 깔끔하고 미래 지향적인 CSS 작성”에 달린 2개의 댓글

댓글을 남겨 주세요