CSS 변수 선언하고 쓰기
CSS 변수, 공식 이름으로 사용자 정의 속성(custom property) 은 이름이 --로 시작하는 속성이에요. 보통 페이지 전체가 상속받도록 :root에 한 번 선언하고, 어디서든 var()로 읽어요.
--brand를 #16a34a로 바꾸면 카드 테두리와 버튼이 함께 바뀌어요. 이게 핵심이에요. 값 하나, 고칠 곳 하나예요. 버튼의 좌우 padding처럼 변수는 calc()와 함께 쓸 수도 있어요.
문법
:root {
--name: value; /* declare */
}
.element {
color: var(--name); /* use */
color: var(--name, #111827); /* use, with a fallback */
padding: calc(var(--space) * 2); /* compute with it */
}
| 규칙 | 설명 |
|---|---|
| 이름 | --로 시작하고 그 뒤에 글자, 숫자, -, _가 와요. 대소문자를 구분해요. --Brand는 --brand가 아니에요. |
| 값 | 거의 무엇이든 돼요. 색, 길이, 목록, 문자열. 쓰일 때까지 검사하지 않아요. |
| 선언 위치 | 아무 규칙에나. 전역 값은 :root, 지역 값은 컴포넌트의 선택자에 둬요. |
| 상속 | 사용자 정의 속성은 상속돼서 자식이 조상의 값을 봐요. |
var()를 쓸 수 있는 곳 | 속성 값에서만 돼요. 선택자, 속성 이름, @media 조건에서는 안 돼요. |
:root는 <html> 요소와 일치해요. html 대신 쓰는 이유는 명시도가 클래스와 같고, 관례상 "전역 설정"이라는 뜻을 전하기 때문이에요.
범위: 컴포넌트마다 변수 덮어쓰기
변수는 상속되기 때문에, 어떤 요소에서 다시 선언하면 그 요소와 그 안의 모든 것에 대해 값이 바뀌어요. 변수를 쓰는 규칙은 그대로예요.
변형들은 변수만 지정하고 테두리 자체는 건드리지 않아요. 네 번째 변형을 추가하는 데 한 줄이면 돼요.
기본값
var(--name, fallback)은 요소나 어떤 조상에도 변수가 정의되지 않았을 때 기본값을 써요. 첫 번째 쉼표 뒤는 전부 기본값이라서 쉼표를 포함할 수 있고(var(--font, Georgia, serif)), 또 다른 var()가 올 수도 있어요.
JavaScript로 변수 바꾸기: 테마 전환
JavaScript는 style.setProperty()로 어떤 요소에든 변수를 지정할 수 있어요. document.documentElement(<html> 요소)에 지정하면 페이지 전체가 따라와요. 더 흔한 방식은 변수를 다시 정의하는 클래스를 토글하는 거예요.
변수 값은 getComputedStyle(element).getPropertyValue('--text')로 다시 읽어요. setProperty는 <html>에 인라인 스타일을 적기 때문에 .dark 클래스를 이겨요. 그래서 두 번째 버튼을 누른 뒤에는 root.style.removeProperty('--text')로 인라인 값을 지울 때까지 두 테마 모두에서 글자 색이 그대로예요.
HTML에서 값 넘기기
style 속성에 적은 변수는 요소별 데이터를 스타일시트로 넘기는 가장 깔끔한 방법이에요. 디자인은 CSS에 남고, HTML은 숫자만 넘겨요.
calc(var(--value) * 1%)는 그냥 숫자를 퍼센트로 바꿔요. 변수는 미디어 쿼리 안에서 바뀔 수도 있어요. @media (min-width: 900px) { :root { --space: 20px; } }는 --space를 읽는 모든 간격을 넓은 화면에서 키워요.
잘못된 값 함정
브라우저는 변수가 그 속성에 맞는 값을 담고 있는지 미리 알 수 없어요. 맞지 않으면 그 속성은 규칙 안의 이전 선언으로 돌아가지 않아요. 계산값 시점에 유효하지 않은(invalid at computed-value time) 상태가 되어 unset으로 지정한 것처럼 동작해요. 상속되는 속성은 부모의 값을, 나머지는 초깃값을 가져요.
글자는 <body>에서 16px을 상속받고, border-width는 초깃값인 medium으로 돌아가요. 브라우저는 이를 3px로 그려요. 변수가 없으면 잘못된 선언은 버려지고 그 앞의 선언이 살아남지만, 변수를 쓰면 그러기엔 이미 늦어요. 변수가 정의되어 있는데 값이 틀렸다면 기본값도 도움이 안 돼요. 기본값은 정의되지 않은 변수에만 쓰여요.
CSS 변수와 Sass 변수 비교
| CSS 변수 | Sass 변수 | |
|---|---|---|
| 문법 | --brand: #2563eb;와 var(--brand) | $brand: #2563eb; |
| 해석 시점 | 브라우저에서, 실시간 | 빌드 시점, 결과물에는 남지 않음 |
| 요소마다 바꾸기 | 돼요, 상속으로 | 안 돼요 |
| 클래스나 미디어 쿼리로 바꾸기 | 돼요 | 안 돼요 |
| JavaScript로 바꾸기 | 돼요 | 안 돼요 |
| 선택자나 미디어 조건에서 쓰기 | 안 돼요 | 돼요 |
둘은 함께 쓸 수 있어요. 많은 프로젝트가 빌드 시점 로직에는 Sass를 쓰고, 테마로 바꿀 수 있는 것은 CSS 변수로 내보내요.
자주 하는 실수
- 하이픈 두 개 빠뜨리기.
var(brand)나-brand로 선언한--brand는 동작하지 않아요. - 미디어 쿼리에서 변수 쓰기.
@media (min-width: var(--bp))는 올바르지 않아요. 미디어 조건에는 리터럴 값이 필요해요. - 변수가 잘못되면 이전 선언이 살아남을 거라고 기대하기. 살아남지 않아요. 위의 함정을 참고하세요.
- 단위를 잘못 섞기.
--gap: 10;다음에margin: var(--gap)px라고 써도10px이 되지 않아요.calc(var(--gap) * 1px)를 쓰거나10px을 저장하세요. - 엉뚱한 요소에 선언하기.
.card에 선언한 변수는 형제나 부모에게 보이지 않아요. 공유하는 값은:root에 선언하세요.
자주 묻는 질문
CSS 변수는 어떻게 선언하나요?
규칙 안에 하이픈 두 개로 시작하는 이름의 속성을 적어요. 전역 값이라면 보통 :root에 둬요. :root { --brand: #2563eb; } 그리고 어디서든 var(--brand)로 읽어요. 예를 들어 color: var(--brand);처럼요.
CSS에서 :root는 무엇인가요?
:root는 문서의 루트 요소와 일치하는 가상 클래스이고, HTML 페이지에서는 <html>이에요. 여기에 선언한 변수는 모든 요소가 상속받아요. 명시도가 클래스와 같아서 그냥 html 선택자를 이겨요.
CSS 변수에 기본값은 어떻게 지정하나요?
var()에 두 번째 인수를 넘겨요. color: var(--accent, #2563eb); 요소나 조상에 --accent가 정의되지 않았을 때 이 기본값을 써요. 변수가 그 속성에 맞지 않는 값으로 정의되어 있을 때는 쓰지 않아요.
JavaScript로 CSS 변수는 어떻게 바꾸나요?
요소의 style에서 setProperty를 호출해요. document.documentElement.style.setProperty('--brand', '#16a34a') 그러면 var(--brand)를 쓰는 모든 규칙이 한 번에 바뀌어요. 현재 값은 getComputedStyle(el).getPropertyValue('--brand')로 읽어요.
CSS 변수와 Sass 변수는 무엇이 다른가요?
Sass 변수는 스타일시트를 컴파일할 때 값으로 바뀌어서 브라우저에서 바꿀 수 없어요. CSS 변수는 브라우저 안에 살아 있어요. 상속되고, 요소마다 다를 수 있고, 미디어 쿼리와 클래스에 반응하고, JavaScript로 바꿀 수 있어요.
CSS 변수 이름은 대소문자를 구분하나요?
네. --Brand와 --brand는 서로 다른 변수예요. 대소문자를 구분하지 않는 color 같은 일반 속성 이름과 달라요.