CSS 변수 - 중요한 이유

CSS 변수(더 정확하게는 CSS 사용자설정 속성)가 Chrome 49에 도입됩니다. CSS의 반복을 줄이는 데 유용하며 테마 전환, 향후 CSS 기능의 확장/폴리필과 같은 강력한 런타임 효과에도 유용합니다.

CSS 혼잡

애플리케이션을 설계할 때는 앱의 모양을 일관되게 유지하기 위해 재사용할 브랜드 색상 세트를 따로 설정하는 것이 일반적입니다. 안타깝게도 CSS에서 이러한 색상 값을 반복해서 사용하면 번거롭고 오류가 발생하기 쉽습니다. 어느 시점에서 색상 중 하나를 변경해야 하는 경우 주의 깊게 살펴보지 않고 모든 항목을 '찾기 및 바꾸기'할 수 있지만, 규모가 큰 프로젝트에서는 위험할 수 있습니다.

최근 많은 개발자가 전처리기 변수를 사용하여 이 문제를 해결하는 SASS 또는 LESS와 같은 CSS 전처리기를 사용하고 있습니다. 이러한 도구는 개발자 생산성을 크게 향상시켰지만 사용하는 변수에는 정적이며 런타임에 변경할 수 없다는 큰 단점이 있습니다. 런타임에 변수를 변경하는 기능을 추가하면 동적 애플리케이션 테마 설정과 같은 작업을 할 수 있을 뿐만 아니라 반응형 디자인과 향후 CSS 기능을 폴리필할 수 있는 잠재력도 갖게 됩니다. Chrome 49 출시와 함께 이러한 기능을 CSS 사용자 지정 속성의 형태로 사용할 수 있습니다.

맞춤 속성 개요

맞춤 속성은 CSS 도구 상자에 두 가지 새로운 기능을 추가합니다.

  • 작성자가 작성자가 선택한 이름으로 속성에 임의의 값을 할당할 수 있는 기능입니다.
  • var() 함수: 작성자가 다른 속성에서 이러한 값을 사용할 수 있습니다.

다음은 이를 보여주는 간단한 예시입니다.

:root {
    --main-color: #06c;
}

#foo h1 {
    color: var(--main-color);
}

--main-color는 값이 #06c인 작성자가 정의한 맞춤 속성입니다. 모든 맞춤 속성은 대시 두 개로 시작합니다.

var() 함수는 자체를 검색하여 맞춤 속성 값으로 바꾸므로 color: #06c;가 됩니다. 맞춤 속성이 스타일시트의 어딘가에 정의되어 있는 한 var 함수에서 사용할 수 있습니다.

처음에는 구문이 다소 이상하게 보일 수 있습니다. 많은 개발자가 '변수 이름에 $foo을 사용하면 안 되나요?'라고 묻습니다. 이 접근 방식은 최대한 유연하고 향후 $foo 매크로를 허용할 수 있도록 선택되었습니다. 배경에 관한 자세한 내용은 사양 작성자 중 한 명인 탭 애킨스의 이 게시물을 참고하세요.

맞춤 속성 문법

맞춤 속성의 구문은 간단합니다.

--header-color: #06c;

맞춤 속성은 대소문자를 구분하므로 --header-color--Header-Color는 서로 다른 맞춤 속성입니다. 단순해 보이지만 맞춤 속성에 허용되는 문법은 실제로 매우 관대합니다. 예를 들어 다음은 유효한 맞춤 속성입니다.

--foo: if(x > 5) this.width = 10;

이는 변수로서는 유용하지 않지만 일반 속성에서는 유효하지 않으므로 런타임 시 JavaScript로 읽고 이에 따라 작업을 실행할 수 있습니다. 즉, 맞춤 속성을 사용하면 현재 CSS 프리프로세서로는 불가능한 모든 종류의 흥미로운 기법을 사용할 수 있습니다. '하품 SASS가 있으니 누가 신경이나 써…'라고 생각한다면 다시 한번 살펴보세요. 익숙하게 사용하던 변수가 아닙니다.

폭포

맞춤 속성은 표준 캐스케이드 규칙을 따르므로 다양한 수준의 구체성으로 동일한 속성을 정의할 수 있습니다.

:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
<p>I inherited blue from the root element!</p>
<div>I got green set directly on me!</div>
<div id="alert">
    While I got red set directly on me!
    <p>I’m red too, because of inheritance!</p>
</div>

즉, 미디어 쿼리 내에서 맞춤 속성을 활용하여 반응형 디자인을 지원할 수 있습니다. 한 가지 사용 사례는 화면 크기가 커질수록 주요 섹션 요소 주변의 여백을 늘리는 것입니다.

:root {
    --gutter: 4px;
}

section {
    margin: var(--gutter);
}

@media (min-width: 600px) {
    :root {
    --gutter: 16px;
    }
}

미디어 쿼리 내에서 변수를 정의할 수 없는 오늘날의 CSS 전처리기를 사용하면 위의 코드 스니펫을 사용할 수 없다는 점에 유의해야 합니다. 이 기능을 사용하면 많은 잠재력을 발휘할 수 있습니다.

다른 맞춤 속성에서 값을 가져오는 맞춤 속성을 보유할 수도 있습니다. 테마 설정에 매우 유용합니다.

:root {
    --primary-color: red;
    --logo-text: var(--primary-color);
}

var() 함수

맞춤 속성의 값을 검색하고 사용하려면 var() 함수를 사용해야 합니다. var() 함수의 구문은 다음과 같습니다.

var(<custom-property-name> [, <declaration-value> ]? )

여기서 <custom-property-name>--foo와 같은 작성자 정의 맞춤 속성의 이름이고 <declaration-value>는 참조된 맞춤 속성이 잘못된 경우 사용할 대체 값입니다. 대체 값은 쉼표로 구분된 목록일 수 있으며, 이 목록은 단일 값으로 결합됩니다. 예를 들어 var(--font-stack, "Roboto", "Helvetica");"Roboto", "Helvetica"의 대체를 정의합니다. 마진 및 패딩에 사용되는 것과 같은 축약된 값은 쉼표로 구분되지 않으므로 패딩에 적절한 대체 값은 다음과 같습니다.

p {
    padding: var(--pad, 10px 15px 20px);
}

이러한 대체 값을 사용하여 구성요소 작성자는 요소에 대한 방어 스타일을 작성할 수 있습니다.

/* In the component’s style: */
.component .header {
    color: var(--header-color, blue);
}
.component .text {
    color: var(--text-color, black);
}

/* In the larger application’s style: */
.component {
    --text-color: #080;
    /* header-color isn’t set,
        and so remains blue,
        the fallback value */
}

이 기법은 맞춤 속성이 그림자 경계를 통과할 수 있으므로 Shadow DOM을 사용하는 Web Components의 테마 설정에 특히 유용합니다. 웹 구성요소 작성자는 대체 값을 사용하여 초기 디자인을 만들고 테마 설정 '후크'를 맞춤 속성 형식으로 노출할 수 있습니다.

<!-- In the web component's definition: -->
<x-foo>
    #shadow
    <style>
        p {
        background-color: var(--text-background, blue);
        }
    </style>
    <p>
        This text has a yellow background because the document styled me! Otherwise it
        would be blue.
    </p>
</x-foo>
/* In the larger application's style: */
x-foo {
    --text-background: yellow;
}

var()를 사용할 때는 몇 가지 유의사항에 주의해야 합니다. 변수는 속성 이름이 될 수 없습니다. 예를 들면 다음과 같습니다.

.foo {
    --side: margin-top;
    var(--side): 20px;
}

하지만 margin-top: 20px;를 설정하는 것과는 다릅니다. 대신 두 번째 선언은 유효하지 않으며 오류로 발생합니다.

마찬가지로 값의 일부가 변수에 의해 제공되는 값을 단순히 빌드할 수는 없습니다.

.foo {
    --gap: 20;
    margin-top: var(--gap)px;
}

다시 한번 말씀드리지만, 이는 margin-top: 20px;를 설정하는 것과는 다릅니다. 값을 빌드하려면 calc() 함수라는 다른 항목이 필요합니다.

calc()를 사용하여 값 빌드

이전에 사용해 본 적이 없다면 calc() 함수는 계산을 실행하여 CSS 값을 결정할 수 있는 편리한 도구입니다. 모든 최신 브라우저에서 지원되며 맞춤 속성과 결합하여 새 값을 만들 수 있습니다. 예를 들면 다음과 같습니다.

.foo {
    --gap: 20;
    margin-top: calc(var(--gap) * 1px); /* niiiiice */
}

JavaScript에서 맞춤 속성으로 작업

런타임에 맞춤 속성의 값을 가져오려면 계산된 CSSStyleDeclaration 객체의 getPropertyValue() 메서드를 사용합니다.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>I’m a red paragraph!</p>
/* JS */
var styles = getComputedStyle(document.documentElement);
var value = String(styles.getPropertyValue('--primary-color')).trim();
// value = 'red'

마찬가지로 런타임에 맞춤 속성의 값을 설정하려면 CSSStyleDeclaration 객체의 setProperty() 메서드를 사용합니다.

/* CSS */
:root {
    --primary-color: red;
}

p {
    color: var(--primary-color);
}
<!-- HTML -->
<p>Now I’m a green paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'green');

setProperty() 호출에서 var() 함수를 사용하여 런타임에 다른 커스텀 속성을 참조하도록 커스텀 속성의 값을 설정할 수도 있습니다.

/* CSS */
:root {
    --primary-color: red;
    --secondary-color: blue;
}
<!-- HTML -->
<p>Sweet! I’m a blue paragraph!</p>
/* JS */
document.documentElement.style.setProperty('--primary-color', 'var(--secondary-color)');

맞춤 속성은 스타일시트의 다른 맞춤 속성을 참조할 수 있으므로 이를 통해 다양한 흥미로운 런타임 효과를 얻을 수 있습니다.

브라우저 지원

현재 Chrome 49, Firefox 42, Safari 9.1, iOS Safari 9.3에서는 맞춤 속성을 지원합니다.

데모

맞춤 속성 덕분에 이제 활용할 수 있는 흥미로운 기법을 모두 살펴보려면 샘플을 사용해 보세요.

추가 자료

맞춤 속성에 대해 자세히 알아보려면 Google 애널리틱스팀의 필립 월튼이 작성한 맞춤 속성에 기대하는 이유에 관한 기본 정보를 참고하세요. 다른 브라우저의 진행 상황은 chromestatus.com에서 확인할 수 있습니다.