작성자 정의 CSS 이름 및 Shadow DOM: 사양 및 실제

작성자가 정의한 CSS 이름과 Shadow DOM은 함께 작동해야 합니다. 그러나 브라우저는 사양과 일치하지 않으며 경우에 따라 서로 일치하지도 않으며 모든 CSS 이름은 약간 다른 방식으로 일치하지 않습니다.

이 도움말에서는 작성자가 정의한 CSS 이름이 섀도우 범위에서 작동하는 방식의 현재 상태를 문서화하여 가까운 시일 내에 상호 운용성을 개선하는 데 도움이 되기를 바랍니다.

작성자가 정의한 CSS 이름이란 무엇인가요?

작성자 정의 CSS 이름은 상대적으로 오래된 CSS 문법 메커니즘으로, 원래 @keyframes 규칙용으로 도입되었으며 <keyframe-name>를 맞춤 식별자 또는 문자열로 정의합니다. 이 개념의 목적은 스타일시트의 한 부분에서 무언가를 선언하고 다른 부분에서 이를 참조하는 것입니다.

/* "fade-in" is a CSS name, representing a set of keyframes */
@keyframes fade-in {
  from { opacity: 0 };
  to { opacity: 1 }
}

.card {
  /* "fade-in" is a reference to the above keyframes */
  animation-name: fade-in;
}

CSS 이름을 사용하는 다른 CSS 기능으로는 글꼴, 속성 선언, 컨테이너 쿼리, 최근에는 뷰 전환, 앵커 위치 지정, 스크롤 기반 애니메이션이 있습니다. 다음 표에는 Chrome에서 상태를 확인하는 이름이 포함되어 있습니다(일부만 포함).

기능 이름 선언 이름 참조
키프레임 @keyframes animation-name
글꼴 @font-face { }
@font-palette-values
font-family
font-palette
속성 선언 @property
등록되지 않은 맞춤 속성 선언
var()
전환 보기 view-transition-name
view-transition-class
::view-transition-* 가상 요소
앵커 위치 지정 anchor-name position-anchor
스크롤 기반 애니메이션 view-timeline-name
scroll-timeline-name
animation-timeline
목록 스타일 @counter-style list-style
카운터 counter-reset
counter-set
counter-increment
컨테이너 쿼리 container-name @container
페이지 page @page

표에서 볼 수 있듯이 CSS 이름에는 일반적으로 상응하는 CSS 참조가 있습니다. 예를 들어 animation-name@keyframes 이름에 대한 참조입니다. CSS 이름은 속성 및 태그 이름과 같이 DOM에 정의된 이름과 다릅니다. CSS 이름은 선언된 후 스타일시트 컨텍스트 내에서 참조되기 때문입니다.

이름과 섀도우 DOM의 관계

CSS 이름은 문서 또는 스타일시트의 여러 부분 간에 관계를 만들기 위해 빌드되지만 Shadow DOM은 그 반대로 작동하도록 빌드됩니다. 자체 네임스페이스가 있어야 하는 웹 구성요소 간에 관계가 유출되지 않도록 관계를 캡슐화합니다.

CSS 이름과 섀도 DOM을 함께 사용하면 웹 구성요소를 구성하는 환경이 유연하면서도 안정적일 만큼 제약되어야 합니다.

이론적으로는 좋습니다. 실제로 브라우저는 CSS 이름이 shadow DOM과 상호작용하는 방식에 있어서 동일한 브라우저의 기능 간에, 브라우저 전반에서, 기능과 사양 간에 일관되지 않습니다.

이름과 Shadow DOM이 함께 작동하는 방식

이 문제를 이해하려면 이론적으로 CSS의 이러한 부분이 어떻게 함께 작동해야 하는지 이해하는 것이 좋습니다.

일반 규칙

CSS 이름이 섀도우 트리에서 작동하는 방식에 관한 일반적인 규칙은 CSS 범위 지정 수준 1 사양에 정의되어 있습니다. 요약하면 CSS 이름은 정의된 범위 내에서 전역입니다. 즉, 하위 요소 그림자 트리에서 액세스할 수 있지만 형제 요소 또는 상위 요소 그림자 트리에서는 액세스할 수 없습니다. 이는 동일한 트리 범위 내에 캡슐화된 요소 ID와 같은 웹 플랫폼의 이름과는 다릅니다.

규칙 예외: @property

다른 CSS 이름과 달리 CSS 속성은 Shadow DOM으로 캡슐화되지 않습니다. 오히려 여러 그림자 트리 간에 매개변수를 전달하는 일반적인 수단입니다. 따라서 @property 설명자는 특별합니다. 특정 이름이 지정된 속성이 작동하는 방식을 정의하는 문서 전역 유형 선언처럼 작동해야 합니다. 속성은 그림자 트리 전체에서 일치해야 하므로 속성 선언이 일치하지 않으면 예상치 못한 결과가 발생하므로 @property 선언은 문서 순서에 따라 평면화되고 확인되도록 지정됩니다.

::part에서 규칙이 작동하는 방식

섀도우 부분은 섀도우 트리 내의 요소를 상위 트리에 노출합니다. 이렇게 하면 상위 트리가 해당 요소에 액세스하고 ::part 요소를 사용하여 스타일을 지정할 수 있습니다.

::part는 두 개의 트리 범위가 동일한 요소의 스타일을 지정하도록 허용하므로 다음과 같은 계단식 순서가 지정됩니다.

  1. 먼저 그림자 컨텍스트 내의 스타일을 확인합니다. 이는 부품의 '기본' 스타일입니다.
  2. 그런 다음 ::part에 정의된 대로 외부 스타일을 적용합니다. 이는 부품의 '맞춤설정된' 스타일입니다.
  3. 그런 다음 !important와 함께 정의된 모든 내부 스타일을 적용합니다. 이를 통해 맞춤 요소는 특정 부분의 특정 속성을 ::part에서 맞춤설정할 수 없다고 선언할 수 있습니다.

즉, ::part는 섀도 범위 스타일이 아닌 호스트 범위 스타일이므로 섀도 DOM 내의 이름을 ::part에서 참조할 수 없습니다. 예를 들면 다음과 같습니다.

// inside the shadow DOM:
@keyframes fade-in {
  from { opacity: 0}
}

// This shouldn't work!
// The host style shouldn't know the name "fade-in"
::part(slider) {
  animation-name: fade-in;  
}

인라인 스타일에서 규칙이 작동하는 방식

::part와 달리 style 속성이 있는 인라인 스타일 또는 스크립트를 사용하여 프로그래매틱 방식으로 스타일을 설정하는 스타일은 요소의 범위로 범위가 지정됩니다. 요소에 스타일을 적용하려면 요소 핸들에 액세스해야 하므로 섀도우 루트 자체에 액세스해야 합니다.

CSS 이름과 Shadow DOM이 실제로 함께 작동하는 방식

위의 규칙은 잘 정의되고 일관성이 있지만 현재 구현은 이를 항상 반영하지는 않습니다. 실제로 @property는 브라우저 간에 일관된 방식으로 사양과 다르게 작동하며, 다른 대부분의 기능에는 아직 해결되지 않은 버그가 있습니다 (일부는 아직 출시되지 않았으므로 수정할 시간이 있음).

이러한 기능이 실제로 작동하는 방식을 테스트하고 보여주기 위해 다음 페이지(https://css-names-in-the-shadow.glitch.me/)를 만들었습니다. 이 페이지에는 각각 기능 중 하나에 중점을 두고 6가지 시나리오를 테스트하는 여러 iframe이 있습니다.

  • 외부 이름에 대한 외부 참조: shadow DOM이 포함되지 않으므로 작동합니다.
  • 내부 이름에 대한 외부 참조: 이는 작동하지 않습니다. 섀도 컨텍스트에 정의된 이름이 유출되었음을 의미하기 때문입니다.
  • 외부 이름에 대한 내부 참조: 트리 범위 이름은 그림자 루트에서 상속되므로 작동합니다.
  • 내부 이름에 대한 내부 참조: 참조의 이름이 모두 동일한 범위에 있으므로 작동합니다.
  • ::part 외부 이름 참조: ::part와 이름이 모두 동일한 범위에서 선언되므로 작동합니다.
  • ::part 내부 이름 참조: 외부 범위는 섀도우 DOM 내에서 선언된 이름에 관한 지식을 얻지 못해야 하므로 작동하지 않습니다.

@keyframes

사양에 정의된 대로 @keyframes at-rule이 상위 요소 범위에 있는 한 그림자 루트 내에서 키프레임 이름을 참조할 수 있습니다. 실제로는 어떤 브라우저도 이 동작을 구현하지 않으며 키프레임 정의는 정의된 범위에서만 참조할 수 있습니다. 문제 10540을 참고하세요.

@property

사양에 정의된 대로 모든 @property 선언은 문서 범위로 평면화됩니다. 하지만 현재 모든 브라우저에서는 문서 범위에서만 @property를 선언할 수 있으며 그림자 루트 내의 @property 선언은 무시됩니다.
문제 10541을 참고하세요.

브라우저별 버그

다른 기능은 브라우저 간에 일관된 동작을 보여주지 않습니다.

  • @font-face는 Safari에서 루트 범위로 평면화됩니다.
  • Chromium이 섀도우 루트에서 anchor-name 규칙을 상속하지 않음
  • scroll-timeline-nameview-timeline-name::part (Chromium에서도)에서 올바르게 범위 지정되지 않았습니다.
  • 그림자 루트에서 @font-palette-values를 선언하는 것을 허용하는 브라우저는 없습니다.
  • view-transition-class는 그림자 루트 내에서 정의할 수 있습니다 (전환 자체는 그림자 루트 외부에 있음).
  • Firefox를 사용하면 ::part가 내부 그림자 이름 (컨테이너 쿼리, 키프레임)에 액세스할 수 있습니다.
  • Firefox 및 Safari는 섀도우 루트에서 @counter-style를 따르지 않습니다.

counter-reset, counter-set, counter-increment는 암시적 이름이므로 규칙이 약간 다르며 CSS 속성을 선언하면 잘 테스트되고 확립된 규칙 집합이 있습니다.

결론

안타까운 점은 CSS 이름 및 shadow DOM과 관련된 현재 상호 운용성 상태의 스냅샷을 검사할 때 환경이 일관되지 않고 버그가 있다는 것입니다. 여기에서 살펴본 기능 중 하나도 브라우저와 사양에 따라 일관되게 작동하지 않습니다. 다행인 점은 환경을 일관되게 만드는 데 필요한 사항이 버그 및 사양 문제의 한정된 목록이라는 것입니다. 이 문제를 해결해 보겠습니다. 그동안 이 도움말에 설명된 불일치로 어려움을 겪고 있다면 이 개요가 도움이 될 수 있습니다.