1. 소개
CSS는 웹 문서의 레이아웃, 페인팅 또는 동작을 수정하기 위해 조작할 수 있는 포괄적인 속성 집합을 정의합니다. 하지만 웹 저자들은 이 속성 집합을 추가적인 속성으로 확장하고 싶어하는 경우가 많습니다.
[css-variables]는 사용자가 제어할 수 있는 속성을 정의할 수 있는 원시적인 방법을 제공합니다. 그러나 이러한 속성은 항상 토큰 리스트를 값으로 사용하고, 항상 상속되어야 하며, var() 참조를 통해 다른 속성의 값에 다시 통합될 때만 문서의 레이아웃이나 페인팅에 영향을 줄 수 있습니다.
이 명세는 [css-variables]를 확장하여, 값 타입, 초기 값, 정의된 상속 동작을 갖는 속성을 두 가지 방법을 통해 등록할 수 있도록 합니다:
-
JS API,
registerProperty()
메서드 -
CSS at-rule, @property 규칙
이 명세는 [css-paint-api-1] 및 [css-layout-api-1]와 보완적으로 동작합니다. 이 명세들은 사용자 정의 속성이 각각 페인팅 또는 레이아웃 동작에 직접 영향을 줄 수 있게 해줍니다.
2. 등록된 사용자 정의 속성
사용자 정의 속성은 등록된 사용자 정의 속성이 될 수 있습니다.
등록되면 UA(사용자 에이전트)가 정의한 속성처럼 동작하며,
UA가 검증하는 구문, 초기 값, 특정 상속 동작을 갖게 됩니다.
이는 @property 규칙이나,
registerProperty()
JS 함수를 통해 가능합니다.
사용자 정의 속성은 Document
에
대해 다음 중 하나에 해당하면 등록된 것으로 간주됩니다:
문서의 스타일시트 중 하나에 이름에 대해 유효한 @property 규칙이 정의되어 있거나,
이름이 문서의 [[registeredPropertySet]]
슬롯에 포함되어 있을 때
(registerProperty()
로 등록된 경우).
등록된 사용자 정의 속성은 등록되지 않은 사용자 정의 속성과 유사하게 동작하지만, 아래에서 정의된 점이 다릅니다.
2.1. 등록 여부 결정
등록된 사용자 정의 속성은 사용자 정의 속성 등록 정보를 가지며, 실제 속성처럼 처리하기 위한 모든 데이터를 포함합니다. 이는 다음으로 구성된 구조체입니다:
-
속성 이름 (사용자 정의 속성 이름 문자열)
-
구문 (구문 문자열)
-
상속 플래그 (불리언)
만약 Document
의
[[registeredPropertySet]]
슬롯이 포함하는 레코드에 사용자 정의
속성의 이름이 있다면,
등록 정보는 해당 레코드입니다.
그렇지 않으면,
Document
의
활성 스타일시트에 이름에 대해 유효한 @property 규칙이 하나 이상 있다면,
문서 순서상 마지막 규칙이 등록 정보가 됩니다.
그 밖에는 등록 정보가 없으며, 사용자 정의 속성은 등록된 사용자 정의 속성이 아닙니다.
2.2. 구문 분석 시 동작
등록된 사용자 정의 속성은 등록되지 않은 사용자 정의 속성과 동일하게 구문 분석됩니다. 거의 모든 값이 허용됩니다. 등록된 속성의 구문은 구문 분석 시점에 검사되지 않습니다.
참고: 다만, 구문은 계산 값 시점에 var()로 치환되기 전에 검사됩니다. § 2.4 계산 값 시점 동작을 참조하십시오.
왜 사용자 정의 속성은 구문 검사를 하지 않을까요?
페이지의 CSS를 파싱할 때, UA는 속도와 메모리 모두를 향상시키기 위해 여러 최적화를 적용합니다.
그 중 하나는 실제로 효과가 있는 속성만 저장하고; 잘못된 속성은 버리며, 하나의 선언 블록에서 같은 속성을 여러 번 쓴 경우 마지막 유효한 것만 남기고 나머지는 버립니다. (이것은 CSS의 오류 복구와 미래 호환성 동작에 매우 중요합니다.)
속성의 구문이 페이지의 생명주기 동안 결코 변하지 않는다면 이 방식은 잘 작동합니다. 하지만 사용자 정의 속성이 등록되면 구문이 변경될 수 있고, 이전에는 잘못된 값이었던 속성이 갑자기 유효해질 수 있습니다.
이를 처리하는 방법은 모든 선언을 저장하거나, (처음에 잘못된 것까지 모두, 페이지의 메모리 비용 증가) 새 구문 규칙으로 페이지의 CSS 전체를 다시 파싱하는 것뿐입니다. (사용자 정의 속성 등록 시 처리 비용 증가) 둘 다 바람직하지 않습니다.
또한 UA가 정의한 속성의 구문은 사용자가 보고 있는 UA 버전에 의해 결정되고, 이는 저자가 제어할 수 없습니다. 이 때문에 CSS는 오류 복구 동작과 다양한 지원 수준을 위한 여러 선언을 작성하는 관행이 있습니다. 반면, 사용자 정의 속성의 구문은 페이지 저자가 스타일시트나 스크립트로 직접 제어합니다; 관리해야 할 불확실성이 없습니다. 구문 오류가 있는 사용자 정의 속성을 버리는 것은 저자에게 편의성만 제공할 뿐, UA 정의 속성처럼 반드시 필요한 것은 아닙니다.
2.3. 지정 값 처리 시 동작
등록되지 않은 사용자 정의 속성과 마찬가지로, 모든 등록된 사용자 정의 속성은 등록된 구문과 관계없이, CSS 전체 키워드(예: inherit 또는 revert)를 허용합니다. 동작은 CSS Cascading 4 § 7.3 명시적 기본값 지정에서 정의되어 있습니다.
2.4. 계산 값 처리 시 동작
등록된 사용자 정의 속성의 계산 값은 등록 정보의 구문에 의해 결정됩니다.
만약 등록 정보의 구문이 범용 구문 정의라면, 계산 값은 등록되지 않은 사용자 정의 속성과 동일합니다 (변수가 치환된 지정 값 또는 보장된-잘못된 값).
그 외의 경우, 등록된 구문에 따라 속성 값을 파싱하려 시도합니다. 실패하면, 선언은 계산 값 시점에 잘못된 값이 되며, 계산 값은 이에 따라 결정됩니다. 성공하면, 계산 값은 구문의 종류에 따라 달라집니다:
"<length>"
, "<length-percentage>"
, "<angle>"
,
"<time>"
, "<resolution>"
, "<integer>"
,
"<number>"
,
그리고 "<percentage>"
값의 경우:
-
지정 값이 치수 리터럴 (예: 50em 또는 .2s)이면, 계산 값은 같은 값이지만 단위가 해당 타입의 정식 단위로 변환됩니다.
-
지정 값이 다른 숫자 리터럴 (예: 5 또는 20%)이면, 계산 값은 지정 값 그대로입니다. (특히, %는 어떤 값에도 절대적으로 해석되지 않습니다.)
-
지정 값이 해당 타입으로 평가되는 함수 (예: 수학 함수)이면, 계산 값은 해당 함수에 의해 정의됩니다.
"<color>"
값의 경우,
값은 색상 값을 해석하여 계산되며,
CSS Color 4 § 14.
<color> 값 해석에 따릅니다.
"<custom-ident>"
, ident, 또는 "*"
값의 경우,
계산 값은 지정 값 그대로입니다.
"<url>"
값의 경우,
계산 값은 다음 중 하나입니다:
-
URL이 상대 경로일 경우, 계산 값은 [css3-values]에 설명된 대로 해석된 절대 URL입니다.
-
그 외에는 계산 값이 지정 값 그대로입니다.
URL 동작 예시
예를 들어, --url-foo 및 --url-bar가 <url> 구문으로 등록된 사용자 정의 속성이라고
가정하고, /style/foo/foo.css
에 다음과 같은 스타일시트가 있다고 하겠습니다:
div{ --url-foo : url ( "foo.png" ); }
그리고 /style/bar/bar.css
에 또 다른 스타일시트가 있습니다.
div{ --url-bar : url ( "bar.png" ); }
그리고 마지막으로 /index.html
에 문서가 있습니다:
< link href = "/style/foo/foo.css" rel = "stylesheet" type = "text/css" > < link href = "/style/bar/bar.css" rel = "stylesheet" type = "text/css" > < div style = "background-image: var(--url-foo), var(---url-bar);" > </ div >
여기서 var(--url-foo) 참조는 /style/foo
를 기준으로 URL을 해석하고, var(--url-bar) 참조는 /style/bar
를 기준으로 해석됩니다.
반면,
--url-foo와 --url-bar가 등록되지 않은 경우,
값(상대 URL)을 /index.html
스타일시트로 치환하게 되고,
URL은 /index.html
을 기준으로 해석됩니다.
"<image>"
값의 경우,
계산 값은 계산된 <image>입니다.
"<transform-function>"
및 "<transform-list>"
값의 경우,
계산 값은 모든 길이가 계산된 값으로 해석된 지정 값입니다.
반복자가 있는 구문의 경우, 계산 값은 기본 타입의 계산 값 리스트입니다.
| 조합자로 명시된 구문의 경우, 값을 만족시키는 첫 번째 절의 계산 값 규칙을 적용하여 계산 값을 얻습니다.
2.5. 애니메이션 동작
참고: [css3-animations] 및 [css3-transitions]에서 정의된 것처럼, 사용자 정의 속성을 참조하는 애니메이션 및 트랜지션을 지정할 수 있습니다.
애니메이션과 트랜지션에서 참조될 때, 사용자 정의 속성 값은 파싱된 타입에 따라 보간되며 계산 값 기준으로 동작합니다.
참고: <color>+
또는
<color>#
와 같은 값 목록은
각 구성요소를 인덱스별로 맞춰 단순 리스트로 보간되며,
구성요소의 수가 일치하지 않으면 보간이 실패합니다.
위 규칙의 예외로,
값이 <transform-list>
,
<transform-function>
,
또는 <transform-function>+
로 파싱된 경우에는 transform 속성의 방식으로 보간됩니다.
참고: 만약 어떤 이유로든 사용자 정의 속성이
<transform-function>#
구문으로 정의된 경우,
먼저 단순 리스트로 보간되고, 이후 각 리스트 항목이 transform 값으로 보간됩니다.
참고: 사용자 정의 속성을 등록(또는 등록을 변경)하면 계산 값이 바뀔 수 있으며, 이는 CSS 트랜지션을 시작하거나 중단시킬 수 있습니다.
2.6. 조건부 규칙
§ 2.2 구문 분석 시 동작에서 설명한 것처럼, 등록되지 않은 속성과 등록된 사용자 정의 속성 모두 파싱 시 거의 모든 값을 허용합니다. 등록된 사용자 정의 속성은 계산 값 시점에만 구문이 적용됩니다.
따라서 모든 사용자 정의 속성은, 등록 여부와 관계없이, @supports 규칙에서 "true"로 판정됩니다. 단, 사용자 정의 속성의 (매우 자유로운) 일반 구문을 위반하지 않아야 합니다.
syntax : "<color>" ;
로
등록되어 있더라도,
@supports (--foo: 1em) {...}
같은 규칙은 true로 평가되어 스타일이 적용됩니다.
해당 선언이 실제로 유효한 속성으로 성공적으로 파싱되기 때문입니다.
2.7. var()를 통한 치환
등록되지 않은 사용자 정의 속성처럼, 등록된 사용자 정의 속성의 값도 var() 함수로 다른 값에 치환할 수 있습니다. 하지만 등록된 사용자 정의 속성은 원래 토큰 시퀀스가 아니라 계산 값으로서 치환됩니다.
등록된 사용자 정의 속성을 참조하는 var() 함수는 반드시 동등한 토큰 시퀀스로 대체되어야 하며, 이는 계산 값이 직렬화된 후 토큰화된 문자열과 동일합니다.
div{ font-size : 10 px ; --x : 8 em ; --y : var ( --x); }
--x의 계산 값(직렬화 시)이 "80px"이므로, --y의 계산 값은 값이 "80"이고 단위가 "px"인 <dimension-token>입니다.
2.7.1. var() 참조의 폴백
var() 함수를 사용해 등록된 사용자 정의 속성을 참조할 때는 폴백을 제공할 수 있습니다. 하지만 폴백 값은 참조하는 사용자 정의 속성의 구문 정의와 일치해야 하며, 그렇지 않으면 선언이 계산 값 시점에 잘못된 값이 됩니다.
참고: 폴백이 실제로 사용되는지 여부와 관계없이 이 규칙이 적용됩니다.
2.7.2. 상대 단위를 통한 의존성 순환
등록된 사용자 정의 속성은 등록되지 않은 사용자 정의 속성과 동일한 의존성 순환 해결 규칙을 따르지만, 다음과 같은 추가 제약이 있습니다:
다음과 같은 구문 구성요소(<length> 또는 <length-percentage>)를 가진 등록된 사용자 정의 속성의 경우:
-
속성에 다음 단위 중 하나가 포함되면: em, ex, cap, ch, ic, lh; 속성과 현재 요소의 font-size 사이에 엣지를 추가합니다.
-
속성에 lh 단위가 포함되면, 속성과 현재 요소의 line-height 사이에 엣지를 추가합니다.
-
속성에 다음 단위 중 하나가 포함되면: rem, rlh; 속성과 루트 요소의 font-size 사이에 엣지를 추가합니다.
-
속성에 rlh 단위가 포함되면, 속성과 루트 요소의 line-height 사이에 엣지를 추가합니다.
CSS. registerProperty({ name: "--my-font-size" , syntax: "<length>" , initialValue: "0px" , inherits: false });
다음과 같이 하면 의존성 순환이 발생합니다:
div{ --my-font-size : 10 em ; font-size : var ( --my-font-size); }
그리고 font-size는 unset 값이 지정된 것처럼 동작합니다.
2.8. 섀도우 DOM
CSS의 많은 개념과 달리
(CSS Scoping 1 § 3.5 이름 정의 구조와
상속 참고),
속성 등록은 트리 범위에 한정되지 않습니다.
모든 등록은,
최상위 문서에 있든 섀도우 트리 내부에 있든,
Document
의
단일 글로벌 등록 맵에서 상호작용합니다.
왜 등록을 범위로 나눌 수 없나요?
속성 등록을 범위로 나누는 명확한 용례가 있습니다—섀도우 DOM을 사용하는 컴포넌트가 자체 내부 용도로 사용자 정의 속성을 등록할 때, 외부 페이지에서 등록을 알 필요가 없으므로 외부 페이지가 해당 속성 등록을 볼 필요가 없습니다.
하지만 등록을 범위로 한정하지 않는 이유도 있습니다—사용자 정의 속성은 컴포넌트로 데이터를 전달하는 데 사용되며, 외부 페이지가 해당 사용자 정의 속성을 설정하고 그 등록에 의해 구문 검사를 받는 것이 유용합니다; 마찬가지로 속성의 초기값 같은 개념도 속성 등록이 전역에 존재해야 의미가 있습니다. 그래야 속성이 문서의 루트에서도 적용됩니다.
하지만 위의 내용은 등록 범위가 제어 가능해야 한다는 의미이지, 반드시 글로벌이어야 한다는 의미는 아닙니다.
등록이 반드시 글로벌이어야 하는 이유는, 요소가 동시에 여러 트리 범위에 존재할 수 있기 때문입니다. 각 트리 범위의 스타일이 서로 섞이고 함께 캐스케이드됩니다. 이는 외부 트리에 존재하면서도 섀도우 트리에서 스타일 지정이 가능한 host 요소에도 적용되며, :host 선택자를 통해 섀도우 트리에서 스타일링할 수 있습니다. 또한 외부 트리에서 ::part() 의사 요소를 통해 섀도우 DOM 내부 요소에 스타일을 적용할 수도 있습니다.
등록이 트리 범위별로 한정될 수 있고, 하나의 속성이 내부와 외부 모두에서 등록된다면, 어떤 등록을 적용해 값을 파싱해야 할지 명확하지 않습니다. 값이 어떤 트리에서 왔는지 추적하고(다른 트리 범위 값에 대해 하는 것처럼) 해당 등록을 적용한다 해도, 섀도우 DOM이 특정 값 공간을 기대하다가 외부 트리가 캐스케이드에서 이겨서 완전히 다른 등록을 적용받으면 의도와 다른 결과가 나타날 수 있습니다.
사용자 정의 속성이 섀도우 DOM 기반 컴포넌트의 공개 API 일부로 노출되는 경우, 이 글로벌 등록 동작은 의도대로 동작합니다. 외부 페이지가 동일한 이름의 사용자 정의 속성을 다른 목적으로 사용하면, 이미 충돌이 발생하는 상황이므로 등록 동작이 문제를 더 악화시키지는 않습니다.
컴포넌트의 내부 전용 목적으로 사용자 정의 속성을 사용할 경우, 다른 컨텍스트와의 충돌 가능성을 최소화하기 위해 속성에 고유할 가능성이 높은 이름을 사용하는 것이 권장됩니다. 예를 들어, 프로젝트 이름이나 짧은 임의 문자열을 속성 이름에 포함시키는 방법이 있습니다.
3. @property 규칙
@property 규칙은 JS를 실행하지 않고도
스타일시트에서 직접 사용자 정의 속성 등록 정보를 나타냅니다.
유효한 @property 규칙은 등록된 사용자 정의 속성이 되며,
registerProperty()
가
동일한 매개변수로 호출된 것과 동일하게 동작합니다.
@property의 구문은 다음과 같습니다:
@property <custom-property-name> { <declaration-list> }
유효한 @property 규칙은 사용자 정의 속성 등록 정보를 나타내며, 속성 이름은 규칙의 프렐루드에 있는 <custom-property-name>의 직렬화 값입니다.
@property 규칙에는 syntax 및 inherits 디스크립터가 반드시 필요합니다; 둘 중 하나라도 없으면 전체 규칙은 무효이며 무시되어야 합니다. initial-value 디스크립터는 구문이 범용 구문 정의일 때만 선택적으로 생략할 수 있고, 그 외에는 반드시 필요합니다; 없으면 전체 규칙은 무효이며 무시되어야 합니다.
알 수 없는 디스크립터는 무효이며 무시되지만, @property 규칙 자체를 무효화하지는 않습니다.
참고: § 2.1 등록 여부
결정에서 명시된 것처럼,
동일한 <custom-property-name>에 대해
여러 개의 유효한 @property 규칙이 정의되어 있으면,
스타일시트 순서상 마지막 규칙이 "승리"합니다.
CSS.registerProperty()
로
등록된 정보는
동일한 <custom-property-name>에 대해
어떠한 @property 규칙보다 우선합니다.
3.1. syntax 디스크립터
이름: | syntax |
---|---|
대상: | @property |
값: | <string> |
초기값: | n/a (본문 참조) |
@property 규칙이 나타내는 사용자 정의 속성 등록 정보의 구문을 지정하며, 속성 값이 계산 값 시점에 어떻게 파싱되는지 제어합니다.
syntax 디스크립터는 @property 규칙이 유효하려면 반드시 필요합니다; 없으면 @property 규칙은 무효입니다.
제공된 문자열이 구문 문자열로 유효하지 않으면 (해당 문자열에 대해 구문 정의 소비를 호출하면 실패가 반환되는 경우), 해당 디스크립터는 무효이며 무시되어야 합니다.
3.2. inherits 디스크립터
이름: | inherits |
---|---|
대상: | @property |
값: | true | false |
초기값: | n/a (본문 참조) |
custom property registration의 상속 플래그를 지정하며, @property 규칙을 통해 속성이 기본적으로 상속되는지 여부를 제어합니다.
inherits 디스크립터는 @property 규칙이 유효하려면 반드시 필요합니다; 없으면 @property 규칙은 무효입니다.
3.3. initial-value 디스크립터
이름: | initial-value |
---|---|
대상: | @property |
값: | <declaration-value>? |
초기값: | guaranteed-invalid value (본문 참조) |
custom property registration의 초기값을 지정하며, @property 규칙을 통해 속성의 초기값을 제어합니다.
syntax 디스크립터의 값이 범용 구문 정의라면, initial-value 디스크립터는 선택사항입니다. 생략된 경우 속성의 초기값은 guaranteed-invalid value입니다.
그 외의 경우, syntax 디스크립터의 값이 범용 구문 정의가 아니라면, @property 규칙이 유효하려면 다음 조건을 만족해야 합니다:
-
initial-value 디스크립터가 반드시 존재해야 합니다.
-
initial-value 디스크립터의 값은 해당 구문 정의의 문법에 따라 성공적으로 파싱되어야 합니다.
-
initial-value는 계산적으로 독립적이어야 합니다.
위 조건을 만족하지 않으면 @property 규칙은 무효입니다.
4. JS에서 사용자 정의 속성 등록
JS를 통해 사용자 정의 속성을 등록하려면,
CSS
객체에 registerProperty()
메서드가 추가됩니다:
dictionary PropertyDefinition {required DOMString name ;DOMString syntax = "*";required boolean inherits ;DOMString initialValue ; };partial namespace CSS {undefined registerProperty (PropertyDefinition ); };
definition
추가적으로, Document
객체에는 [[registeredPropertySet]]
라는 새로운 private 슬롯이 추가되며,
이는 등록된 사용자 정의 속성을 설명하는 레코드 집합입니다.
4.1.
registerProperty()
함수
registerProperty(PropertyDefinition definition)
메서드는
definition
에 지정된 설정 옵션에 따라 사용자 정의 속성을 등록합니다.
호출될 때,
definition
인자의 옵션들을 동일한 이름의 인자로 넘겨
register
a custom property 알고리즘을 실행합니다.
-
property set을 현재 글로벌 객체의 연결된
Document
의[[registeredPropertySet]]
슬롯 값으로 설정합니다. -
name이 custom property name string이 아니면, throw
SyntaxError
하고 알고리즘을 종료합니다.property set에 name과 동일한 property name(코드포인트 비교)이 이미 있으면, throw
InvalidModificationError
하고 알고리즘을 종료합니다. -
syntax에서 구문 정의 소비를 시도합니다. 실패하면 throw
SyntaxError
하고 알고리즘을 종료합니다. 성공하면 반환된 syntax definition을 syntax definition으로 설정합니다. -
syntax definition이 범용 구문 정의이고, initialValue가 존재하지 않으면, parsed initial value를 비어있음으로 설정합니다. 이는 [css-variables]에 정의된 사용자 정의 속성의 "기본" 초기값과 동일하게 처리되어야 합니다. 알고리즘의 다음 단계로 이동합니다.
그 외에, syntax definition이 범용 구문 정의이면, parse initialValue를 <declaration-value>로 파싱합니다. 실패하면 throw
SyntaxError
하고 알고리즘을 종료합니다. 성공하면 parsed initial value를 파싱 결과로 설정합니다. 알고리즘의 다음 단계로 이동합니다.그 외에 initialValue가 존재하지 않으면, throw
SyntaxError
하고 알고리즘을 종료합니다.그 밖에는 parse
initialValue
를 syntax definition에 따라 파싱합니다. 실패하면 throwSyntaxError
하고 알고리즘을 종료합니다.성공하면 parsed initial value를 파싱 결과로 설정합니다. parsed initial value가 계산적으로 독립적이 아니면, throw
SyntaxError
하고 알고리즘을 종료합니다. -
inherit flag를 inherits의 값으로 설정합니다.
-
registered property를 struct로 만들되, property name에 name, syntax에 syntax definition, initial value에 parsed initial value, inherit flag에 inherit flag를 설정합니다. Append registered property를 property set에 추가합니다.
속성 값이 계산적으로 독립적이란, 오직 해당 요소의 속성 값과 "CSS로 변경할 수 없는" 글로벌 정보만으로 계산 값으로 변환할 수 있는 경우를 의미합니다.
반면, 3em은 계산적으로 독립적이 아닙니다. 해당 요소(또는 부모)의 font-size 값에 의존하기 때문입니다. var() 함수가 있는 값도 마찬가지로, 사용자 정의 속성의 값에 의존하기 때문입니다.
사용자 정의 속성이 특정 타입으로 등록되면, 해당 속성의 지정 값이 계산 값으로 변환되는 과정은 선택된 타입에 의해 완전히 정의되며, § 2.4 계산 값 처리 동작에 설명되어 있습니다.
참고: 속성을 등록 해제하는 방법은 추후 추가될 수 있습니다.
사용자 정의 속성 등록은 cascade에 절대 영향을 주지 않아야 합니다. 등록된 속성에 어떤 구문이 지정되었든, 파싱 시점에는 여전히 사용자 정의 속성의 일반 파싱 방식으로 처리되며, 거의 모든 값을 허용합니다. specified value가 등록된 구문을 위반하면, 해당 속성은 계산 값 시점에 잘못된 값이 되어 등록된 초기값으로 리셋됩니다.
.thing{ --my-color : green; --my-color : url ( "not-a-color" ); color : var ( --my-color); }
"thing" 클래스 요소의 color 속성이 inherit으로 설정됩니다. 두 번째 --my-color 선언이 첫 번째 선언을 파싱 시점에 덮어쓰고(둘 다 유효함), var() 참조가 color 속성에서 계산 값 시점에 잘못된 값으로 판정되어(url("not-a-color")이 color가 아니기 때문), CSS 파이프라인의 이 단계(계산 시점)에서는 속성의 초기값만 폴백으로 사용됩니다. color 속성의 경우 inherit입니다. 유효한 값(green)이 있었지만, URL이 덮어써서 파싱 시점에 제거되었습니다.
다음과 같이 호출하면:
CSS. registerProperty({ name: "--my-color" , syntax: "<color>" , initialValue: "black" , inherits: false });
등록이 위 스타일시트보다 먼저든 나중이든 파싱 방식에 큰 변화는 없습니다. 다른 점은 --my-color 속성이 계산 값 시점에 잘못된 값이 되어 초기값 black으로 설정된다는 점입니다. 그 후 color 속성은 black으로 유효하게 설정되며, 계산 값 시점에 잘못된 값이 되어 inherit이 되는 것이 아닙니다.
4.2.
PropertyDefinition
딕셔너리
PropertyDefinition
딕셔너리는
사용자 정의 속성에 대해 저자가 지정한 설정 옵션을 나타냅니다. PropertyDefinition
딕셔너리에는 다음 멤버가 포함됩니다:
name
, 타입 DOMString-
정의되는 사용자 정의 속성의 이름입니다.
syntax
, 타입 DOMString, 기본값"*"
-
이 사용자 정의 속성이 어떻게 파싱되는지 나타내는 문자열입니다.
inherits
, 타입 boolean-
이 사용자 정의 속성이 DOM 트리에서 상속되어야 한다면 true, 그렇지 않으면 false입니다.
initialValue
, 타입 DOMString-
이 사용자 정의 속성의 초기값입니다.
5. 구문 문자열
구문 문자열은 등록된 사용자 정의 속성이 허용하는 값 타입을 설명합니다. 구문 문자열은 구문 구성요소 이름으로 이루어져 있으며, 선택적으로 반복자와 조합자로 결합할 수 있습니다.
구문 문자열은 구문 정의로 파싱될 수 있으며, 이는 다음 중 하나입니다:
-
구문 구성요소 리스트. 각각 § 5.1 지원되는 이름에서 지정된 값 타입을 허용합니다.
참고: 지정된 구문과 관계없이 모든 사용자 정의 속성은 CSS 전체 키워드를 허용하며, 이에 맞게 값을 처리합니다.
"<length>"
-
길이 값을 허용합니다
"<length> | <percentage>"
-
길이, 백분율, 백분율 calc 표현식, 길이 calc 표현식을 허용하지만 길이와 백분율이 혼합된 calc 표현식은 허용하지 않습니다.
"<length-percentage>"
-
"<length> | <percentage>"
가 허용하는 모든 값과, 길이와 백분율이 혼합된 calc 표현식도 허용합니다. "big | bigger | BIGGER"
-
ident
big
,bigger
,BIGGER
중 하나를 허용합니다. "<length>+"
-
공백으로 구분된 길이 값 리스트를 허용합니다.
- "*"
-
모든 유효한 토큰 스트림을 허용합니다
참고: 구문 문자열의 내부 문법은 CSS 값 정의 문법의 부분집합입니다. 향후 명세에서는 허용되는 문법의 복잡성이 더 확대되어, 사용자 정의 속성이 CSS 속성 전체와 더 유사하게 동작할 수 있을 것입니다.
이 장의 나머지 부분에서는 구문 문자열의 내부 문법을 설명합니다.
5.1. 지원되는 이름
이 섹션에서는 지원되는 구문 구성요소 이름과, 결과적으로 생성되는 구문 구성요소가 허용하는 타입을 정의합니다.
- "<length>"
-
모든 유효한 <length> 값
- "<number>"
-
<number> 값
- "<percentage>"
-
모든 유효한 <percentage> 값
- "<length-percentage>"
-
모든 유효한 <length> 또는 <percentage> 값, 그리고 <calc()> 표현식에서 <length>와 <percentage> 구성요소를 결합한 값.
- "<color>"
-
모든 유효한 <color> 값
- "<image>"
-
모든 유효한 <image> 값
- "<url>"
-
모든 유효한 <url> 값
- "<integer>"
-
모든 유효한 <integer> 값
- "<angle>"
-
모든 유효한 <angle> 값
- "<time>"
-
모든 유효한 <time> 값
- "<resolution>"
-
모든 유효한 <resolution> 값
- "<transform-function>"
-
모든 유효한 <transform-function> 값
- "<custom-ident>"
-
모든 유효한 <custom-ident> 값
- 식별자 시작이 가능한 시퀀스, 이름으로 소비 가능하고, <custom-ident> 생산식에 일치하는 경우
-
그 식별자
참고: <custom-ident>들은 서로 코드포인트 단위로 비교합니다; 이는 UA 정의 CSS의 일반 동작(ASCII만 제한, ASCII 대소문자 무시)과 다릅니다. 예를 들어,
Red
를 지정하면 정확히 그 값만 허용되며 Red, red, RED 등은 일치하지 않습니다. CSS 규칙과 맞추기 위해 ident는 ASCII로 제한하고 소문자로 작성하는 것이 권장됩니다. - "<transform-list>"
-
유효한 <transform-function> 값 리스트.
"<transform-list>"
는 사전-반복된 데이터 타입 이름으로"<transform-function>+"
와 동일합니다.
참고: 구문 문자열 "*"
는 범용 구문 정의를 생성하며, 이는 구문 구성요소가 아닙니다.
따라서 "*"
는 어떤 것과도 반복자나 조합자로 결합할 수 없습니다.
5.2. '+' 및 '#' 반복자
구문 구성요소 이름 중 사전-반복된 데이터 타입 이름을 제외한 모든 이름은 반복자를 바로 뒤에 붙일 수 있습니다:
- U+002B 플러스 기호 (+)
-
공백으로 구분된 리스트를 나타냅니다.
- U+0023 샵 기호 (#)
-
콤마로 구분된 리스트를 나타냅니다.
참고: 반복자는 반드시 반복될 구문 구성요소 이름 바로 뒤에 붙어야 합니다.
5.3. '|' 조합자
구문 문자열은 U+007C 세로줄 (|)로 여러 구문 구성요소 이름을 제공할 수 있습니다. 이런 구문 문자열은 구문 정의에서 여러 구문 구성요소를 가지게 됩니다.
여러 구문 구성요소가 있는 구문 정의로 CSS 값을 파싱할 때, 지정된 순서대로 구성요소를 매칭합니다.
참고: 예를 들어, 구문 문자열 "red | <color>"
에서는,
값 red는 식별자로 파싱되고,
값 blue는 <color>로
파싱됩니다.
"<length> | auto"
-
길이 또는 auto를 허용합니다
"foo | <color># | <integer>"
-
foo, 콤마로 구분된 색상 값 리스트, 또는 하나의 정수를 허용합니다
5.4. 구문 문자열 파싱
5.4.1. 정의
- 데이터 타입 이름
-
U+003C LESS-THAN SIGN (<)으로 시작하고 0개 이상의 ident 코드포인트로 이어지며, U+003E GREATER-THAN SIGN (>)으로 끝나는 코드포인트 시퀀스입니다.
- 사전-반복된 데이터 타입 이름
- 구문 구성요소
-
구문 구성요소 이름과 선택적으로 반복자로 구성된 객체입니다.
- 구문 구성요소 이름
-
코드포인트 시퀀스로, 데이터 타입 이름 또는 <custom-ident>을 생성할 수 있는 시퀀스입니다.
- 구문 정의
-
구문 구성요소 리스트로 구성된 객체입니다.
- 범용 구문 정의
-
모든 유효한 토큰 스트림을 허용하는 특수 구문 정의입니다.
5.4.2. 구문 정의 소비
-
ASCII 앞뒤 공백 제거를 string에 적용합니다.
-
string의 길이가 0이면, 실패를 반환합니다.
-
string의 길이 가 1이고, 유일한 코드포인트가 U+002A ASTERISK (*)라면, 범용 구문 정의를 반환합니다.
-
stream을 입력 스트림으로 생성합니다. 코드포인트로 string을 변환하고, [css-syntax-3]에 지정된 대로 전처리합니다. definition을 리스트로 초기화합니다. 구문 구성요소만 포함합니다.
-
구문 구성요소 소비를 stream에서 실행합니다. 실패하면 실패를 반환합니다; 그렇지 않으면 반환된 값을 리스트에 추가합니다.
공백을 가능한 만큼 stream에서 소비합니다.
stream의 다음 입력 코드포인트를 소비합니다:
- EOF
-
definition을 반환합니다.
- U+007C 세로줄 (|)
-
5단계를 반복합니다.
- 그 외:
-
실패를 반환합니다.
5.4.3. 구문 구성요소 소비
stream에서 가능한 한 많은 공백을 소비합니다.
component를 새 구문 구성요소로 생성하되, name과 multiplier는 처음엔 비워둡니다.
stream에서 다음 입력 코드포인트를 소비합니다:
- U+003C LESS-THAN SIGN (<)
-
데이터 타입 이름 소비를 stream에서 수행합니다. 반환값이 string이면 component의 name에 그 값을 할당합니다. 그렇지 않으면 실패를 반환합니다.
- ident-start 코드포인트
- U+005C REVERSE SOLIDUS (\)
-
스트림이 ident 시퀀스로 시작하면, 현재 입력 코드포인트를 다시 사용한 뒤, ident 시퀀스 소비를 stream에서 수행하고, component의 name에 반환값을 할당합니다. 그렇지 않으면 실패를 반환합니다.
component의 name이 <custom-ident>으로 파싱되지 않으면 실패를 반환합니다.
- 그 외
-
실패를 반환합니다.
component의 name이 사전-반복된 데이터 타입 이름이면, component를 반환합니다.
stream에서 다음 입력 코드포인트가 U+002B PLUS SIGN (+) 또는 U+0023 NUMBER SIGN (#)이면, 다음 입력 코드포인트를 stream에서 소비하고, component의 multiplier에 현재 입력 코드포인트를 할당합니다.
component를 반환합니다.
5.4.4. 데이터 타입 이름 소비
참고: 이 알고리즘은 U+003C LESS-THAN SIGN (<) 코드포인트가 이미 스트림에서 소비되었다고 가정합니다.
name을 U+003C LESS-THAN SIGN (<) 코드포인트 하나만 포함하는 문자열로 초기화합니다.
다음 입력 코드포인트를 반복적으로 소비합니다:
- U+003E GREATER-THAN SIGN (>)
-
코드포인트를 name에 추가합니다. name이 지원되는 구문 구성요소 이름이면 name을 반환, 그렇지 않으면 실패를 반환합니다.
- ident 코드포인트
-
코드포인트를 name에 추가합니다.
- 그 외
-
실패를 반환합니다.
6. CSSOM
[[registeredPropertySet]]
슬롯을 무시하고,
모든 사용자 정의 속성을 등록되지 않은 것으로 처리해야 합니다. 6.1.
CSSPropertyRule
인터페이스
CSSPropertyRule
인터페이스는 @property 규칙을 표현합니다.
[Exposed =Window ]interface CSSPropertyRule :CSSRule {readonly attribute CSSOMString name ;readonly attribute CSSOMString syntax ;readonly attribute boolean inherits ;readonly attribute CSSOMString ?initialValue ; };
name
, 타입 CSSOMString, 읽기 전용- @property 규칙과 연관된 사용자 정의 속성 이름.
syntax
, 타입 CSSOMString, 읽기 전용- @property와 연관된 구문(명시된 그대로).
inherits
, 타입 boolean, 읽기 전용- @property 규칙과 연관된 inherits 디스크립터.
initialValue
, 타입 CSSOMString, 읽기 전용, nullable- @property 규칙과 연관된 초기값(없을 수도 있음).
-
문자열
"@property"
와 단일 공백(U+0020)을 반환. -
규칙의 name에 식별자 직렬화를 수행한 결과와 단일 공백(U+0020)을 반환.
-
문자열
"{ "
, 즉 왼쪽 중괄호(U+007B)와 공백(U+0020)를 반환. -
문자열
"syntax:"
와 단일 공백(U+0020)을 반환. -
문자열
"inherits:"
와 단일 공백(U+0020)을 반환. -
규칙의 inherits 속성에 따라 다음 중 하나를 반환:
- true
-
문자열
"true"
, 세미콜론(U+003B), 공백(U+0020)을 반환. - false
-
문자열
"false"
, 세미콜론(U+003B), 공백(U+0020)을 반환.
-
규칙의 initial-value가 존재한다면, 다음을 수행:
-
문자열
"initial-value:"
를 반환. -
규칙의 initial-value에 CSS 값 직렬화를 수행한 결과와 세미콜론(U+003B), 공백(U+0020)을 반환.
-
-
오른쪽 중괄호(U+007D)를 반환.
6.2.
CSSStyleValue
실체화
지정 값은 구성요소 값 리스트 실체화를 수행하고 결과를 반환.
계산 값의 경우:
-
값이 <length>, <integer>, <number>, <angle>, <time>, <resolution>, <percentage> 또는 <length-percentage>이면, 숫자 값 실체화를 수행하고 결과 반환.
-
값이 <transform-function>이면 <transform-function> 실체화를 수행하고 결과 반환.
-
값이 <transform-list>이면 <transform-list> 실체화를 수행하고 결과 반환.
-
값이 <image>이면 CSSImageValue로 실체화하여 결과 반환.
-
값이 identifier면 identifier 실체화를 수행하고 결과 반환.
-
syntax가 범용 구문 정의이면 구성요소 값 리스트 실체화를 수행하고 결과 반환.
-
그 외에는 CSSStyleValue로 실체화하되
[[associatedProperty]]
내부 슬롯을 property로 설정하고 결과 반환.
7. 예시
7.1. 예시 1: 사용자 정의 속성을 사용한 애니메이션 동작 추가
< script > CSS. registerProperty({ name: "--stop-color" , syntax: "<color>" , inherits: false , initialValue: "rgba(0,0,0,0)" }); </ script > < style > . button { --stop-color : red ; background : linear-gradient( var ( --stop-color ), black ); transition : -- stop-color 1 s ; } . button : hover { --stop-color : green ; } </ style >
7.2. 예시 2: @property를 사용한 속성 등록
< script > CSS. paintWorklet. addModule( 'circle.js' ); </ script > < style > @ property --radius { syntax : "<length>" ; inherits : false ; initial-value : 0px ; } div { width : 100 px ; height : 100 px ; --radius : 10 px ; background : paint ( circle ); transition : -- radius 1 s ; } div : hover { --radius : 50 px ; } </ style > < div ></ div >
// circle.js registerPaint( 'circle' , class { static get inputProperties() { return [ '--radius' ]; } paint( ctx, geom, properties) { let radius= properties. get( '--radius' ). value; ctx. fillStyle= 'black' ; ctx. beginPath(); ctx. arc( geom. width/ 2 , geom. height/ 2 , radius, 0 , 2 * Math. PI); ctx. fill(); } });
8. 보안 고려 사항
이 기능들로 인해 알려진 보안 이슈는 없습니다.
9. 프라이버시 고려 사항
이 기능들로 인해 알려진 프라이버시 이슈는 없습니다.
10. 변경 사항
10.1. 2020년 10월 13일 워킹 드래프트 이후 변경 사항
/* 2024년 3월 20일까지 */
-
"initial-value"가 <declaration-value>?를 가지도록 변경, 사용자 정의 속성과 동일하게 함. (#9078)
-
Shadow 트리에서도 @Property 허용. (#1085)
-
속성 등록이 글로벌(전역)이고 shadow-scoped가 아닌 이유를 설명하는 섹션 추가.
-
다른 명세서에서 사용할 수 있도록 "registered custom property"와 "universal syntax definition" 용어를 내보냄. (#1020)
-
"guaranteed-invalid value" 대신 "invalid at computed-value time" 용어 사용.