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. Eu gostaria de compartilhar como resolvemos os problemas relacionados ao estilo de componentes da Web usando as propriedades personalizadas de CSS, além de alguns outros benefícios de usar propriedades personalizadas em sistemas de design e bibliotecas de componentes.
Como criamos componentes da Web
Para criar nossos componentes da Web, usamos a Lit, uma biblioteca que fornece muito código boilerplate, como estado, estilos com escopo, modelos e muito mais. Além de ser leve, o Lit foi desenvolvido com APIs JavaScript nativas. Isso significa que podemos oferecer um conjunto enxuto de código que aproveita os recursos do navegador.
Mas o mais interessante sobre os Web Components é que eles funcionam com quase qualquer estrutura JavaScript existente, ou até mesmo com nenhuma estrutura. Depois que o pacote JavaScript principal é referenciado na página, o uso de um componente Web é muito semelhante ao uso de um elemento HTML nativo. O único indício real de que ele não é um elemento HTML nativo é o hífen consistente nas tags, que é um padrão para indicar ao navegador que se trata de um componente da Web.
Encapsulamento do estilo 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 de fazer isso, tente examinar um elemento de entrada nativo no inspetor. Agora você terá a opção de abrir essa entrada e ver todos os elementos dentro dela. Você pode até mesmo testar isso com um dos nossos componentes da Web. Confira nosso componente de entrada personalizado para ver o Shadow DOM.
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 Web principal não pode vazar para o seu componente Web.
Esse encapsulamento de estilos é um benefício em 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 ainda mais, adicionamos all: unset;
à raiz, ou "host", de todos os 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 precise ser mais grossa? Se nenhum estilo pode entrar no seu componente, como você pode desbloquear essas opções de estilo?
É aí que entram as propriedades personalizadas do CSS.
Propriedades personalizadas de CSS
As propriedades personalizadas têm nomes muito apropriados: são propriedades CSS que você pode nomear e aplicar o valor necessário. A única exigência é que eles sejam prefixados com dois hifens. Depois de declarar a propriedade personalizada, o valor pode ser usado no CSS com 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 regulares. Qualquer propriedade personalizada aplicada a um elemento pai ou ao próprio elemento pode ser usada como um valor em outras propriedades. Fazemos um grande uso das propriedades personalizadas para nossos tokens de design ao aplicá-las ao elemento raiz por meio de nosso CSS Framework. Isso significa que todos os elementos da página podem usar esses valores de token, sejam eles um componente da Web, uma classe auxiliar de CSS ou um desenvolvedor que queira extrair um valor de 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 Web do Nord
Sempre que desenvolvemos um componente para nosso sistema de design, adotamos uma abordagem cuidadosa ao CSS. Gostamos de buscar um código enxuto, mas que seja fácil de manter. Os tokens de design que temos são definidos como propriedades personalizadas em 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. Para outros, na verdade, 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, podemos interceptar os 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 levar as propriedades personalizadas ainda mais longe
No momento, 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 nesse 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 os 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 particulares foram criadas por Lea Verou, que é uma propriedade personalizada contextual "privada" no próprio componente, mas definida 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-se argumentar que esse método não é verdadeiramente "particular", ainda acreditamos que essa é uma solução elegante para um problema que nos preocupamos. Quando tivermos a oportunidade, vamos abordar isso em nossos componentes para que nossa equipe de desenvolvimento tenha mais controle sobre o uso dos componentes e, ao mesmo tempo, aproveite as proteções que estabelecemos.
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, bem como o restante da minha equipe, que se esforçaram 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ț