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인 작성자 정의 맞춤 속성입니다. 모든 커스텀 속성은 대시 2개로 시작합니다.

var() 함수는 자체를 검색하여 맞춤 속성 값으로 대체합니다. 그 결과 color: #06c;가 됩니다. 따라서 맞춤 속성이 스타일시트의 어딘가에 정의되어 있으면 var 함수에서 사용할 수 있어야 합니다.

처음에는 구문이 약간 이상하게 보일 수 있습니다. 많은 개발자는 변수 이름에 $foo만 사용하면 안 되는 이유를 묻습니다. 이 접근 방식은 최대한 유연하고 향후 $foo 매크로를 허용할 수 있도록 특별히 선택되었습니다. 비하인드 스토리는 사양 작성자 중 한 명인 Tab Atkins의 이 게시물을 참고하세요.

맞춤 속성 구문

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

--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을 사용하는 웹 구성요소의 테마를 설정하는 데 특히 유용합니다. 웹 구성요소 작성자는 대체 값을 사용하여 초기 디자인을 만들고 맞춤 속성 형식으로 테마 설정 '후크'를 노출할 수 있습니다.

<!-- 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 */
}

자바스크립트에서 맞춤 속성 사용

런타임에 맞춤 속성의 값을 가져오려면 계산된 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에서 다른 브라우저의 진행 상황을 계속 확인할 수 있습니다.