Estilizando componentes da Web usando uma folha de estilos compartilhada

Publicados: 2022-03-10
Resumo rápido ↬ Os componentes da Web são um novo recurso incrível da Web, permitindo que os desenvolvedores definam seus próprios elementos HTML personalizados. Quando combinados com um guia de estilo, os componentes da Web podem criar uma API de componente, que permite que os desenvolvedores parem de copiar e colar trechos de código e, em vez disso, usem apenas um elemento DOM. Ao usar o shadow DOM, podemos encapsular o componente web e não precisamos nos preocupar com guerras de especificidade com qualquer outra folha de estilo na página. No entanto, componentes da web e guias de estilo parecem estar em desacordo entre si.

Os componentes da Web são um novo recurso incrível da Web, permitindo que os desenvolvedores definam seus próprios elementos HTML personalizados. Quando combinados com um guia de estilo, os componentes da Web podem criar uma API de componente, que permite que os desenvolvedores parem de copiar e colar trechos de código e, em vez disso, usem apenas um elemento DOM. Ao usar o shadow DOM, podemos encapsular o componente web e não precisamos nos preocupar com guerras de especificidade com qualquer outra folha de estilo na página.

No entanto, componentes da web e guias de estilo parecem estar em desacordo entre si. Por um lado, os guias de estilo fornecem um conjunto de regras e estilos que são aplicados globalmente à página e garantem consistência em todo o site. Por outro lado, os componentes da Web com o shadow DOM impedem que qualquer estilo global penetre em seu encapsulamento, evitando assim que o guia de estilo os afete.

Leitura adicional no SmashingMag:

  • Aplicação das melhores práticas em sistemas baseados em componentes
  • Como usar o pré-processador LESS CSS para folhas de estilo mais inteligentes
  • Um mergulho profundo no Adobe Edge Reflow

Então, como os dois podem coexistir, com guias de estilo globais continuando a fornecer consistência e estilos, mesmo para componentes da Web com o shadow DOM? Felizmente, existem soluções que funcionam hoje e mais soluções estão por vir, que permitem que os guias de estilo globais forneçam estilo aos componentes da web. (Para o restante deste artigo, usarei o termo “componentes da web” para me referir a elementos personalizados com o shadow DOM.)

Mais depois do salto! Continue lendo abaixo ↓

O que deve um estilo de guia de estilo global em um componente da Web?

Antes de discutir como obter um guia de estilo global para estilizar um componente da Web, devemos discutir o que ele deve ou não tentar estilizar.

Em primeiro lugar, as melhores práticas atuais para componentes da Web afirmam que um componente da Web, incluindo seus estilos, deve ser encapsulado, para que não dependa de nenhum recurso externo para funcionar. Isso permite que ele seja usado em qualquer lugar dentro ou fora do site, mesmo quando o guia de estilo não estiver disponível.

Abaixo está um componente web de formulário de login simples que encapsula todos os seus estilos.

 <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } p { margin: 0; } p + p { margin-top: 20px; } a { color: #1f66e5; } label { display: block; margin-bottom: 5px; } input[type="text"], input[type="password"] { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } input[type="submit"] { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <div class="container"> <form action="#"> <p> <label for="username">User Name</label> <input type="text" name="username"> </p> <p> <label for="password">Password</label> <input type="password" name="password"> </p> <p> <input type="submit" value="Login"> </p> <p class="footnote">Not registered? <a href="#">Create an account</a></p> </form> </div> </template> <script> const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); const root = this.attachShadow({mode: 'closed'}); const temp = document.importNode(template.content, true); root.appendChild(temp); } }); </script>

Nota: Os exemplos de código são escritos na especificação da versão 1 para componentes da web.

Formulário de login com nome de usuário e senha.
Um componente web de formulário de login simples

No entanto, encapsular totalmente todos os componentes da Web inevitavelmente levaria a muitos CSS duplicados, especialmente quando se trata de configurar a tipografia e o estilo de elementos nativos. Se um desenvolvedor quiser usar um parágrafo, uma marca de âncora ou um campo de entrada em seu componente da web, ele deve ser estilizado como o restante do site.

Se encapsularmos totalmente todos os estilos que o componente da Web precisa, o CSS para estilizar parágrafos, tags de âncora, campos de entrada e assim por diante seria duplicado em todos os componentes da Web que os utilizam. Isso não apenas aumentaria os custos de manutenção, mas também levaria a tamanhos de download muito maiores para os usuários.

Em vez de encapsular todos os estilos, os componentes da Web devem encapsular apenas seus estilos exclusivos e, em seguida, depender de um conjunto de estilos compartilhados para lidar com estilos para todo o resto. Esses estilos compartilhados se tornariam essencialmente um tipo de Normalize.css, que os componentes da Web poderiam usar para garantir que os elementos nativos fossem estilizados de acordo com o guia de estilo.

No exemplo anterior, o componente web do formulário de login declararia os estilos apenas para suas duas classes exclusivas: .container e .footnote . O restante dos estilos pertenceria à folha de estilos compartilhada e estilizaria os parágrafos, marcas de ancoragem, campos de entrada e assim por diante.

Em resumo, o guia de estilo não deve tentar estilizar o componente da Web, mas deve fornecer um conjunto de estilos compartilhados que os componentes da Web podem usar para obter uma aparência consistente.

Como estilizar o Shadow DOM com folhas de estilo externas costumava ser feito

A especificação inicial para componentes da web (conhecida como versão 0) permitia que qualquer folha de estilo externa penetrasse no shadow DOM através do uso dos seletores ::shadow ou /deep/ CSS. O uso de ::shadow e /deep/ permitiu que você tivesse um guia de estilo para penetrar no shadow DOM e configurar os estilos compartilhados, quer o componente da web quisesse ou não.

 /* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }

Com o advento da versão mais recente da especificação de componentes da Web (conhecida como versão 1), os autores removeram a capacidade das folhas de estilo externas de penetrar no shadow DOM e não forneceram nenhuma alternativa. Em vez disso, a filosofia mudou de usar dragões para estilizar componentes da Web para usar pontes. Em outras palavras, os autores de componentes da Web devem ser responsáveis ​​por quais regras de estilo externas podem estilizar seu componente, em vez de serem forçados a permiti-las.

Infelizmente, essa filosofia ainda não alcançou a web, o que nos deixa em apuros. Felizmente, algumas soluções disponíveis hoje, e algumas que virão em um futuro não tão distante, permitirão que uma folha de estilo compartilhada estilize um componente da web.

O que você pode fazer hoje

Existem três técnicas que você pode usar hoje que permitirão que um componente da Web compartilhe estilos: @import , elementos personalizados e uma biblioteca de componentes da Web.

Usando @import

A única maneira nativa hoje de trazer uma folha de estilo para um componente da web é usar @import . Embora isso funcione, é um antipadrão. Para componentes da web, no entanto, é um problema de desempenho ainda maior.

 <template> <style> @import "styleguide.css" </style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

Normalmente, @import é um antipadrão porque baixa todas as folhas de estilo em série, em vez de em paralelo, especialmente se estiverem aninhadas. Em nossa situação, baixar uma única folha de estilo em série não pode ser ajudado, então, em teoria, deve ser bom. Mas quando testei isso no Chrome, os resultados mostraram que usar @import fez com que a página fosse renderizada até meio segundo mais lenta do que quando apenas incorporava os estilos diretamente no componente da web.

Nota: Devido a diferenças em como o polyfill de importações de HTML funciona em comparação com as importações de HTML nativas, WebPagetest.org só pode ser usado para fornecer resultados confiáveis ​​em navegadores que suportam nativamente importações de HTML (ou seja, Chrome).

Gráfico de barras do desempenho do componente web nativo.
Os resultados de três testes de desempenho mostram que @import faz com que o navegador seja renderizado até meio segundo mais lento do que quando apenas incorpora os estilos diretamente no componente da web.

No final, @import ainda é um antipadrão e pode ser um problema de desempenho em componentes da web. Então, não é uma ótima solução.

Não use o Shadow DOM

Como o problema de tentar fornecer estilos compartilhados para componentes da Web decorre do uso do shadow DOM, uma maneira de evitar totalmente o problema é não usar o shadow DOM.

Ao não usar o shadow DOM, você estará criando elementos personalizados em vez de componentes da web (veja o lado abaixo), a única diferença é a falta do shadow DOM e do escopo. Seu elemento estará sujeito aos estilos da página, mas já temos que lidar com isso hoje, então não é nada que ainda não saibamos lidar. Elementos personalizados são totalmente suportados pelo polyfill webcomponentjs, que tem um ótimo suporte para navegadores.

O maior benefício dos elementos personalizados é que você pode criar uma biblioteca de padrões usando-os hoje e não precisa esperar até que o problema do estilo compartilhado seja resolvido. E como a única diferença entre componentes da Web e elementos personalizados é o shadow DOM, você sempre pode habilitar o shadow DOM em seus elementos personalizados assim que uma solução para estilo compartilhado estiver disponível.

Se você decidir criar elementos personalizados, esteja ciente de algumas diferenças entre elementos personalizados e componentes da web.

Primeiro, como os estilos do elemento personalizado estão sujeitos aos estilos de página e vice-versa, você deve garantir que seus seletores não causem conflitos. Se suas páginas já usam um guia de estilo, deixe os estilos do elemento personalizado no guia de estilo e faça com que o elemento produza o DOM esperado e a estrutura de classes.

Ao deixar os estilos no guia de estilo, você criará um caminho de migração suave para seus desenvolvedores, porque eles podem continuar usando o guia de estilo como antes, mas migrar lentamente para usar o novo elemento personalizado quando puderem. Depois que todos estiverem usando o elemento personalizado, você pode mover os estilos para residir dentro do elemento para mantê-los juntos e permitir uma refatoração mais fácil para componentes da Web posteriormente.

Em segundo lugar, certifique-se de encapsular qualquer código JavaScript dentro de uma expressão de função imediatamente invocada (IFFE), para que você não sangre nenhuma variável para o escopo global. Além de não fornecer escopo CSS, os elementos personalizados não fornecem escopo JavaScript.

Em terceiro lugar, você precisará usar a função connectedCallback do elemento personalizado para adicionar o modelo DOM ao elemento. De acordo com a especificação do componente da Web, os elementos personalizados não devem adicionar filhos durante a função construtora, portanto, você precisará adiar a adição do DOM à função connectedCallback .

Por último, o elemento <slot> não funciona fora do shadow DOM. Isso significa que você terá que usar um método diferente para fornecer uma maneira para os desenvolvedores inserirem seu conteúdo em seu elemento personalizado. Normalmente, isso envolve apenas manipular o DOM para inserir seu conteúdo onde você quiser.

No entanto, como não há separação entre o shadow DOM e o light DOM com elementos personalizados, você também terá que ter muito cuidado para não estilizar o DOM inserido, devido aos estilos em cascata de seus elementos.

 <!-- login-form.html --> <template> <style> login-form .container { max-width: 300px; padding: 50px; border: 1px solid grey; } login-form .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> (function() { const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); } // Without the shadow DOM, we have to manipulate the custom element // after it has been inserted in the DOM. connectedCallback() { const temp = document.importNode(template.content, true); this.appendChild(temp); } }); })(); </script>
 <!-- index.html --> <link rel="stylesheet" href="styleguide.css"> <link rel="import" href="login-form.html"> <login-form></login-form>

Em termos de desempenho, os elementos personalizados são quase tão rápidos quanto os componentes da Web que não estão sendo usados ​​(ou seja, vinculando a folha de estilo compartilhada no head e usando apenas elementos DOM nativos). De todas as técnicas que você pode usar hoje, esta é de longe a mais rápida.

Gráfico de barras do desempenho do elemento personalizado.
Os resultados de dois testes de desempenho mostram que os elementos personalizados são quase tão rápidos quanto não usar componentes da Web.

À parte: um elemento personalizado ainda é um componente da web para todos os efeitos. O termo “componentes da web” é usado para descrever quatro tecnologias separadas: elementos personalizados, tags de modelo, importações de HTML e o shadow DOM.

Infelizmente, o termo tem sido usado para descrever qualquer coisa que use qualquer combinação das quatro tecnologias. Isso levou a muita confusão sobre o que as pessoas querem dizer quando dizem “componente da web”. Assim como Rob Dodson descobriu, achei útil usar termos diferentes ao falar sobre elementos personalizados com e sem o shadow DOM.

A maioria dos desenvolvedores com quem conversei tendem a associar o termo “componente web” a um elemento personalizado que usa o shadow DOM. Portanto, para os propósitos deste artigo, criei uma distinção artificial entre um componente da Web e um elemento personalizado.

Usando uma biblioteca de componentes da Web

Outra solução que você pode usar hoje é uma biblioteca de componentes web, como Polymer, SkateJS ou X-Tag. Essas bibliotecas ajudam a preencher as lacunas do suporte atual e também podem simplificar o código necessário para criar um componente da web. Eles também geralmente fornecem recursos adicionais que facilitam a escrita de componentes da web.

Por exemplo, o Polymer permite criar um componente web simples em apenas algumas linhas de JavaScript. Um benefício adicional é que o Polymer fornece uma solução para usar o shadow DOM e uma folha de estilo compartilhada. Isso significa que você pode criar componentes da Web hoje que compartilham estilos.

Para fazer isso, crie o que eles chamam de módulo de estilo, que contém todos os estilos compartilhados. Pode ser uma tag <style> com os estilos compartilhados embutidos ou uma tag <link rel=“import”> que aponta para uma folha de estilos compartilhada. Em ambos os casos, inclua os estilos em seu componente da web com uma tag <style include> e, em seguida, o Polymer analisará os estilos e os adicionará como uma tag <style> inline ao seu componente da web.

 <!-- shared-styles.html --> <dom-module> <!-- Link to a shared style sheet --> <!-- <link rel="import" href="styleguide.css"> --> <!-- Inline the shared styles --> <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } /* Rest of shared CSS */ </style> </template> </dom-module>
 <!-- login-form.html --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../shared-styles/shared-styles.html"> <dom-module> <template> <!-- Include the shared styles --> <style include="shared-styles"></style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> Polymer({ is: 'login-form' }); </script> </dom-module>

A única desvantagem de usar uma biblioteca é que ela pode atrasar o tempo de renderização de seus componentes da web. Isso não deve ser uma surpresa porque baixar o código da biblioteca e processá-lo leva tempo. Nenhum componente da web na página pode começar a renderizar até que a biblioteca termine o processamento.

No caso do Polymer, ele pode atrasar o tempo de renderização da página em até meio segundo em comparação com os componentes nativos da web. Um módulo de estilo que incorpora os estilos é um pouco mais lento do que um módulo de estilo que vincula os estilos, e incorporar os estilos diretamente no componente da Web é tão rápido quanto usar um módulo de estilo.

Novamente, o Polymer não faz nada em particular para tornar o tempo de renderização mais lento. Fazer o download da biblioteca Polymer e processar todos os seus recursos incríveis, além de criar todas as ligações de modelos, leva tempo. É apenas a troca que você terá que fazer para usar uma biblioteca de componentes da web.

Gráfico de barras do desempenho do componente Web Polymer.

Os resultados dos testes de desempenho mostram que, usando o Polymer, os componentes da Web serão renderizados até meio segundo mais lentos do que os componentes da Web nativos.

A promessa do futuro

Se nenhuma das soluções atuais funcionar para você, não se desespere. Se tudo correr bem, dentro de alguns meses a alguns anos, poderemos usar estilos compartilhados usando algumas abordagens diferentes.

Propriedades personalizadas

Propriedades personalizadas (ou variáveis ​​CSS, como são chamadas) são uma forma de definir e usar variáveis ​​em CSS. Essa noção não é nova para pré-processadores CSS, mas como um recurso CSS nativo, as propriedades personalizadas são realmente mais poderosas do que uma variável de pré-processador.

Para declarar uma propriedade customizada, use a notação de propriedade customizada de –my-variable: value e acesse a variável usando property: var(–my-variable) . Uma propriedade personalizada em cascata como qualquer outra regra CSS, portanto, seu valor é herdado de seu pai e pode ser substituído. A única ressalva para as propriedades personalizadas é que elas devem ser declaradas dentro de um seletor e não podem ser declaradas por conta própria, ao contrário de uma variável de pré-processador.

 <style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>

Uma coisa que torna as propriedades personalizadas tão poderosas é sua capacidade de perfurar o shadow DOM. Esta não é a mesma ideia dos seletores /deep/ e ::shadow porque eles não forçam seu caminho para o componente web. Em vez disso, o autor do componente da Web deve usar a propriedade customizada em seu CSS para que ela seja aplicada. Isso significa que um autor de componente da web pode criar uma API de propriedade customizada que os consumidores do componente da web podem usar para aplicar seus próprios estilos.

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

O suporte do navegador para propriedades personalizadas é surpreendentemente bom. A única razão pela qual não é uma solução que você pode usar hoje é que não há polyfill funcionando sem Custom Elements versão 1. A equipe por trás do polyfill webcomponentjs está trabalhando para adicioná-lo, mas ainda não foi lançado e em um estado construído, o que significa que, se você fizer o hash de seus ativos para produção, não poderá usá-los. Pelo que entendi, deve ser lançado no início do ano que vem.

Mesmo assim, as propriedades personalizadas não são um bom método para compartilhar estilos entre componentes da web. Como eles só podem ser usados ​​para declarar um único valor de propriedade, o componente da Web ainda precisa incorporar todos os estilos do guia de estilo, embora com seus valores substituídos por variáveis.

As propriedades personalizadas são mais adequadas para opções de temas, em vez de estilos compartilhados. Por isso, as propriedades personalizadas não são uma solução viável para o nosso problema.

/* Use a propriedade customizada */ input { background: var(–main-bg-color); } </style>

Uma coisa que torna as propriedades personalizadas tão poderosas é sua capacidade de perfurar o shadow DOM. Esta não é a mesma ideia dos seletores /deep/ e ::shadow porque eles não forçam seu caminho para o componente web. Em vez disso, o autor do componente da Web deve usar a propriedade customizada em seu CSS para que ela seja aplicada. Isso significa que um autor de componente da web pode criar uma API de propriedade customizada que os consumidores do componente da web podem usar para aplicar seus próprios estilos.

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

O suporte do navegador para propriedades personalizadas é surpreendentemente bom. A única razão pela qual não é uma solução que você pode usar hoje é que não há polyfill funcionando sem Custom Elements versão 1. A equipe por trás do polyfill webcomponentjs está trabalhando para adicioná-lo, mas ainda não foi lançado e em um estado construído, o que significa que, se você fizer o hash de seus ativos para produção, não poderá usá-los. Pelo que entendi, deve ser lançado no início do ano que vem.

Mesmo assim, as propriedades personalizadas não são um bom método para compartilhar estilos entre componentes da web. Como eles só podem ser usados ​​para declarar um único valor de propriedade, o componente da Web ainda precisa incorporar todos os estilos do guia de estilo, embora com seus valores substituídos por variáveis.

As propriedades personalizadas são mais adequadas para opções de temas, em vez de estilos compartilhados. Por isso, as propriedades personalizadas não são uma solução viável para o nosso problema.

@aplicar regras

Além das propriedades personalizadas, o CSS também está recebendo regras @apply . Regras de aplicação são essencialmente mixins para o mundo CSS. Eles são declarados de maneira semelhante às propriedades personalizadas, mas podem ser usados ​​para declarar grupos de propriedades em vez de apenas valores de propriedade. Assim como as propriedades personalizadas, seus valores podem ser herdados e substituídos e devem ser declarados dentro de um seletor para funcionar.

 <style> /* Declare rule */ html { --typography: { font: 16px Arial, sans-serif; color: #333333; } } /* Use rule */ input { @apply --typography; } </style>

O suporte do navegador para regras @apply é basicamente inexistente. Atualmente, o Chrome o suporta por trás de um sinalizador de recurso (que não consegui encontrar), mas é isso. Também não há polyfill funcionando pelo mesmo motivo que não há polyfill para propriedades personalizadas. A equipe de polyfill do webcomponentjs também está trabalhando para adicionar regras @apply , juntamente com propriedades personalizadas, para que ambos estejam disponíveis assim que a nova versão for lançada.

Ao contrário das propriedades personalizadas, as regras @apply são uma solução muito melhor para compartilhar estilos. Como eles podem configurar um grupo de declarações de propriedade, você pode usá-los para configurar o estilo padrão para todos os elementos nativos e usá-los dentro do componente da web. Para fazer isso, você teria que criar uma regra @apply para cada elemento nativo.

No entanto, para consumir os estilos, você teria que aplicá-los manualmente a cada elemento nativo, o que ainda duplicaria a declaração de estilo em cada componente da web. Embora isso seja melhor do que incorporar todos os estilos, também não é muito conveniente porque se torna padrão no topo de cada componente da Web, que você deve lembrar de adicionar para que os estilos funcionem corretamente.

 /* styleguide.css */ html { --typography: { color: #333333; font: 16px Arial, sans-serif; } --paragraph: { margin: 0; } --label { display: block; margin-bottom: 5px; } --input-text { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } --input-submit { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } /* And so on for every native element */ }
 <!-- login-form.html --> <template> <style> :host { @apply --typography; } p { @apply --paragraph; } label { @apply --label; } input-text { @apply --input-text; } .input-submit { @apply --input-submit; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template>

Devido à necessidade de um clichê extenso, não acredito que as regras @apply sejam uma boa solução para compartilhar estilos entre componentes da web. Eles são uma ótima solução para temas, no entanto.

na sombra DOM

De acordo com a especificação do componente web, os navegadores ignoram qualquer <link rel=“stylesheet”> no shadow DOM, tratando-as como fariam dentro de um fragmento de documento. Isso nos impediu de poder vincular qualquer estilo compartilhado em nossos componentes da Web, o que foi lamentável - isto é, até alguns meses atrás, quando o Grupo de Trabalho de Componentes da Web propôs que as tags <link rel=“stylesheet”> deveriam funcionar em a sombra DOM. Após apenas uma semana de discussão, todos concordaram que deveriam, e alguns dias depois eles o adicionaram à especificação HTML.

 <template> <link rel="stylesheet" href="styleguide.css"> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

Se isso parece um pouco rápido demais para o grupo de trabalho concordar com uma especificação, é porque não era uma proposta nova. Fazer com que as tags de link funcionem no shadow DOM foi proposto há pelo menos três anos, mas ficou em atraso até que eles pudessem garantir que não fosse um problema para o desempenho.

Se a aceitação da proposta não for empolgante o suficiente, o Chrome 55 (atualmente Chrome Canary) adicionou a funcionalidade inicial de fazer as tags de link funcionarem no shadow DOM. Parece até que essa funcionalidade chegou à versão atual do Chrome. Até o Safari implementou o recurso no Safari 18.

Ser capaz de vincular os estilos compartilhados é de longe o método mais conveniente para compartilhar estilos entre componentes da web. Tudo o que você precisa fazer é criar a tag de link e todos os elementos nativos serão estilizados de acordo, sem exigir nenhum trabalho adicional.

É claro que a maneira como os fabricantes de navegadores implementam o recurso determinará se essa solução é viável. Para que isso funcione corretamente, as tags de link precisariam ser desduplicadas, de modo que vários componentes da Web solicitando o mesmo arquivo CSS causariam apenas uma solicitação HTTP. O CSS também precisaria ser analisado apenas uma vez, para que cada instância do componente da Web não precisasse recalcular os estilos compartilhados, mas sim reutilizar os estilos calculados.

O Chrome já faz as duas coisas. Portanto, se todos os outros fabricantes de navegadores o implementarem da mesma maneira, as tags de link trabalhando no shadow DOM definitivamente resolverão o problema de como compartilhar estilos entre componentes da web.

Folhas de Estilo Construíveis

Você pode achar difícil de acreditar, já que ainda não conseguimos, mas uma tag de link trabalhando no shadow DOM não é uma solução de longo prazo. Em vez disso, é apenas uma solução de curto prazo para nos levar à solução real: folhas de estilo construtíveis.

As folhas de estilo construtíveis são uma proposta para permitir a criação de objetos StyleSheet em JavaScript através de uma função construtora. A folha de estilo construída poderia então ser adicionada ao shadow DOM por meio de uma API, o que permitiria ao shadow DOM usar um conjunto de estilos compartilhados.

Infelizmente, isso é tudo o que pude obter da proposta. Tentei descobrir mais informações sobre o que eram folhas de estilo construtíveis perguntando ao Web Components Working Group, mas eles me redirecionaram para a lista de discussão do CSS Working Group do W3C, onde perguntei novamente, mas ninguém respondeu. Eu não conseguia nem entender como a proposta estava progredindo, porque ela não era atualizada há mais de dois anos.

Mesmo assim, o Grupo de Trabalho de Componentes da Web o utiliza como solução para compartilhar estilos entre componentes da Web. Esperamos que a proposta seja atualizada ou o Grupo de Trabalho de Componentes da Web divulgue mais informações sobre ela e sua adoção. Até lá, a solução de “longo prazo” parece não acontecer no futuro próximo.

Lições aprendidas

Após meses de pesquisa e testes, estou bastante esperançoso para o futuro. É reconfortante saber que depois de anos sem uma solução para compartilhar estilos entre componentes da web, finalmente há respostas. Essas respostas podem não ser estabelecidas por mais alguns anos, mas pelo menos estão lá.

Se você quiser usar um guia de estilo compartilhado para estilizar componentes da Web hoje, não poderá usar o shadow DOM e, em vez disso, criar elementos personalizados, ou poderá usar uma biblioteca de componentes da Web que suporte polyfills para compartilhar estilos. Ambas as soluções têm seus prós e contras, então use a que funcionar melhor para o seu projeto.

Se você decidir esperar um pouco antes de se aprofundar nos componentes da Web, em alguns anos devemos ter algumas ótimas soluções para compartilhar os estilos entre eles. Então, continue verificando como está progredindo.

Coisas para manter em mente

Lembre-se de algumas coisas se você decidir usar elementos personalizados ou componentes da Web hoje.

Mais importante ainda, a especificação do componente web ainda está sendo desenvolvida ativamente, o que significa que as coisas podem e vão mudar. Os componentes da Web ainda são a vanguarda, portanto, esteja preparado para ficar atento à medida que desenvolve com eles.

Se você decidir usar o shadow DOM, saiba que ele é bastante lento e sem desempenho em navegadores polyfilled. Foi por esse motivo que os desenvolvedores do Polymer criaram sua implementação obscura do DOM e a tornaram padrão.

Chrome, Opera e, recentemente, Safari são os únicos navegadores que suportam o shadow DOM versão 0. O Firefox ainda está em desenvolvimento, embora o suporte por trás de um experimento desde a versão 29. A Microsoft ainda o considera para o Edge e o tem como alta prioridade em seu roteiro.

No entanto, o shadow DOM versão 0 é a especificação antiga. Shadow DOM versão 1 é o novo, e apenas Chrome, Safari e Opera o suportam totalmente. Sem mencionar que a versão 0 dos elementos personalizados passou pela mesma atualização, e apenas o Chrome oferece suporte total aos elementos personalizados versão 1, enquanto a visualização técnica do Safari oferece suporte a partir da versão 17. A versão 1 dos elementos personalizados tem algumas mudanças importantes na forma como os componentes da Web são escritos, então certifique-se de entender completamente o que isso implica.

Por fim, o polyfill webcomponentjs suporta apenas a implementação da versão 0 do shadow DOM e elementos personalizados. Uma ramificação da versão 1 do polyfill oferecerá suporte à versão 1, mas ainda não foi lançada.