Menu

CSS 변수: 사용자 정의 속성, :root, var()

CSS 변수(사용자 정의 속성)는 값을 한 번 저장하고 var()로 재사용해요. :root에 선언하는 법, 범위와 덮어쓰기, 기본값 지정, JavaScript로 테마 바꾸기, 잘못된 값 함정을 피하는 법을 알아봐요.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

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 같은 일반 속성 이름과 달라요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기