Os nomes de CSS definidos pelo autor e o DOM secundário devem funcionar juntos. No entanto, os navegadores são inconsistentes com a especificação, às vezes com cada outro, e cada nome de CSS é inconsistente de uma maneira um pouco diferente.
Neste artigo, documentamos o status atual de como os nomes CSS definidos pelo autor se comportam em escopos de sombra, com a esperança de que ele sirva como um guia para melhorar a interoperabilidade no futuro próximo.
O que são nomes CSS definidos pelo autor?
Os nomes de CSS definidos pelo autor são um mecanismo de sintaxe de CSS relativamente antigo, originalmente
introduzido para a regra @keyframes
, que define um <keyframe-name>
como
um identificador personalizado ou uma string. O objetivo desse conceito é declarar
algo em uma parte de uma folha de estilo e fazer referência a ele 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 a seguir, não abrangente, inclui nomes 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 declaração de propriedade personalizada não registrada |
var() |
Conferir transições | view-transition-name
view-transition-class |
Pseudoelementos ::view-transition-* |
Posicionamento da âncora | anchor-name |
position-anchor |
Animação gerada por rolagem | view-timeline-name
scroll-timeline-name |
animation-timeline |
Estilos de lista | @counter-style |
list-style |
Contadores | counter-reset
counter-set
counter-increment |
|
Consultas em contêiner | container-name |
@container |
Página | page |
@page |
Como você pode ver na tabela, um nome CSS geralmente tem uma referência CSS correspondente. Por exemplo, animation-name
é uma referência ao nome @keyframes
. Os nomes CSS são diferentes dos nomes definidos no DOM, como atributos
e nomes de tags, porque são declarados e referenciados no contexto das
folhas de estilo.
Como os nomes se relacionam com o shadow DOM
Enquanto os nomes CSS são criados para criar relações entre diferentes partes de um documento ou folha de estilo, o DOM oculto é criado para fazer o oposto. Ele encapsula relacionamentos para que eles não vazem em componentes da Web que devem ter o próprio namespace.
Ao unir nomes de CSS e o DOM de sombra, a experiência de composição de componentes da Web precisa ser suficientemente expressiva para ser flexível, mas restrita o suficiente para ser estável.
Isso é bom em teoria. Na prática, os navegadores são inconsistentes na forma como os nomes CSS interagem com o DOM sombra, tanto entre recursos no mesmo navegador quanto entre navegadores e entre os recursos e a especificação.
Como os nomes e o shadow DOM devem funcionar juntos
Para entender o problema, vale a pena entender como essas partes do CSS funcionam juntas na teoria.
A regra geral
A regra geral sobre como os nomes do CSS se comportam nas árvores de sombra é definida na especificação do nível 1 do escopo do CSS. Resumindo: um nome de CSS é global dentro do escopo em que é definido, ou seja, ele pode ser acessado de árvores shadow descendentes, mas não de árvores shadow irmãs ou ancestral. Observe que isso é diferente 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 de CSS, as propriedades de CSS não são encapsuladas pelo Shadow DOM.
Em vez disso, elas são o meio comum de transmitir parâmetros em diferentes árvores
de sombra.
Isso torna o
descritor @property
especial: ele precisa se comportar como uma declaração de tipo global do documento que
define como uma propriedade nomeada específica age. Como as propriedades precisam ser correspondentes
em árvores de sombra, a incompatibilidade de declarações de propriedade criaria resultados
inesperados. Portanto, as declarações de @property
são especificadas para serem achatadas e resolvidas
de acordo com a ordem do documento.
Como a regra funciona com ::part
As partes de sombra
expõem um elemento dentro de uma árvore de sombra para a árvore mãe. Ao fazer isso, a
árvore pai pode acessar esse elemento e também estilizá-lo usando o elemento
::part
.
Como ::part
permite que dois escopos de árvore estilizem o mesmo elemento, a seguinte
ordem em cascata é especificada:
- Primeiro, verifique o estilo dentro do contexto da sombra. Esse é o estilo "padrão" da parte.
- Em seguida, aplique o estilo externo conforme definido em
::part
. Esse é o estilo "personalizado" da peça. - Em seguida, aplique qualquer estilo interno definido com
!important
. Isso permite que um elemento personalizado declare que determinada propriedade de uma determinada parte não é personalizável por::part
.
Isso significa que os nomes do DOM shadow não podem ser referenciados por um
::part
, já que o ::part
é um estilo com escopo de host, e não um estilo com escopo
shadow. 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 funciona com estilos inline
Ao contrário do ::part
, os estilos inline com o atributo style
ou aqueles
que definem o estilo de forma programática usando o script têm o mesmo escopo do elemento. Isso ocorre porque, para aplicar um estilo a um elemento, é necessário ter acesso
ao identificador do elemento e, portanto, à raiz paralela.
Como os nomes de CSS e o DOM sombra funcionam juntos na prática
Embora as regras anteriores sejam bem definidas e consistentes, as implementações
atuais nem sempre refletem isso.
Na prática, @property
funciona de maneira diferente da especificação de maneira consistente
em todos os navegadores, e a maioria dos outros recursos tem bugs abertos. Alguns deles 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 a página a seguir: 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, isso deve funcionar.
- Referência externa a um nome interno: isso não funciona, porque significaria que o nome definido no contexto sombra vazou.
- Referência interna ao nome externo: isso deve funcionar, já que os nomes no escopo da árvore são herdados pelas raízes de sombra.
- Referência interna ao nome interno: isso deve funcionar, já que o nome da referência está no mesmo escopo.
- Referência de
::part
ao nome externo: isso deve funcionar, já que o::part
e o nome são declarados no mesmo escopo. - Referência
::part
ao nome interno: isso não funciona, porque o escopo externo não tem conhecimento sobre nomes declarados dentro do shadow DOM.
@keyframes
Conforme definido na especificação, você pode referenciar nomes de frames-chave
em uma raiz de sombra, desde que a regra @keyframes
at-rule esteja em um escopo
ancestral. Na prática, nenhum navegador implementa esse comportamento, e as definições de frame-chave
só podem ser referenciadas no escopo em que são definidas. Consulte
o problema 10540.
@property
Conforme definido na especificação, qualquer declaração de @property
será
agrupada no escopo do documento. No entanto, atualmente, em todos os navegadores, só é possível
declarar @property
no escopo do documento, e as declarações de @property
dentro
de raízes de sombra são ignoradas.
Consulte o problema 10541.
Bugs específicos do navegador
Os outros recursos não mostram um comportamento consistente em todos os navegadores:
@font-face
é achatado para o escopo raiz no Safari.- O Chromium não permite a herança de regras
anchor-name
em uma raiz sombra scroll-timeline-name
eview-timeline-name
não têm o escopo correto em::part
(também no Chromium).- Nenhum navegador permite declarar
@font-palette-values
em uma raiz de sombra. view-transition-class
pode ser definido dentro de uma raiz sombra (a transição está fora da raiz sombra).- O Firefox permite que o
::part
acesse nomes de sombras internos (consultas de contêiner, frames de chaves). - O Firefox e o Safari não respeitam
@counter-style
em uma raiz de sombra.
counter-reset
, counter-set
e counter-increment
têm regras um pouco
diferentes porque são nomes implícitos, e a declaração de propriedades CSS
tem 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 do CSS e ao DOM sombra, a experiência é inconsistente e com bugs. Nenhum dos recursos que examinamos aqui se comporta de maneira consistente em navegadores e de acordo com as especificações. A boa notícia é que o delta para tornar a experiência consistente é uma lista de bugs e problemas de especificação finita. Vamos corrigir isso. Enquanto isso, esta visão geral poderá ajudar se você estiver enfrentando as inconsistências descritas neste artigo.