CSS 계단식 변수용 사용자 정의 속성 모듈 레벨 1

W3C 후보 권고안 스냅샷,

이 문서에 대한 추가 정보
이 버전:
https://www.w3.org/TR/2022/CR-css-variables-1-20220616/
최신 공개 버전:
https://www.w3.org/TR/css-variables-1/
편집자 초안:
https://drafts.csswg.org/css-variables/
이전 버전:
히스토리:
https://www.w3.org/standards/history/css-variables-1
구현 보고서:
https://wpt.fyi/results/css/css-variables
테스트 스위트:
http://test.csswg.org/suites/css-variables-1_dev/nightly-unstable/
https://wpt.fyi/results/css/css-variables/
피드백:
CSSWG 이슈 저장소
편집자:
Tab Atkins Jr. (Google)
이 명세에 대한 편집 제안:
GitHub 편집기

요약

이 모듈은 모든 CSS 속성에서 사용할 수 있는 새로운 원시 값 타입으로서 계단식 변수를 도입하며, 이를 정의하기 위한 사용자 정의 속성을 제공합니다.

CSS는 구조화된 문서(예: HTML 및 XML)를 화면, 인쇄물 등에서 렌더링하는 방법을 기술하는 언어입니다.

이 문서의 상태

이 섹션은 본 문서가 게시된 시점의 상태를 설명합니다. 최신 W3C 발행물 목록과 이 기술 보고서의 최신 개정판은 W3C 기술 보고서 색인 https://www.w3.org/TR/에서 확인하실 수 있습니다.

이 문서는 CSS 작업 그룹에 의해 후보 권고안 스냅샷으로 권고 트랙을 사용하여 발행되었습니다. 후보 권고안으로 발행되었다고 해서 W3C 및 회원들의 승인을 의미하지는 않습니다. 후보 권고안 스냅샷은 광범위한 검토를 받았으며, 구현 경험을 수집하기 위해 마련된 것으로, 작업 그룹 멤버들이 구현을 위한 로열티 무료 라이선스 제공을 약속합니다. 본 문서는 W3C 권고안이 되는 것을 목표로 하며, 추가 피드백을 수집하기 위해 까지 후보 권고안 상태를 유지합니다.

피드백은 GitHub 이슈를 등록(권장)하여 보내주십시오. 이때 명세 코드 “css-variables”를 제목에 포함시켜 “[css-variables] …의견 요약…”와 같이 작성해 주세요. 모든 이슈와 의견은 아카이브에 보관됩니다. 또는 (아카이브됨) 공개 메일링 리스트 www-style@w3.org로 피드백을 보낼 수도 있습니다.

이 문서는 2021년 11월 2일 W3C 프로세스 문서의 적용을 받습니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 작성되었습니다. W3C는 그룹 산출물과 관련하여 공개 특허 공개 목록을 유지합니다; 해당 페이지에는 특허 공개 방법도 안내되어 있습니다. 개인이 특정 특허가 필수 청구항을 포함한다고 판단할 경우, W3C 특허 정책 6항에 따라 정보를 공개해야 합니다.

1. 소개

이 섹션은 규범적이지 않습니다.

대형 문서나 애플리케이션 (그리고 작은 것들도) 상당한 양의 CSS를 포함할 수 있습니다. CSS 파일 내의 많은 값들은 중복 데이터일 수 있습니다; 예를 들어, 사이트가 색상 테마를 정하고 세 가지 또는 네 가지 색상을 사이트 전체에서 재사용할 수 있습니다. 이러한 데이터를 변경하는 것은 어렵고 오류가 발생하기 쉽습니다, 왜냐하면 CSS 파일 전체(그리고 여러 파일에 걸쳐) 흩어져 있기 때문이며, 찾기 및 바꾸기로 해결하기 어려울 수 있습니다.

이 모듈은 사용자 정의 속성이라는 일련의 사용자 정의 속성군을 도입합니다. 저자는 임의의 값을 직접 지정한 이름의 속성에 할당할 수 있으며, var() 함수로 이러한 값을 문서 내 다른 속성에 사용할 수 있습니다. 이를 통해 대형 파일을 읽기 쉽고, 임의의 값이 이제는 설명적인 이름을 가지게 되며, 이러한 파일을 편집하는 과정도 훨씬 쉽고 오류가 적어집니다. 값을 한 번만 변경하면, 사용자 정의 속성에서, 해당 변수의 모든 사용처에 변경 사항이 자동으로 반영됩니다.

1.1. 값 정의

이 명세는 [CSS2]CSS 속성 정의 관례를 따르며, 값 정의 문법[CSS-VALUES-3]을 사용합니다. 이 명세에서 정의되지 않은 값 타입은 CSS 값 및 단위 [CSS-VALUES-3]에서 정의됩니다. 다른 CSS 모듈과의 조합으로 이러한 값 타입의 정의가 확장될 수 있습니다.

각 속성 정의에 명시된 속성별 값 외에도, 이 명세에서 정의된 모든 속성은 CSS 전역 키워드도 속성 값으로 허용합니다. 가독성을 위해 명시적으로 반복하지 않았습니다.

2. 사용자 정의 속성 정의: --* 속성군

이 명세는 사용자 정의 속성이라 불리는 제한 없는 속성 집합을 정의합니다. 이 속성들은 var() 함수대체 값을 정의하는 데 사용됩니다.

이름: --*
값: <declaration-value>?
초기값: 보장된 잘못된 값
적용 대상: 모든 요소와 모든 의사 요소(제한된 속성 목록을 가진 경우도 포함)
상속:
백분율: 해당 없음
계산된 값: 변수가 대체된 지정한 값, 또는 보장된 잘못된 값
정식 순서: 문법에 따름
애니메이션 유형: 불연속

사용자 에이전트는 모든 미디어(비시각적 미디어 포함)에서 이 속성을 지원해야 합니다.

사용자 정의 속성은 이름이 두 개의 대시(U+002D 하이픈 마이너스)로 시작하는 모든 속성입니다, 예를 들어 --foo와 같습니다. <custom-property-name> 생성규칙은 이것에 해당합니다: 두 대시로 시작하는 유효한 <dashed-ident> (유효한 식별자)이며, 단 -- 자체는 제외, 이것은 CSS에서 미래 사용을 위해 예약되어 있습니다. 사용자 정의 속성은 저자와 사용자만 사용할 수 있으며, CSS가 이 명세에서 제시하는 것 이상의 의미를 부여하지 않습니다.

테스트
사용자 정의 속성은 변수를 정의하며, var() 표기법으로 참조할 수 있습니다, 이는 다양한 용도로 사용할 수 있습니다. 예를 들어, 디자인에 소수의 색상만 일관적으로 사용하는 페이지는 색상을 사용자 정의 속성에 저장하고 변수로 사용할 수 있습니다:
:root {
  --main-color: #06c;
  --accent-color: #006;
}
/* CSS 파일의 나머지 부분 */
#foo h1 {
  color: var(--main-color);
}

이름 지정은 색상에 대한 기억 장치를 제공하며, 색상 코드에서 발생하기 쉬운 오타를 방지하고, 테마 색상이 변경될 경우 한 곳(사용자 정의 속성 값)에서만 수정하면 되므로 웹페이지의 모든 스타일시트에서 여러 번 수정할 필요가 없습니다.

다른 CSS 속성과 달리, 사용자 정의 속성 이름은 ASCII 대소문자 구분 없음이 아닙니다. 대신, 사용자 정의 속성 이름은 서로 동일한 경우에만 같습니다.

테스트
--foo--FOO는 모두 유효하지만, 서로 다른 속성입니다 —var(--foo)를 사용하면 첫 번째를 참조하고, var(--FOO)를 사용하면 두 번째를 참조합니다.

더 놀라운 예로, --foó--foó 또한 서로 다른 속성입니다. 첫 번째는 U+00F3(LATIN SMALL LETTER O WITH ACUTE)로 작성되어 있고, 두 번째는 ASCII "o" 다음에 U+0301(COMBINING ACUTE ACCENT)로 작성되어 있습니다. "서로 동일함" 관계는 두 문자열이 같은지 확인하기 위해 직접 코드포인트 비교를 사용합니다, 유니코드 정규화나 로케일별 비교의 복잡성과 문제를 피하기 위해서입니다.

운영체제, 키보드, 또는 입력 방법이 시각적으로 동일한 텍스트를 서로 다른 코드포인트 시퀀스로 인코딩하는 경우가 있습니다. 저자들은 혼란을 피하기 위해 변수 이름을 신중히 선택하거나, 이스케이프 및 기타 방법을 사용하여 비슷해 보이는 시퀀스가 실제로 동일함을 보장하는 것이 좋습니다. 예시는 [CHARMOD-NORM]의 2.3절을 참고하십시오.

다음 CSS를 유지보수하는 개발자는 테스트 패치가 빨간색인 이유를 혼란스러워할 수 있습니다:
--fijord: red;
--fijord: green;
--fijord: blue;

.test {
  background-color: var(--fijord);
}

첫 번째 사용자 정의 속성은 LATIN SMALL LETTER F + LATIN SMALL LETTER I + LATIN SMALL LETTER J의 시퀀스를 사용하고, 두 번째 속성은 LATIN SMALL LETTER F + LATIN SMALL LIGATURE IJ 시퀀스를 사용하며, 세 번째 속성은 LATIN SMALL LIGATURE FI + LATIN SMALL LETTER J 시퀀스를 사용합니다.

따라서 CSS에는 서로 다른 세 개의 사용자 정의 속성이 있으며, 그중 두 개는 사용되지 않습니다.

사용자 정의 속성은 all 속성에 의해 초기화되지 않습니다. 향후 모든 변수를 초기화하는 속성을 정의할 수 있습니다.

CSS 전역 키워드는 사용자 정의 속성에서도 사용할 수 있으며, 다른 속성에서와 동일한 의미를 가집니다.

테스트

참고: 이는 일반적으로 캐스케이드 값 해석 시 해석되며, 사용자 정의 속성의 값으로 유지되지 않으므로, 해당 변수로 대체되지 않습니다.

참고: 이 모듈은 사용자 정의 속성var() 함수와 함께 “변수”로 사용하는 것에 중점을 두지만, 실제 사용자 정의 속성으로도 사용할 수 있으며, 스크립트에서 파싱 및 동작할 수 있습니다. CSS 확장 명세 [CSS-EXTENSIONS]가 이러한 사용 사례를 확장하고 더 쉽게 사용할 수 있도록 할 것으로 기대됩니다.

사용자 정의 속성은 일반 속성이므로, 모든 요소에 선언할 수 있고, 일반 상속 및 캐스케이드 규칙으로 처리되며, @media 및 기타 조건부 규칙으로 조건부 선언이 가능하며, HTML의 style 속성에서도 사용할 수 있고, CSSOM으로 읽거나 설정할 수도 있습니다.

테스트

특히, 전환이나 애니메이션도 가능하지만, UA가 내용 해석 방법을 알 수 없으므로, 지능적으로 보간할 수 없는 값 쌍에 사용하는 "50%에서 전환" 동작을 사용합니다. 단, 사용자 정의 속성@keyframes 규칙에서 사용될 경우, 애니메이션 오염됨(animation-tainted) 상태가 되며, 이는 애니메이션 속성에서 var() 함수로 참조될 때 처리 방식에 영향을 줍니다.

테스트

Animation-tainted는 "전염성"이 있습니다: animation-tainted 속성을 참조하는 사용자 정의 속성도 animation-tainted가 됩니다.

이 스타일 규칙:
:root {
  --header-color: #06c;
}

루트 요소에 사용자 정의 속성 --header-color를 선언하고, 값으로 "#06c"를 할당합니다. 이 속성은 문서의 나머지 요소에 상속됩니다. var() 함수로 값을 참조할 수 있습니다:

h1 { background-color: var(--header-color); }

이 규칙은 background-color: #06c;를 작성하는 것과 동일합니다. 변수명을 사용하면 색상의 출처를 더 명확하게 알 수 있고, var(--header-color)가 문서 내 다른 요소에서 사용된다면 루트 요소의 --header-color 속성만 변경해 모든 사용처를 한 번에 업데이트할 수 있습니다.

사용자 정의 속성을 여러 번 선언하면, 표준 계단식 규칙에 따라 해결됩니다. 변수는 항상 동일한 요소의 관련 사용자 정의 속성의 계산된 값에서 가져옵니다:
:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }

<p>나는 루트 요소에서 파란색을 상속받았어요!</p>
<div>나는 녹색을 직접 설정받았어요!</div>
<div id='alert'
  나는 빨간색을 직접 설정받았어요!
  <p>나도 빨간색이에요, 상속 때문이에요!</p>
</div>
사용자 정의 속성의 실제 사용 예시는 사용되는 위치에서 문자열을 쉽게 분리하여 국제화 유지 관리에 도움이 되는 것입니다:
:root,
:root:lang(en) {--external-link: "external link";}
:root:lang(el) {--external-link: "εξωτερικός σύνδεσμος";}

a[href^="http"]::after {content: " (" var(--external-link) ")"}

변수 선언은 별도의 파일에 보관할 수도 있어, 번역 유지 관리가 더 쉬워질 수 있습니다.

2.1. 사용자 정의 속성 값 문법

사용자 정의 속성에 허용되는 문법은 매우 관대합니다. <declaration-value> 생산식은 하나 이상의 토큰 시퀀스와 일치합니다. 단, 해당 시퀀스에 <bad-string-token>, <bad-url-token>, 일치하지 않는 <)-token>, <]-token>, 또는 <}-token>, 또는 최상위 <semicolon-token> 토큰이나 값이 "!"인 <delim-token> 토큰이 포함되어 있지 않아야 합니다.

테스트

또한, 사용자 정의 속성의 값에 var() 참조가 포함되어 있으면, var() 참조는 지정된 var() 문법에 따라 유효해야 합니다. 그렇지 않으면 사용자 정의 속성이 잘못된 것으로 간주되어 무시되어야 합니다.

참고: 이 정의는 일반적인 CSS 문법 규칙과 함께, 사용자 정의 속성 값에 맞지 않는 따옴표나 괄호가 포함되지 않음을 의미합니다. 따라서 다시 직렬화될 때 스타일 규칙과 같은 더 큰 문법 구조에 영향을 줄 수 없습니다.

참고: 사용자 정의 속성에는 끝에 !important가 포함될 수 있지만, 이것은 CSS 파서에 의해 속성 값에서 자동으로 제거되며, 해당 사용자 정의 속성을 CSS 계단식에서 "중요"하게 만듭니다. 즉, 최상위 "!" 문자의 금지는 !important의 사용을 막지 않으며, !important는 구문 검사 전에 제거됩니다.

테스트
예를 들어, 다음은 유효한 사용자 정의 속성입니다:
--foo: if(x > 5) this.width = 10;

이 값은 변수로서는 분명히 쓸모없지만, 일반 속성에서는 유효하지 않으므로, JavaScript에서 읽어 동작하게 할 수도 있습니다.

사용자 정의 속성의 값과, var() 함수가 사용자 정의 속성에 대입될 때의 값은 대소문자를 구분하며, 원래 작성자가 준 원래의 대소문자를 그대로 유지해야 합니다. (많은 CSS 값은 ASCII 대소문자 구분 없음이며, 사용자 에이전트는 이를 하나의 대소문자로 "정규화"할 수 있지만, 사용자 정의 속성에는 허용되지 않습니다.)

테스트
사용자 정의 속성은 아무 값이나 포함할 수 있으므로, 그 내부를 어떻게 해석해야 하는지 일반적인 방법은 없습니다 (var()로 알려진 속성에 대입될 때까지). 일부 경우에 부분적으로 해석되는 대신, 완전히 해석되지 않은 상태로 남아 있습니다; 이들은 CSS 토큰의 순수 스트림이며, 그 사이에 var() 함수가 끼어 있을 수 있습니다.

이로 인해 추가적인 영향이 있습니다. 예를 들어 CSS의 상대 URL은 값이 나타나는 스타일시트의 기준 URL을 기준으로 해석됩니다. 그러나 --my-image: url(foo.jpg);와 같은 사용자 정의 속성이 "/a/style.css" 스타일시트에 있다면, 즉시 절대 URL로 해석되지 않습니다; 만약 그 변수가 다른 "/b/style.css" 스타일시트에서 background: var(--my-image);처럼 사용된다면, 그 시점에 "/b/foo.jpg"로 해석됩니다.

2.2. 보장된 잘못된 값

사용자 정의 속성의 초기 값은 보장된 잘못된 값입니다. § 3 계단식 변수 사용: var() 표기법에 정의된 바와 같이, var()를 사용해 이 값을 가진 사용자 정의 속성을 대체하면, 이를 참조하는 속성은 계산값 시점에서 잘못됨이 됩니다.

이 값은 빈 문자열로 직렬화되지만, 실제로 --foo: ;처럼 사용자 정의 속성에 빈 값을 작성하는 것은 유효한 (빈) 값이며, 보장된 잘못된 값이 아닙니다. 만약 어떤 이유로든, 변수를 수동으로 보장된 잘못된 값으로 초기화하고 싶다면, initial 키워드를 사용하면 됩니다.

2.3. 의존성 순환 해결

사용자 정의 속성은 거의 평가되지 않은 채로 남아 있지만, 값에 var() 함수는 허용되고 평가됩니다. 이를 통해 사용자 정의 속성이 var()로 자기 자신을 참조하거나, 두 개 이상의 사용자 정의 속성이 서로를 참조하는 등 순환 의존성이 생길 수 있습니다.

각 요소마다, 각 사용자 정의 속성에 대한 노드가 포함된 방향성 의존성 그래프를 만듭니다. 만약 사용자 정의 속성 prop의 값에 var() 함수가 var 속성을 참조한다면 (var()의 폴백 인자 포함), prop에서 var로 엣지를 추가합니다. 자기 자신을 가리키는 엣지도 가능합니다.

의존성 그래프에 순환이 있으면, 해당 순환에 속한 모든 사용자 정의 속성계산값 시점에서 잘못됨이 됩니다.

테스트

참고: 순환에 참여하는 정의된 속성은 값에 잘못된 변수가 존재할 수 있으며 (계산값 시점에서 잘못됨), 아니면 자체 순환 처리 방식을 정의할 수 있습니다 (font-sizeem 값을 사용할 때처럼). 이들은 사용자 정의 속성처럼 보장된 잘못된 값으로 계산되지 않습니다.

이 예시는 변수를 안전하게 사용하는 사용자 정의 속성을 보여줍니다:
:root {
  --main-color: #c06;
  --accent-background: linear-gradient(to top, var(--main-color), white);
}

--accent-background 속성은 (var(--main-color)을 사용하는 다른 모든 속성과 함께) --main-color 속성이 변경되면 자동으로 업데이트됩니다.

반면에, 이 예시에서는 서로 의존하는 변수의 잘못된 사례를 보여줍니다:
:root {
  --one: calc(var(--two) + 20px);
  --two: calc(var(--one) - 20px);
}

--one--two는 모두 이제 계산값 시점에서 잘못됨이 되며, 길이 대신 보장된 잘못된 값으로 계산됩니다.

중요한 점은 사용자 정의 속성은 값에 있는 var() 함수를 계산값 시점에 해석하며, 이 시점은 값이 상속되기 이전에 발생합니다. 일반적으로, 순환 의존성은 동일한 요소의 여러 사용자 정의 속성이 서로를 참조할 때만 발생합니다; 요소 트리의 상위에 정의된 사용자 정의 속성은 하위에 정의된 속성과 순환 참조를 일으킬 수 없습니다.

테스트
예를 들어, 다음 구조를 갖는다면, 이 사용자 정의 속성들은 순환적이지 않으며, 모두 유효한 변수를 정의합니다:
<one><two><three /></two></one>
<style>
one   { --foo: 10px; }
two   { --bar: calc(var(--foo) + 10px); }
three { --foo: calc(var(--bar) + 10px); }
</style>

<one> 요소는 --foo 값을 정의합니다. <two> 요소는 이 값을 상속받고, 추가로 --barfoo 변수를 사용해 값을 할당합니다. 마지막으로, <three> 요소는 변수 치환 이후 --bar 값을 상속받습니다 (즉, calc(10px + 10px) 값을 보게 됩니다), 그리고 그 값으로 --foo를 다시 정의합니다. 상속받은 --bar 값에는 더 이상 <one>에 정의된 --foo에 대한 참조가 없으므로, var(--bar)--foo를 정의하는 것은 순환적이지 않으며, 실제로 나중에 (일반 속성에서 변수로 참조할 때) 30px로 해석됩니다.

3. 계단식 변수 사용: var() 표기법

사용자 정의 속성의 값은 var() 함수를 사용하여 다른 속성의 값에 대입할 수 있습니다. var()의 문법은 다음과 같습니다:

var() = var( <custom-property-name> , <declaration-value>? )
테스트

@supports


일반적인 쉼표 생략 규칙의 예외로, 해당 규칙은 쉼표가 값을 구분하지 않을 때 생략을 요구하지만, 아무 것도 뒤따르지 않는 단독 쉼표는 var()에서 유효한 것으로 취급되어야 하며, 이는 빈 폴백 값을 나타냅니다.

테스트

참고: 즉, var(--a,)는 유효한 함수이며, --a 사용자 정의 속성이 잘못되었거나 없을 경우, var()가 아무것도 없는 것으로 대체됨을 지정합니다.

var() 함수는 요소의 모든 속성 값의 어떤 부분에도 사용할 수 있습니다. var() 함수는 속성명이나 셀렉터, 또는 속성 값 이외의 곳에서는 사용할 수 없습니다. (이렇게 사용하면 대개 잘못된 구문이 되거나, 변수와 아무 관련 없는 의미가 없는 값이 됩니다.)

테스트
예를 들어, 다음 코드는 변수를 속성 이름으로 잘못 사용하려고 시도합니다:
.foo {
  --side: margin-top;
  var(--side): 20px;
}

이것은 margin-top: 20px;을 설정하는 것과 동일하지 않습니다. 대신, 두 번째 선언은 잘못된 속성 이름 때문에 구문 오류로 그냥 버려집니다.

함수의 첫 번째 인자는 대체될 사용자 정의 속성의 이름입니다. 함수의 두 번째 인자는, 제공되는 경우, 폴백 값이며, 참조된 사용자 정의 속성의 값이 보장된 잘못된 값일 때 대체 값으로 사용됩니다.

테스트

참고: 폴백의 문법은 사용자 정의 속성과 같이 쉼표를 허용합니다. 예를 들어, var(--foo, red, blue)는 폴백 값으로 red, blue를 정의합니다; 즉, 첫 번째 쉼표 이후부터 함수 끝까지가 모두 폴백 값으로 간주됩니다.

폴백 값은 일부 유형의 방어적 코딩을 가능하게 합니다. 예를 들어, 작성자가 더 큰 애플리케이션에 포함될 컴포넌트를 만들고, 스타일을 변수로 지정하여 더 큰 앱 작성자가 그 컴포넌트의 스타일을 앱의 나머지와 쉽게 맞출 수 있도록 할 수 있습니다.

폴백이 없다면, 앱 작성자는 컴포넌트가 사용하는 모든 변수에 값을 제공해야 합니다. 폴백이 있다면 컴포넌트 작성자가 기본값을 지정할 수 있으므로, 앱 작성자는 오버라이드하고 싶은 변수만 값을 주면 됩니다.

/* 컴포넌트의 스타일: */
.component .header {
  color: var(--header-color, blue);
}
.component .text {
  color: var(--text-color, black);
}

/* 더 큰 애플리케이션의 스타일: */
.component {
  --text-color: #080;
  /* header-color는 설정되지 않아서,
     blue(폴백 값)으로 남습니다 */
}

속성에 하나 이상의 var() 함수가 포함되어 있고, 그 함수들이 구문상 유효하다면, 전체 속성의 문법은 파싱 시점에 유효한 것으로 간주되어야 합니다. 실제 구문 검사는 var() 함수가 대체된 후 계산값 시점에만 이루어집니다.

테스트

속성 값에서 var()를 치환하려면:

  1. 사용자 정의 속성var() 함수의 첫 번째 인자로 지정되었고, 해당 var() 함수가 animation-tainted이며, 해당 속성이 애니메이션이 불가능한 속성에서 사용된다면, 이 알고리즘의 나머지에서 사용자 정의 속성은 초기 값을 갖는 것으로 처리한다.
  2. 사용자 정의 속성var() 함수의 첫 번째 인자로 지정되었고, 그 값이 초기 값이 아니라면, var() 함수를 해당 사용자 정의 속성의 값으로 치환한다.
  3. 그렇지 않고, var() 함수의 두 번째 인자가 폴백 값이라면, var() 함수를 폴백 값으로 치환한다. 폴백 값에 var() 참조가 있으면, 그것들도 치환한다.
  4. 그 외의 경우, var() 함수가 포함된 속성은 계산값 시점에서 잘못됨이 된다.

    참고: 속성이 계산값 시점에서 잘못됨이 되는 다른 경우도 있습니다.

테스트

CSSOM


참고: var() 치환은 텍스트 수준이 아니라 CSS 토큰 [css-syntax-3] 수준에서 이루어진다는 점에 유의하세요. 변수로 일부를 제공한다고 해서 하나의 토큰을 조합할 수는 없습니다:
.foo {
  --gap: 20;
  margin-top: var(--gap)px;
}

이것은 margin-top: 20px; (길이)을 설정하는 것과 동일하지 않습니다. 대신, 이는 margin-top: 20 px; (숫자 뒤에 식별자)가 되어, margin-top 속성에는 단순히 잘못된 값입니다. 하지만 calc()를 사용하면 다음과 같이 같은 효과를 올바르게 낼 수 있습니다:

.foo {
  --gap: 20;
  margin-top: calc(var(--gap) * 1px);
}
테스트

var() 함수는 치환이 계산값 시점에 이루어집니다. 모든 var() 함수가 치환된 후, 선언이 해당 선언된 문법과 일치하지 않으면, 그 선언은 계산값 시점에서 잘못된 것이 됩니다.

테스트

모든 var() 함수가 치환된 후, 선언이 CSS 전체 키워드(그리고 공백만 있을 수도 있음)만 포함한다면, 해당 값은 해당 키워드가 처음부터 지정값이었던 것처럼 결정됩니다.

테스트
예를 들어, 다음 사용법은 구문적으로는 문제가 없지만, 변수가 치환되면 잘못된 결과가 나옵니다:
:root { --looks-valid: 20px; }
p { background-color: var(--looks-valid); }

20pxbackground-color에 대해 잘못된 값이므로, 해당 속성의 값은 transparent(background-color의 초기 값)로 계산됩니다.

속성이 기본적으로 상속되는 것이라면, 예를 들어 color처럼, 초기값이 아니라 상속값으로 계산됩니다.

var() 함수는 CSS 전체 키워드를 사용자 정의 속성 자체에서 가져올 수 없습니다—예를 들어 --foo: initial;처럼 지정하려고 하면, 해당 사용자 정의 속성에 명시적 기본값만 트리거됩니다—하지만 폴백에는 CSS 전체 키워드를 사용할 수 있습니다:
p { color: var(--does-not-exist, initial); }

위 코드에서 --does-not-exist 속성이 존재하지 않거나 계산값 시점에서 잘못됨이라면, var()initial 키워드로 치환되어, 해당 속성이 처음에 color: initial처럼 동작하게 만듭니다. 이는 폴백이 없을 때처럼 상속값이 아니라 문서의 초기 color 값을 갖게 만듭니다.

3.1. 잘못된 변수

사용자 정의 속성의 값이 보장된 잘못된 값이면, var() 함수는 치환에 사용할 수 없습니다. 시도하면 선언이 계산값 시점에서 잘못됨이 되고, 폴백이 유효한 경우를 제외하면 그렇습니다.

선언은 계산값 시점에서 잘못됨이 될 수 있습니다. 예를 들어 var()보장된 잘못된 값을 가진 사용자 정의 속성을 참조할 때, 혹은 유효한 사용자 정의 속성을 사용하지만, 그 속성 값이 모든 var() 함수 치환 후에도 잘못된 경우입니다. 이런 경우, 계산값은 속성의 종류에 따라 다음 중 하나가 됩니다:

속성이 등록되지 않은 사용자 정의 속성인 경우
등록된 사용자 정의 속성범용 문법을 가진 경우

계산값은 보장된 잘못된 값입니다.

그 외의 경우

속성이 상속되는 경우에는 상속값, 그렇지 않으면 초기값이 됩니다. 이는 속성 값이 unset 키워드로 지정된 것과 같습니다.

테스트
예를 들어, 다음 코드에서:
:root { --not-a-color: 20px; }
p { background-color: red; }
p { background-color: var(--not-a-color); }

<p> 요소는 투명 배경 (background-color의 초기값)을 가지게 되며, 빨간 배경이 되지 않습니다. 사용자 정의 속성이 해제되었거나, 잘못된 var() 함수를 포함한 경우에도 마찬가지입니다.

이것은 작성자가 스타일시트에 background-color: 20px를 직접 쓴 것과는 차이가 있습니다 - 그 경우에는 일반적인 구문 오류가 발생하여, 규칙이 버려지고 background-color: red 규칙이 적용됩니다.

참고: 계산값 시점에서 잘못됨 개념은 변수는 다른 구문 오류처럼 "빠르게 실패"할 수 없기 때문에 존재합니다. 사용자 에이전트가 속성 값이 잘못된 것을 알게 되는 시점에는 이미 다른 계단식 값들은 버려진 뒤입니다.

3.2. 축약 속성에서의 변수

var() 함수는 축약 속성을 구성요소 롱핸드로 파싱할 때, 그리고 축약 속성을 구성요소 롱핸드에서 직렬화할 때 몇 가지 복잡함을 만듭니다.

축약 속성var() 함수가 값에 포함되어 있으면, 해당 축약 속성과 연관된 롱핸드 속성은 특수한, 작성자가 관찰할 수 없는 대기-치환 값으로 채워져야 하며, 이는 축약 속성에 변수가 포함되어 있다는 것을 나타냅니다. 따라서 롱핸드 값은 변수가 치환될 때까지 결정할 수 없습니다.

이 값은 일반적으로 계단식되고, 계산값 시점에, var() 함수가 최종적으로 치환된 후, 축약 속성은 파싱되고 롱핸드에는 그 시점에서 적절한 값이 할당되어야 합니다.

테스트

참고: 축약 속성이 var() 없이 작성된 경우, 파싱 시점에 구성요소 롱핸드 속성으로 분리되어 파싱됩니다; 롱핸드들은 계단식에 참여하게 되고, 축약 속성은 사실상 폐기됩니다. 하지만 축약 속성에 var()가 포함된 경우에는 이렇게 할 수 없으며, var()가 무엇으로 치환될지 알 수 없기 때문입니다.

대기-치환 값은 API에서 관찰이 가능하다면 빈 문자열로 직렬화되어야 합니다.

테스트

축약 속성은 구성요소 롱핸드 속성의 값을 모아서, 동일한 값 집합으로 파싱될 수 있는 값을 합성하여 직렬화됩니다.

주어진 축약 속성의 모든 구성요소 롱핸드 속성이 동일한 원래 축약 값에서 온 대기-치환 값이면, 축약 속성은 그 원래(var()를 포함한) 값을 직렬화해야 합니다.

그렇지 않고, 주어진 축약 속성의 구성요소 롱핸드 속성 중 하나라도 대기-치환 값이거나, 아직 치환되지 않은 var() 함수를 포함한다면, 축약 속성은 빈 문자열로 직렬화해야 합니다.

3.3. 지나치게 긴 변수 안전하게 처리하기

순진하게 구현된 var() 함수는 "billion laughs attack"의 변형에 사용될 수 있습니다:

.foo {
  --prop1: lol;
  --prop2: var(--prop1) var(--prop1);
  --prop3: var(--prop2) var(--prop2);
  --prop4: var(--prop3) var(--prop3);
  /* etc */
}

이 짧은 예시에서 --prop4의 계산값은 lol lol lol lol lol lol lol lol이 되어, 원래 lol이 8번 반복됩니다. 레벨을 하나 추가할 때마다 식별자 수가 두 배로 늘어나며, 단 30단계로 확장하기만 해도 --prop30에는 거의 10억 번의 식별자가 들어가게 됩니다.

이런 공격을 막기 위해, UA는 var() 함수가 확장될 때 허용되는 토큰 스트림의 길이에 UA가 정한 제한을 두어야 합니다. var()가 이 제한보다 긴 토큰 스트림으로 확장된다면, 확장되는 속성은 계산값 시점에서 잘못됨이 됩니다.

테스트

이 명세는 어떤 크기 제한을 둬야 하는지 정의하지 않습니다. 하지만 1킬로바이트 이상의 텍스트를 담는 사용자 정의 속성의 유효한 사용 사례도 있으므로, 제한은 비교적 높게 설정하는 것이 권장됩니다.

참고: UA가 자원 제약으로 인해 표준을 위반할 수 있다는 일반 원칙은 여기서도 여전히 유효합니다; UA는 별도로 지원하는 사용자 정의 속성의 길이나 식별자의 크기에 제한을 둘 수 있습니다. 이 절에서는 이 공격이 오랜 역사를 가지고 있고, 처음에는 개별 항목이 커 보이지 않더라도 충분히 위험할 수 있어서 특별히 언급한 것입니다.

4. API

모든 사용자 정의 속성 선언에는 대소문자 구분 플래그가 설정되어 있습니다.

테스트

참고: 사용자 정의 속성은 CSSStyleDeclaration 객체에서 카멜 케이스 형태로 나타나지 않습니다. 왜냐하면 이름에 대소문자가 모두 들어갈 수 있고, 이는 서로 다른 사용자 정의 속성을 나타내기 때문입니다. 자동 카멜 케이싱이 수행하는 텍스트 변환은 이와 호환되지 않습니다. 대신 getPropertyValue() 등으로 올바른 이름으로 접근할 수 있습니다.

4.1. 사용자 정의 속성 직렬화하기

사용자 정의 속성 이름은 작성자가 제공한 정확한 코드 포인트 시퀀스로, 대소문자를 변경하지 않고 직렬화해야 합니다.

참고: 사용자 정의 속성이 아닌 속성의 경우, 속성 이름은 ASCII 범위로 제한되고 ASCII 대소문자 구분 없음이므로, 구현체는 보통 이름을 소문자로 직렬화합니다.

사용자 정의 속성의 지정값은 작성자가 지정한 그대로 직렬화해야 합니다. 다른 속성에서 발생할 수 있는 주석 삭제, 공백 정규화, 숫자 토큰을 값에서 다시 직렬화 등 단순화 작업을 하면 안 됩니다.

사용자 정의 속성의 계산값 역시 작성자가 지정한 그대로 직렬화해야 하며, 단, var() 함수가 치환된 경우는 예외입니다.

테스트
예를 들어, 다음 속성들이 주어진 경우:
--y: /* baz */;
--x: /* foo */ var(--y) /* bar */;

--x의 지정값 직렬화는 "/* foo */ var(--y) /* bar */"가 되어야 하며, 계산값 직렬화는 "/* foo */ /* baz */ /* bar */"가 되어야 합니다.

(값 앞의 공백은 CSS 파서가 자동으로 잘라내므로 여기에는 보존되지 않습니다.)

이 요구사항은 작성자가 사용자 정의 속성에 CSS가 아닌 정보를 저장하는 경우가 있기 때문에 존재합니다. "정규화" 과정에서 이 정보가 변경되면 작성자 코드가 깨질 수 있습니다.

예를 들어 사용자 정의 속성에 UUID를 저장할 때, --uuid: 12345678-12e3-8d9b-a456-426614174000처럼, 스크립트로 접근할 때 UUID가 작성된 그대로 반환되어야 합니다.

이 값은 기술적으로는 CSS에서 인접한 숫자와 차원들의 연속으로 파싱됩니다. 특히 "-12e3" 부분은 -12000에 해당하는 숫자로 파싱됩니다. 다른 컨텍스트에서 CSSOM이 요구하는 형태로 다시 직렬화하면, 작성자의 값 사용을 치명적으로 깨뜨릴 수 있습니다.

5. 변경 사항

5.1. 2021년 11월 11일 CR 초안 이후의 변경 사항

5.2. 2015년 12월 3일 CR 이후의 변경 사항

5.3. 2014년 5월 6일 Last Call Working Draft 이후의 변경 사항

6. 감사의 글

수년간 CSS Working Group에서 변수의 꿈을 지켜준 여러 분들께 많은 감사를 드립니다, 특히 Daniel Glazman과 David Hyatt에게. 메일링 리스트에서 이 변수 정의 발전에 기여해주신 여러 분들께도 감사드립니다, 특히 Brian Kardell, David Baron, François Remy, Roland Steiner, 그리고 Shane Stephens.

7. 프라이버시 고려사항

이 명세는 작성자가 직접 제어하는 페이지 내에서 스타일 정보를 전달하기 위한 순수 작성자 수준의 메커니즘을 정의합니다. 그러므로 새로운 프라이버시 고려사항은 없습니다.

8. 보안 고려사항

§ 3.3 지나치게 긴 변수 안전하게 처리하기에서는 var() 함수와 같은 "매크로 확장"과 유사한 메커니즘에 대해 오래된 서비스 거부(DoS) 공격을 지적하며, 이에 대한 방어 조치를 요구합니다.

적합성

문서 관례

적합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”이라는 주요 단어는 RFC 2119에서 설명된 대로 해석되어야 합니다. 다만, 가독성을 위해 이 단어들은 이 명세서에서 모두 대문자로 표기하지 않습니다.

이 명세서의 모든 텍스트는 명시적으로 비규범적임을 표시한 섹션, 예시, 참고를 제외하고는 규범적입니다. [RFC2119]

이 명세서의 예시는 “for example”이라는 문구로 도입되거나, 아래와 같이 class="example" 로 구분됩니다:

이것은 정보 제공용 예시입니다.

참고는 “Note”라는 단어로 시작하며, 아래와 같이 class="note"로 구분됩니다:

참고, 이것은 정보 제공용 참고입니다.

권고(advisement)는 특별한 주의를 유도하는 규범 섹션으로, 아래와 같이 <strong class="advisement">로 구분됩니다: UA는 접근 가능한 대체 기능을 반드시 제공해야 합니다.

테스트

이 명세서 내용과 관련된 테스트는 아래처럼 “테스트” 블록에 문서화될 수 있습니다. 이러한 블록은 모두 비규범적입니다.


적합성 클래스

이 명세서에 대한 적합성은 세 가지 적합성 클래스로 정의됩니다:

스타일 시트
CSS 스타일 시트.
렌더러
UA는 스타일 시트의 의미를 해석하고, 이를 사용하는 문서를 렌더링합니다.
저작 도구
UA는 스타일 시트를 작성합니다.

스타일 시트가 이 명세서에 적합하다고 하려면, 이 모듈에서 정의된 문법을 사용하는 모든 문(statement)이 일반 CSS 문법 및 이 모듈에서 정의된 각 기능의 개별 문법에 따라 유효해야 합니다.

렌더러가 이 명세서에 적합하려면, 적합한 명세서에 정의된 대로 스타일 시트를 해석하는 것 외에도, 이 명세서에서 정의한 모든 기능을 올바르게 파싱하고 문서를 이에 따라 렌더링함으로써 지원해야 합니다. 단, 장치의 한계로 인해 UA가 문서를 올바르게 렌더링하지 못해도 UA가 비적합한 것은 아닙니다. (예: UA가 흑백 모니터에서 색상을 렌더링할 필요는 없음)

저작 도구가 이 명세서에 적합하려면, 일반 CSS 문법 및 이 모듈의 각 기능의 문법에 따라 구문적으로 올바른 스타일 시트를 작성해야 하며, 이 모듈에서 설명하는 스타일 시트의 모든 다른 적합성 요구사항도 충족해야 합니다.

부분 구현

작성자가 미래 호환성 파싱 규칙을 활용해 폴백 값을 지정할 수 있도록, CSS 렌더러는 반드시 지원 가능한 수준이 없는 at-rule, 속성, 속성 값, 키워드 등 모든 구문 구조를 잘못된 것으로 취급하고(적절하게 무시) 합니다. 특히, UA는 지원하지 않는 구성요소 값을 선택적으로 무시하고, 다중 값 속성 선언에서 지원하는 값만 적용해서는 안 됩니다: 어떤 값이라도 잘못된 것으로 간주되면(지원하지 않는 값은 반드시), CSS는 전체 선언을 무시하도록 요구합니다.

불안정 및 독자적 기능의 구현

향후 안정적인 CSS 기능과의 충돌을 피하기 위해, CSSWG는 모범 사례를 따라 불안정 기능 및 독자적 확장을 구현할 것을 권장합니다.

비실험적 구현

명세가 Candidate Recommendation 단계에 도달하면, 비실험적 구현이 가능하며, 구현자는 자신이 명세에 따라 올바르게 구현했다고 입증할 수 있는 CR 수준 기능에 대해 접두어 없는(unprefixed) 구현을 릴리스해야 합니다.

CSS의 상호운용성을 확보하고 유지하려면, CSS Working Group은 비실험적 CSS 렌더러가 접두어 없는 구현을 릴리스하기 전에 W3C에 구현 보고서(필요한 경우 구현 보고서에 사용된 테스트케이스 포함)를 제출해줄 것을 요청합니다. W3C에 제출된 테스트케이스는 CSS Working Group이 검토 및 수정할 수 있습니다.

테스트케이스 및 구현 보고서 제출에 관한 추가 정보는 CSS Working Group 웹사이트 http://www.w3.org/Style/CSS/Test/에서 확인할 수 있습니다. 문의는 public-css-testsuite@w3.org 메일링 리스트로 보내주세요.

CR 종료 기준

이 명세가 Proposed Recommendation 단계로 진척되려면, 각 기능마다 독립적이고 상호운용 가능한 구현이 최소 두 개 이상 있어야 합니다. 각 기능은 서로 다른 제품에서 구현될 수 있으며, 모든 기능이 단일 제품에서 구현될 필요는 없습니다. 이 기준의 목적을 위해 다음 용어를 정의합니다:

독립적(Independent)
각 구현은 서로 다른 주체에 의해 개발되어야 하며, 다른 자격을 갖춘 구현에 사용된 코드를 공유, 재사용 또는 파생해서는 안 됩니다. 이 명세 구현에 영향을 주지 않는 코드 부분은 이 요구사항에서 예외입니다.
상호운용 가능(Interoperable)
공식 CSS 테스트 스위트의 해당 테스트케이스를 통과하거나, 웹 브라우저가 아닌 경우에는 동등한 테스트를 통과합니다. 테스트 스위트의 모든 관련 테스트에는 동등한 테스트가 만들어져야 하며, 해당 UA가 상호운용성을 주장하려면 이러한 동등 테스트를 동일한 방식으로 통과할 수 있는 하나 이상의 추가 UA가 있어야 합니다. 동등 테스트는 동료 검토(peer review) 목적으로 공개되어야 합니다.
구현(Implementation)
사용자 에이전트(UA)로서:
  1. 명세를 구현함
  2. 일반 대중이 사용할 수 있음. 구현은 출시 제품이나 기타 공개 버전(예: 베타, 프리뷰, 나이틀리 빌드)이 될 수 있음. 출시되지 않은 제품 릴리스는 안정성을 입증하기 위해 해당 기능을 최소 한 달 이상 구현 상태로 유지해야 함.
  3. 실험적이지 않음(즉, 테스트 스위트만 통과하도록 설계된 버전이 아니며, 향후 정상적인 사용을 위해 설계되지 않은 버전이 아님).

명세는 최소 6개월간 Candidate Recommendation 단계에 머물게 됩니다.

색인

이 명세서에서 정의된 용어

참조로 정의된 용어

참고 문헌

규범적 참고 문헌

[CSS-ANIMATIONS-1]
Dean Jackson; 외. CSS Animations Level 1. 2018년 10월 11일. WD. URL: https://www.w3.org/TR/css-animations-1/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 2022년 1월 13일. CR. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-CONDITIONAL-3]
David Baron; Elika Etemad; Chris Lilley. CSS Conditional Rules Module Level 3. 2022년 1월 13일. CR. URL: https://www.w3.org/TR/css-conditional-3/
[CSS-PROPERTIES-VALUES-API-1]
Tab Atkins Jr.; 외. CSS Properties and Values API Level 1. 2020년 10월 13일. WD. URL: https://www.w3.org/TR/css-properties-values-api-1/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2021년 12월 24일. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. 2019년 6월 6일. CR. URL: https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 2021년 12월 16일. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS2]
Bert Bos; 외. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011년 6월 7일. REC. URL: https://www.w3.org/TR/CSS21/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). 2021년 8월 26일. WD. URL: https://www.w3.org/TR/cssom-1/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997년 3월. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. 2018년 11월 21일. WD. URL: https://www.w3.org/TR/selectors-4/
[WEB-ANIMATIONS-1]
Brian Birtles; 외. Web Animations. 2021년 5월 18일. WD. URL: https://www.w3.org/TR/web-animations-1/

정보 제공 참고 문헌

[CHARMOD-NORM]
Addison Phillips; 외. Character Model for the World Wide Web: String Matching. 2021년 8월 11일. NOTE. URL: https://www.w3.org/TR/charmod-norm/
[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 2021년 7월 26일. CR. URL: https://www.w3.org/TR/css-backgrounds-3/
[CSS-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. 2020년 4월 21일. WD. URL: https://www.w3.org/TR/css-box-4/
[CSS-CASCADE-6]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 6. 2021년 12월 21일. WD. URL: https://www.w3.org/TR/css-cascade-6/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley; Lea Verou. CSS Color Module Level 4. 2021년 12월 15일. WD. URL: https://www.w3.org/TR/css-color-4/
[CSS-EXTENSIONS]
Tab Atkins Jr.. CSS Extensions. ED. URL: https://drafts.csswg.org/css-extensions/
[CSS-FONTS-4]
John Daggett; Myles Maxfield; Chris Lilley. CSS Fonts Module Level 4. 2021년 12월 21일. WD. URL: https://www.w3.org/TR/css-fonts-4/

속성 색인

이름 초기값 적용 대상 상속 % 비율 애니메이션 유형 표준 순서 계산값
--* <declaration-value>? the guaranteed-invalid value 모든 요소와 모든 의사 요소(제한된 속성 목록을 가진 의사 요소 포함) 해당 없음 단일(이산) 문법에 따름 치환된 변수의 지정값 또는 보장된 잘못된 값