커스텀 속성과 모던 CSS


브랜드 블루를 하나 정하고, 마흔 개의 규칙에 그 색을 일일이 입력하고, 사이트를 배포합니다. 한 달 뒤 마케팅팀에서 블루 톤을 조금 바꿔달라고 합니다. 이제 모든 스타일시트를 뒤지면서 마흔 곳을 전부 찾았는지, 대문자로 적어놓은 두 곳을 빠뜨리지 않았는지 노심초사해야 합니다. 커스텀 속성은 이 문제를 해결해줍니다. 색상 값에 이름을 한 번 붙이고, 그 이름을 어디서나 쓰고, 필요할 때 한 곳만 바꾸면 됩니다. 이 작은 습관 하나가 테마 적용, 디자인 토큰, 그리고 리디자인에도 살아남는 스타일시트의 출발점이 됩니다.
변수 선언하고 사용하기
**커스텀 속성(custom property)**은 재사용할 수 있도록 이름을 붙인 값입니다. 이름 앞에 대시(-) 두 개를 붙여서 값을 지정하고, 나중에 var()로 그 값을 다시 읽어옵니다.
:root {
--color-brand: #2563eb; /* 값을 이름 아래 한 번만 선언 */
}
.button {
background: var(--color-brand); /* 여기서 읽어옴 */
}
.link {
color: var(--color-brand); /* 여기서도, 같은 값 */
}--color-brand가 이름이고(대시 두 개는 필수입니다), var(--color-brand)가 그 값을 사용하는 방법입니다. :root(페이지 최상단에 있는 <html> 요소)에 선언하면 이 이름을 어디서나 쓸 수 있게 됩니다. :root에 있는 한 줄만 바꾸면 이 값을 읽는 모든 규칙이 함께 바뀝니다.
--color-brand처럼 대시 두 개로 적고, var(--color-brand)로 읽어옵니다. :root에 두면 페이지 전체에서 쓸 수 있습니다. 한 줄만 바꾸면 그 이름을 읽는 모든 규칙이 함께 바뀝니다. 캐스케이드와 상속
다른 언어에서 변수를 써본 적이 있다면 다음 부분이 뜻밖일 수 있습니다. 커스텀 속성은 고정된 값이 아닙니다. 다른 CSS 속성과 똑같은 규칙을 따르기 때문에 자식 요소로 **상속**되며, 나머지는 그대로 두고 한 곳에서만 값을 다르게 줄 수 있습니다.
:root {
--color-text: #1f2937; /* 페이지 전체의 기본값 */
}
.callout {
--color-text: #92400e; /* .callout 안에서만 다른 값 */
color: var(--color-text);
}
p {
color: var(--color-text); /* 보통은 짙은 회색, callout 안에서는 호박색 */
}같은 var(--color-text)인데 대부분의 문단에서는 짙은 회색이고 callout 안에서는 호박색이 되는 것은, callout이 자기만의 값을 설정했기 때문입니다. 이것이 테마 적용을 가능하게 해주는 원리이며, 그 뒤에 있는 캐스케이드는 CSS의 동작 원리에서 이미 살펴봤습니다.
var(--color-text)가 대부분의 문단에서는 회색이고 callout 안에서는 호박색이 될 수 있습니다. 컨테이너에서 그 이름을 다시 정의하면 그 안의 모든 것이 새 값을 따라갑니다. 폴백과 calc()
var()는 두 번째 값을 받을 수 있는데, 이는 커스텀 속성이 설정되지 않았을 때만 사용됩니다. 이것이 **폴백(fallback)**이고, 토큰이 없을 때도 규칙이 계속 작동하게 해줍니다.
.card {
gap: var(--gap, 1rem); /* --gap이 있으면 사용, 없으면 1rem */
}calc()를 이용하면 커스텀 속성으로 계산도 할 수 있습니다. 기본 여백 값이 하나 있다면, 두 번째 숫자를 하드코딩하는 대신 그 값을 바탕으로 더 큰 값을 만들 수 있습니다.
:root {
--space-md: 16px;
}
.section {
padding: calc(var(--space-md) * 2); /* 32px, 기본값에서 파생 */
}이렇게 값을 파생시키면 --space-md 하나를 바꿀 때 그 위에 만들어진 모든 값이 함께 바뀝니다.
var(--gap, 1rem)은 토큰이 있으면 그것을 쓰고 없으면 1rem으로 대체하므로, 값이 빠졌을 때도 규칙이 계속 작동합니다. 그리고 calc()를 이용하면 calc(var(--space-md) * 2)처럼 토큰에서 값을 만들어낼 수 있고, 기본 여백의 두 배를 얻을 수 있습니다. 기본값을 바꾸면 그 위에 세워진 모든 값이 함께 따라갑니다. 테마 적용
커스텀 속성은 트리 아래쪽에서 다시 정의할 수 있으므로, 클래스 아래에서 토큰만 바꾸는 방식으로 다크 테마를 만들 수 있고, 컴포넌트 규칙은 하나도 건드리지 않아도 됩니다.
:root {
--color-bg: #ffffff;
--color-text: #1f2937;
}
.theme-dark {
--color-bg: #0f172a; /* 이름은 같고, 값만 새로움 */
--color-text: #e2e8f0;
}
body {
background: var(--color-bg);
color: var(--color-text);
}페이지에 class="theme-dark"를 추가하면 --color-bg나 --color-text를 읽는 모든 규칙이 다크 값으로 바뀝니다. 테마를 전환하기 위해 body 규칙이나 그 어떤 컴포넌트도 건드리지 않았습니다. 이것이 헥스 코드를 직접 쓰는 대신 색상에 토큰으로 이름을 붙이는 것에 대한 보상입니다.
.theme-dark 같은 클래스 아래에서 다른 값을 쓰는 것뿐입니다. class="theme-dark"를 설정하면 그 토큰을 읽는 모든 규칙이 컴포넌트를 하나도 건드리지 않고 전환됩니다. 그래서 각 규칙에 헥스 코드를 직접 타이핑하는 대신 색상에 토큰으로 이름을 붙이는 것입니다. 
