Nomes CSS definidos pelo autor e shadow DOM: na especificação e na prática

Os nomes CSS definidos por autoria e o shadow DOM devem funcionar juntos. No entanto, os navegadores são inconsistentes com a especificação, às vezes com problemas e cada nome CSS é inconsistente de uma maneira um pouco diferente.

Este artigo documenta o status atual de como os nomes CSS definidos pelo autor se comportam nos escopos de sombra, na esperança de que possa servir de guia para melhorar e interoperabilidade em um futuro próximo.

O que são nomes CSS definidos pelo autor?

Os nomes CSS definidos pelo autor são um mecanismo de sintaxe CSS relativamente antigo, introduzido para a regra @keyframes, que define um <keyframe-name> como uma ident personalizada ou uma string. O objetivo desse conceito é declarar algo em uma parte de uma folha de estilo e referenciá-lo em outra parte.

/* "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;
}

Outros recursos CSS que usam nomes CSS são fontes, declarações de propriedade, consultas de contêiner e, mais recentemente, transições de visualização, posicionamento de âncoras e animações de rolagem. A tabela não abrangente a seguir inclui nomes em que o Chrome verifica o estado.

Recurso Declaração de nome Referência de nome
Frames-chave @keyframes animation-name
Fontes @font-face { }
@font-palette-values
font-family
font-palette
Declarações de propriedade @property Qualquer propriedade personalizada
Ver transição view-transition-name
view-transition-class
::view-transition-group()
Posicionamento da âncora anchor-name position-anchor
Animação de rolagem animation-timeline view-timeline-name
scroll-timeline-name
Estilo do contador @counter-style
Counter-reset
counter-set
counter-increment
list-style
Consultas em contêiner container-name @container
Variável CSS --something var(--something)
Página @page

Como você pode ver na tabela, um name de CSS geralmente tem um CSS correspondente referência. Por exemplo, animation-name é uma referência ao objeto @keyframes nome. Os nomes CSS são diferentes dos definidos no DOM, como atributos e nomes de tag, já que são declarados e depois referenciados dentro do contexto do as folhas de estilo.

Como os nomes se relacionam com o shadow DOM

Enquanto os nomes CSS são construídos para criar relações entre diferentes partes de uma documento ou folha de estilo, o Shadow DOM é criado para fazer o oposto. Ela encapsula as relações para que não vazem entre os componentes da Web que devem ter o próprio namespace.

Ao juntar os nomes CSS e o shadow DOM, a experiência de compor os componentes da Web devem parecer expressivos o suficiente para serem flexíveis, mas restritos o suficiente para se manter estável.

Na teoria, isso é bom. Na prática, os navegadores são inconsistentes na forma como o CSS interoperam com o shadow DOM entre recursos no mesmo navegador, entre navegadores e entre os recursos e a especificação.

Como os nomes e o shadow DOM devem funcionar juntos

Para entender o problema, é importante entender como essas partes do CSS devem funcionar juntos na teoria.

A regra geral

A regra geral de como os nomes CSS se comportam entre árvores paralelas é definida no arquivo Especificação de escopo do CSS nível 1. Para resumir: um nome CSS é global dentro do escopo em que é definido, ou seja, ele pode ser acessado por árvores paralelas descendentes, mas não por árvores-sombra ancestrais. Observe que são diferentes de nomes na plataforma da Web, como IDs de elementos, que são encapsulados no mesmo escopo de árvore.

Exceção à regra: @property

Ao contrário de outros nomes CSS, as propriedades CSS não são encapsuladas pelo shadow DOM. Em vez disso, são os meios comuns de transmitir parâmetros em diferentes árvores. Isso faz Descritor @property especial: deve se comportar como uma declaração de tipo "document-global" define como uma propriedade nomeada específica age. Como as propriedades precisam ser entre árvores paralelas, a incompatibilidade de declarações de propriedade criaria resultados, portanto, as declarações @property são especificadas para serem niveladas e resolvidas. de acordo com a ordem do documento.

Como a regra vai funcionar com ::part

Partes de sombra expor um elemento dentro de uma árvore paralela à árvore pai. Ao fazer isso, o árvore mãe pode acessar esse elemento e também estilizá-lo usando a ::part. .

Como ::part permite que dois escopos de árvore estilizem o mesmo elemento, o seguinte a ordem em cascata é especificada:

  1. Primeiro, verifique o estilo dentro do contexto da sombra. Essa é a configuração "padrão" o estilo da peça.
  2. Em seguida, aplique o estilo externo conforme definido em ::part. Esta é a "personalizado" o estilo da peça.
  3. Em seguida, aplique qualquer estilo interno definido com !important. Isso permite que um elemento personalizado declare que determinada propriedade de um determinado não é personalizável por ::part.

Isso significa que os nomes dentro do shadow DOM não podem ser referenciados em um ::part, já que ::part é um estilo com escopo de host em vez de com escopo de sombra. estilo. Exemplo:

// 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;  
}

Como a regra deve funcionar com estilos in-line

Diferente de ::part, estilos in-line com o atributo style ou aqueles definindo programaticamente o estilo usando script, têm o escopo do elemento tem o escopo definido. Isso porque, para aplicar um estilo a um elemento ao qual você precisa de acesso, na alça do elemento e, portanto, na própria raiz paralela.

Como os nomes CSS e o shadow DOM funcionam juntos na realidade

Embora as regras anteriores sejam bem definidas e consistentes, a abordagem nem sempre refletem isso. Na prática, @property funciona de maneira diferente da especificação de maneira consistente. em vários navegadores, e a maioria dos outros recursos apresenta bugs abertos (alguns deles estão que ainda não foram lançados, então há tempo para corrigi-los).

Para testar e demonstrar como esses recursos funcionam na prática, criamos o seguinte página: https://css-names-in-the-shadow.glitch.me/. Esta página tem vários iframes, cada um focado em um dos recursos e testando seis cenários:

  • Referência externa a um nome externo: nenhum shadow DOM envolvido, deve funcionam.
  • Referência externa a um nome interno: não funciona, porque significa que o nome definido no contexto sombra vazou.
  • Referência interna ao nome externo: funciona, como nomes com escopo de árvore. são herdados por raízes sombra.
  • Referência interna ao nome interno: deve funcionar como o nome do bloco estão no mesmo escopo.
  • Referência de ::part ao nome externo: precisa funcionar, já que o ::part e o nome sejam declarados no mesmo escopo.
  • Referência de ::part ao nome interno: não vai funcionar, como o escopo externo não devem adquirir conhecimento sobre nomes declarados dentro do shadow DOM.

@keyframes

Conforme definido na especificação, será possível fazer referência a nomes de frames-chave de uma raiz paralela, desde que a @keyframes esteja em um ancestral. do projeto. Na prática, nenhum navegador implementa esse comportamento, e o frame-chave as definições só podem ser referenciadas no escopo em que são definidas. Consulte problema 10540 (link em inglês).

@property

Conforme definido na especificação, qualquer declaração de @property será simplificada para o escopo do documento. Atualmente, no entanto, em todos os navegadores, só é possível declarar @property no escopo do documento e @property declarações no as raízes paralelas serão ignoradas.
Consulte o problema 10541.

Bugs específicos do navegador

Os outros recursos não apresentam um comportamento consistente nos navegadores:

  • @font-face é simplificado para o escopo raiz no Safari.
  • O Chromium não permite herdar regras @anchor-name em uma raiz paralela
  • O escopo de @scroll-timeline-name e @view-timeline-name não está definido corretamente no ::part (também no Chromium).
  • Nenhum navegador permite declarar @font-palette-values em raízes paralelas.
  • view-transition-class pode ser definido dentro de uma raiz paralela (a transição está fora da raiz paralela).
  • O Firefox permite que o ::part acesse nomes de sombras internos (consultas de contêiner, frames-chave).
  • O Firefox e o Safari não respeitam a @counter-style em uma raiz paralela.

counter-reset, counter-set e counter-increment têm um pouco de regras diferentes porque são nomes implícitos e declaram propriedades CSS ter um conjunto de regras estabelecido e bem testado.

Conclusão

A má notícia é que, ao examinar o instantâneo do estado de interoperabilidade atual, em relação aos nomes CSS e ao shadow DOM, a experiência é inconsistente e de bugs. Nenhum dos recursos que examinamos se comporta de forma consistente navegadores e de acordo com as especificações. A boa notícia é que o delta para tornar a experiência consistente é finito lista de bugs e problemas de especificação. Vamos corrigir isso. Enquanto isso, esta visão geral pode ajudar se você estiver com dificuldades e inconsistências descritas neste artigo.