Os benefícios de usar propriedades personalizadas em sistemas de design e bibliotecas de componentes.
Meu nome é Dave, e sou desenvolvedor sênior de front-end na Nordhealth. Trabalho no design e desenvolvimento do nosso sistema de design Nord, que inclui a criação de componentes da Web para nossa biblioteca de componentes. Quero compartilhar como resolvemos os problemas relacionados ao estilo dos componentes da Web usando as propriedades personalizadas do CSS e alguns dos outros benefícios do uso de propriedades personalizadas em sistemas de design e bibliotecas de componentes.
Como criamos componentes da Web
Para criar nossos componentes da Web, usamos o Lit, uma biblioteca que fornece muito código boilerplate, como estado, estilos de escopo, modelos e muito mais. O Lit é leve e também foi criado com base em APIs JavaScript nativas, o que significa que podemos oferecer um pacote enxuto de código que aproveita os recursos que o navegador já tem.
Mas a melhor parte dos Web Components é que eles funcionam com quase qualquer framework JavaScript ou até mesmo sem nenhum framework. Depois que o pacote principal de JavaScript é referenciado na página, usar um componente da Web é muito parecido com usar um elemento HTML nativo. O único sinal de que ele não é um elemento HTML nativo é o hífen consistente nas tags, que é um padrão para indicar ao navegador que ele é um componente da Web.
Encapsulamento de estilo do Shadow DOM
Assim como os elementos HTML nativos têm um Shadow DOM, os componentes da Web também têm. O Shadow DOM é uma árvore oculta de nós dentro de um elemento. A melhor maneira de visualizar isso é abrir o Web Inspector e ativar a opção "Mostrar árvore do Shadow DOM". Depois disso, tente analisar um elemento de entrada nativo no inspetor. Agora você terá a opção de abrir essa entrada e conferir todos os elementos nela. Você pode até tentar isso com um dos nossos Web Components. Inspecione nosso componente de entrada personalizado para conferir o shadow DOM dele.
Uma das vantagens (ou desvantagens, dependendo da sua perspectiva) do shadow DOM é o encapsulamento de estilo. Se você escrever CSS no componente da Web, esses estilos não vão vazar e afetar a página principal ou outros elementos. Eles ficam completamente contidos no componente. Além disso, o CSS escrito para a página principal ou um componente da Web pai não pode vazar para o componente da Web.
Esse encapsulamento de estilos é um benefício da nossa biblioteca de componentes. Isso nos dá mais garantia de que, quando alguém usa um dos nossos componentes, ele terá a aparência que planejamos, independentemente dos estilos aplicados à página mãe. Para garantir isso, adicionamos all: unset;
à raiz, ou "host", de todos os nossos componentes da Web.
No entanto, e se alguém que usa seu componente da Web tiver um motivo legítimo para mudar determinados estilos? Talvez haja uma linha de texto que precise de mais contraste devido ao contexto ou uma borda que precisa ser mais grossa? Se nenhum estilo puder entrar no componente, como você pode desbloquear essas opções de estilo?
É aí que entram as propriedades personalizadas do CSS.
Propriedades personalizadas do CSS
As propriedades personalizadas têm nomes muito apropriados: são propriedades CSS que você pode nomear e aplicar o valor necessário. O único requisito é que você adicione dois hifens como prefixo. Depois de declarar a propriedade personalizada, o valor pode ser usado no CSS usando a função var()
.
Quando se trata de herança, todas as propriedades personalizadas são herdadas, o que segue o comportamento típico de propriedades e valores CSS normais. Qualquer propriedade personalizada aplicada a um elemento pai ou ao próprio elemento pode ser usada como um valor em outras propriedades. Usamos muito as propriedades personalizadas para nossos tokens de design, aplicando-as ao elemento raiz pelo nosso framework CSS. Isso significa que todos os elementos na página podem usar esses valores de token, seja um componente da Web, uma classe auxiliar de CSS ou um desenvolvedor que queira extrair um valor da nossa lista de tokens.
Essa capacidade de herdar propriedades personalizadas, com o uso da função var()
, é como perfuramos o DOM de sombra dos componentes da Web e permitimos que os desenvolvedores tenham um controle mais refinado ao estilizar nossos componentes.
Propriedades personalizadas em um componente da Web do Nord
Sempre que desenvolvemos um componente para nosso sistema de design, adotamos uma abordagem cuidadosa em relação ao CSS. Gostamos de buscar um código enxuto, mas muito fácil de manter. Os tokens de design que temos são definidos como propriedades personalizadas no nosso framework CSS principal no elemento raiz.
Esses valores de token são referenciados nos nossos componentes. Em alguns casos, aplicamos o valor diretamente na propriedade CSS, mas em outros, definimos uma nova propriedade personalizada contextual e aplicamos o valor a ela.
Também vamos abstrair alguns valores específicos do componente, mas não dos nossos tokens, e transformá-los em uma propriedade personalizada contextual. As propriedades personalizadas que são contextuais ao componente oferecem dois benefícios principais. Primeiro, isso significa que podemos usar o CSS de forma mais "seca", já que esse valor pode ser aplicado a várias propriedades dentro do componente.
Em segundo lugar, ele deixa o estado do componente e as mudanças de variação muito mais claras. É necessário mudar apenas a propriedade personalizada para atualizar todas essas propriedades quando, por exemplo, você está estilizando um estado de passagem do cursor ou ativo ou, neste caso, uma variação.
Mas o benefício mais importante é que, ao definir essas propriedades personalizadas contextuais em um componente, criamos uma espécie de API CSS personalizada para cada um dos nossos componentes, que pode ser acessada pelo usuário desse componente.
O exemplo anterior mostra um dos nossos componentes da Web com uma propriedade personalizada contextual alterada por um seletor. O resultado dessa abordagem é um componente que oferece flexibilidade suficiente de estilo ao usuário, mantendo a maioria dos estilos reais sob controle. Além disso, como bônus, nós, desenvolvedores de componentes, temos a capacidade de interceptar esses estilos aplicados pelo usuário. Se quisermos ajustar ou estender uma dessas propriedades, podemos fazer isso sem que o usuário precise mudar o código.
Essa abordagem é muito poderosa, não apenas para nós, criadores dos componentes do sistema de design, mas também para nossa equipe de desenvolvimento quando ela usa esses componentes nos nossos produtos.
Como aproveitar melhor as propriedades personalizadas
No momento da escrita, não revelamos essas propriedades personalizadas contextuais na nossa documentação. No entanto, planejamos fazer isso para que nossa equipe de desenvolvimento mais ampla possa entender e aproveitar essas propriedades. Nossos componentes são empacotados no npm com um arquivo de manifesto, que contém tudo o que há para saber sobre eles. Em seguida, consumimos o arquivo de manifesto como dados quando o site de documentação é implantado, o que é feito usando o Eleventy e o recurso de dados globais. Planejamos incluir essas propriedades personalizadas contextuais neste arquivo de dados do manifesto.
Outra área que queremos melhorar é a forma como essas propriedades personalizadas contextuais herdam valores. Atualmente, por exemplo, se você quiser ajustar a cor de dois componentes de divisor, precisará segmentar ambos os componentes especificamente com seletores ou aplicar a propriedade personalizada diretamente no elemento com o atributo de estilo. Isso pode parecer bom, mas seria mais útil se o desenvolvedor pudesse definir esses estilos em um elemento de contenção ou até mesmo no nível raiz.
Você precisa definir o valor da propriedade personalizada diretamente no componente porque estamos definindo esse valor no mesmo elemento usando o seletor de host do componente. Os tokens de design globais que usamos diretamente no componente são transmitidos diretamente, sem serem afetados por esse problema, e podem até ser interceptados em elementos pais. Como podemos aproveitar o melhor dos dois mundos?
Propriedades personalizadas públicas e particulares
As propriedades personalizadas privadas foram criadas por Lea Verou. Elas são uma propriedade personalizada "privada" contextual no componente, mas definidas como uma propriedade personalizada "pública" com um substituto.
Ao definir nossas propriedades personalizadas contextuais dessa maneira, ainda podemos fazer tudo o que fazíamos antes, como herdar valores de token globais e reutilizar valores em todo o código do componente. No entanto, o componente também herda novas definições dessa propriedade em si mesmo ou em qualquer elemento pai.
Embora possa ser argumentado que esse método não é realmente "privado", ainda achamos que essa é uma solução elegante para um problema que nos preocupava. Quando tivermos a oportunidade, vamos resolver esse problema nos nossos componentes para que a equipe de desenvolvimento tenha mais controle sobre o uso dos componentes, aproveitando os limites que temos.
Esperamos que você tenha achado útil este insight sobre como usamos os componentes da Web com propriedades personalizadas de CSS. Conte o que você achou e, se decidir usar algum desses métodos no seu trabalho, me encontre no Twitter @DavidDarnes. Você também pode encontrar a Nordhealth @NordhealthHQ no Twitter, assim como o restante da minha equipe, que trabalhou muito para reunir esse sistema de design e executar os recursos mencionados neste artigo: @Viljamis, @WickyNilliams e @eric_habich.
Imagem principal de Dan Cristian Pădureț