10 princípios para criar ótimos WebComponents

10 princípios para criar ótimos WebComponents

Ao longo desses 12 anos desenvolvendo para Frontend, pude perceber como organizar e planejar antes pode ser de grande ajuda. O problema no começo era pensar: o que, afinal, preciso planejar? Desenvolver uma página para uma aplicação pode (e deve) ser simples, entretanto, para grandes empresas ou mesmo para o mundo do Open Source, planejar desde o início alguns aspectos são de grande valia e podem te ajudar a não ter que refazer tudo depois de alguns anos.

Desde que comecei a trabalhar com WebComponents, absorvi e percebi que muitos problemas teriam sido evitados. Como eu disse, muita coisa foi descoberta ao longo dos anos, com tentativa e erro. Um requisito atrás do outro que fazia a minha equipe coçar a cabeça e pensar: e agora, não pensamos nesse aspecto quando criamos aquele simples input text. Como mudar?

Então, como aprender com os erros dos outros é mais divertido, segue aqui as minhas lições aprendidas em 10 pontos.

10 dicas para criar WebComponents

1- Atinja uma necessidade comum

Quando começamos a identificar quais são os componentes a serem desenvolvidos no projeto, é comum pensarmos nos primeiros e mais básicos tipos (inputs, dropdown, textarea, etc.), muito por eles serem essenciais para qualquer outra utilização. Porém, o que tem que ser levado em conta para o backlog também são os componentes de uso comum como um header que todas as aplicações precisam desenvolver com um padrão visual ou um stepper que têm uma barra de progresso para os passos clicados ou até uma lista com rolagem infinita. Todos esses são bons exemplos por aparecerem em todos os lugares e aplicações.

Lembrando que componentes de uso comum geralmente não tem regra de negócio (um que chame uma API no back-end por exemplo, dificilmente será de uso comum).

2- Crie componentes que só façam 1 coisa, mas muito bem feita

Os WebComponents são maleáveis. Você pode pegar apenas uma fração de uma página ou componente e transformá-lo em um WebComponent (ao contrário daqueles Widgets gigantes e monolíticos que tínhamos antes). Agora, com os micro componentes, não precisamos mais nos limitar a criar algo gigante com inúmeras parametrizações que podiam trazer coisas que nem ao menos era utilizadas na aplicação (alterando o bundle final da aplicação e adicionando 1MB de código inutilizado). Ao invés disso, um WebComponent pode ser criado para fazer uma coisa mínima, porém muito bem feita.

Avaliar se um componente está fazendo apenas uma coisa não é tão simples, mas temos modos de identificar se:

  • O componente deve ser fácil de incluir em uma aplicação

Você deve conseguir importá-lo apenas com 1 linha na aplicação. Isso indica que ele não tem dependências externas e não precisa de mais nada para ser usado (se você precisa de uma lista apenas para importar um componente, provavelmente ele está fazendo mais de uma coisa)

Entretanto,  não há problema caso ele estiver herdando funcionalidades de base de algum outro lugar. O importante é que ele tenha apenas uma responsabilidade.

  • O componente não poder ter conflitos de combinação

Se você declarou algum atributo (propriedade ou método) que para usar um é preciso desativar o outro, talvez o seu componente esteja fazendo mais de uma coisa e precisa ser alterado, por exemplo:

- Para usar a propriedade name você não pode declarar a propriedade label serão ocasionará quebra.

Isso vale para os métodos, pois se existe uma regra de que um determinado método não pode ser chamado quando a propriedade X for true então é possível que você tenha que quebrar esse componente em dois e compartilhar uma classe entre si.

Vamos a um exemplo de WebComponent que só faz 1 coisa. Veja esse header aqui:

Podemos pensar em criar um header que vai ter algumas responsabilidades, visuais e dinâmicas, tais como incluir o logo da empresa, mostrar itens de menu, incluir um input text para busca ou então mostrar um ícone de lupa.

Se esse componente for pensado desde o início como apenas 1, ao invés de 4 (considero que o logotipo da empresa também é um ícone) teremos sempre que pensar no todo quando houver alguma alteração, e se o componente de input mudar, tenho que lembrar todos os outros que usam o input como dependência e alterar?

Uma proposta para esse header seria essa:

1 - <my-header>: contêiner que acomodaria os outros e os organizaria em seus devidos lugares, se preocuparia apenas na sua responsividade e em como tudo será mostrado.

2 - <my-icon>: componente de ícone, que tem os ícones disponíveis da empresa e o que pode ou não ser usado, teria exposto um evento de click e teria suas variáveis para mudar a cor e tamanho.

3 - <my-header-menu>:  filho do my-header, incluído no nome para ser de fácil identificação que só pode ser usado pelo header e não filho de qualquer outro, tem as funcionalidades de clique, hover e foco.

4 - <my-text>:  componente de texto, altamente mutável, aceita como filho um <my-icon> para ser mostrado à direita e observa o teclado para emitir um evento a cada input, também emite o blur, e outros eventos básicos como keyup ou keypress.

Veja que cada componente está focado em fazer apenas uma coisa no nosso header. Ao invés de dar todas as responsabilidades para o header fazer, essa divisão permite que os desenvolvedores obtenham apenas o que precisam ao usar.

Pense que uma aplicação queria mudar o logo ou então ao invés de mostrar itens de menu ele queria mostrar os detalhes da página atual ou então pense que muitas aplicações não precisarão de um campo de busca diretamente no header, pois não tem uma busca geral.

3- Seja criativo, mas evite gold plating

Gold plating em resumo é quando oferecemos coisas para o nosso cliente que pensamos que vai ser útil, mas o cliente não pediu ainda (ou talvez nem vá pedir).

Evitar colocar coisas no componente desde o início é uma linha tênue, pois você precisa prever o básico para o funcionamento e ao mesmo tempo não colocar um parâmetro que apenas talvez vai ser usado, mesmo que você considere mega útil. Construa o básico e aguarde pelos pedidos de mudança, um componente recém lançado ainda leva um tempo para ficar maduro.

4 - Um componente com configurações zero deve ser útil

Um componente vai passar a ter diversos estados, por exemplo, podemos o criar um datepicker dessa maneira em uma aplicação:

O resultado provavelmente será um datepicker com label, placeholder, um X ao final e um valor preenchido.

Mas o que aconteceria se o desenvolvedor tentasse utilizar dessa maneira?

O que deveria ser mostrado?

O datepicker deveria, ao menos, mostrar um input text com um botão para selecionar a data e talvez mostrar uma label padrão? Ou dependendo da regra, uma data pré-estabelecida como a data de hoje?

Esse é o ponto. Um componente tem de ser útil sem qualquer tipo de parametrizações, útil imediatamente. Isso também é chamado de “teste de tag simples” (plain tag test), se o desenvolvedor apenas abrir e fechar a tag do componente criado, ele obterá algo útil?

Algumas regras que podem te ajudar:

●   Evite exigir atributos. Deve-se fornecer bons valores padrão para todos os atributos internos.

●   Evite exigir CSS. Todas as variáveis de CSS também devem ter um valor padrão.

●   Evite exigir Javascript para executar algo básico na inicialização

●   Evite exigir alterações na página de quem vai usar a nova tag

A regra é: se o desenvolvedor que for utilizar o componente precisa conhecer um conjunto de regras para usá-lo, quer dizer que ele ainda não é um componente de configuração zero.

5 - Crie um componente líquido

Pense que você está criando algo que será combinado com outros componentes para formar uma interface de usuário. Esse é o conceito de composability.

Para dar essa possibilidade do seu componente poder ser composto com outros, você precisará se preocupar com os slots, ou seja, a possibilidade de colocar algo dentro da tag, por exemplo:

Como a tag content será tratada? Se faz sentido seu componente receber conteúdo, esse conteúdo estará na Shadow DOM ou na Light DOM?

Essa tag content, se dentro da Shadow DOM, não conseguirá enxergar classes CSS externas, o seu componente apenas aceitará CSS in-line?

Isso deve ser pensado desde o início quando se utiliza Slot e se aceita conteúdos externos.

Em muitos componentes eu já tive que remover da Shadow DOM por não ter planejado nisso desde o início, o que os deixava expostos para terem o CSS sobrescritos.

6- CSS Variables são suas amigas

Desde o início, muitas propriedades CSS não precisaram ter sido fixadas no código (a não ser que faça parte do Design System da empresa, o Azul e Verde são a marca da empresa? Então fixe essas cores no CSS para que ninguém possa alterar).

Mas cores de fundo, de ícones internos, bordas, altura e largura, geralmente são propriedades que os desenvolvedores precisarão. Não tenha medo de criar uma lista de propriedades CSS com valores padrões definidos, pois isso ajudará muito o futuro do seu componente.

7 - Crie componentes abertos para serem alterados

Esse ponto depende um pouco do Framework ou ferramenta escolhida para escrever o WebComponent. O ponto é: um componente deveria poder ser herdado para sofrer mutação e deveria ser extensível.

A partir do momento que um WebComponent pode ser criado com apenas Javascript, o seu componente pode ser criado com o objetivo de ser herdado. Por exemplo: podemos criar um Input Text como base que outros componentes mais específicos como um Datepicker, Search ou até um Textarea podem usufruir de métodos básicos já escritos como limpar, escrever, incluir um ícone ao final etc.

Se o seu framework/biblioteca permitir a extensão das classes, você ganha produtividade e consistência. Se não, uma ideia interessante pode ser externalizar comportamentos em comum em uma biblioteca compartilhada entre esses componentes.

8 - Pense pequeno

Tamanho importa em aplicações web. Se um desenvolvedor estiver pensando em como resolver determinada situação na aplicação, ele não pode pensar duas vezes antes de usar o seu componente. O efeito que um componente pesado tem, pode ser grande para uma aplicação que já utilizará vários outros componentes de terceiro ao mesmo tempo. (ou o seu repetidas vezes).

Então, a quantidade de artefatos baixados na aplicação e o tamanho de cada um deles é importante:

●   Prefira utilizar imagens em formatos leves (Avif > Webp > SVGs, são muito melhores do que usar os famosos PNG e JPG

●   Evite usar bibliotecas (como jQuery ou Lodash por exemplo) – coisas como manipular o DOM ou fazer uma chamada HTTP podem serem feitas apenas com simples Javascript.

Para te ajudar:

●   You might not need jQuery: https://youmightnotneedjquery.com/

●   You might not need Lodash: https://youmightnotneed.com/lodash

9- Acessibilidade e Responsividade não podem ser um problema

Na medida do possível, ofereça suporte para uma grande variedade de usuários e dispositivos. Além de muitas ferramentas que hoje nos ajudam a testar a acessibilidade de um componente, vale sempre entender como funciona os Web Content Accessibility Guidelines (WCAG), a separação dos níveis (e tentar alcançar ao menos o nível AA). Alguns leitores de tela também podem ter dificuldade em ler conteúdo dentro da ShadowDOM, então lembre de expor textos, indicadores de tipo (roles) no host do seu WebComponent para que sejam simples de serem alterados e lidos.

Os desenvolvedores que forem usar o seu componente precisarão obter um resultado responsivo ao puxar e esticar a sua aplicação, por isso, tome cuidado com valores fixos (em pixels por exemplo você pode definir um input text com tamanho 100px), tente sempre usar valores relativos como percentuais (um input text com 100% do tamanho vai se comportar com o que for definido na tag superior, dando flexibilidade para quem for utilizar).

Usar media queries podem ser muito úteis também para alterar o visual dependendo do tamanho da tela, muitos componentes (como um header ou um footer por exemplo) precisarão ser responsivos para serem usados, portanto pense nisso desde o início.

10 - Um pouco de técnica: Focus, ShadowDOM, Scoped e Web Tradicional

Deixei essa dica por último por se tratar de aspectos mais técnicos que tive que aprender conforme o tempo e espero que te ajude, são pequenas pílulas, porém podem lhe ser muito úteis:

Cuidado com o focus(): o que acontecerá se o desenvolvedor que for usar o seu componente mandar um focus() para ele? Qual o ponto inicial do foco? WebComponents precisam propagar o foco para dentro da ShadowDOM e cabe a você enviar para o lugar correto. Você também precisa saber para onde o foco vai quando o usuário pressionar Tab no teclado, talvez você precise manipular os tabindex internos para que funcione como você deseja.

ShadowDOM vs Scoped: Um componente inteiramente funcionando na ShadowDOM traz inúmeras vantagens pelo seu encapsulamento de CSS, porém, se você for criar um componente que tem a responsabilidade de receber conteúdo externo (um card por exemplo esperará que algo seja incluído nele). Se você incluir esse conteúdo externo usando um append para dentro da ShadowDOM, o desenvolvedor vai perder a habilidade de customizar o próprio conteúdo,mesmo com classes CSS. Pense então que muitas vezes, componentes mais crus e simples (como cards, painéis ou contêineres) ficariam melhor apenas com o CSS Scoped ao invés de usar a ShadowDOM, que não vai trazer nenhum benefício e só vai atrapalhar o uso.

Uso do seu WebComponent por uma tecnologia Web Tradicional (não SPA): Tecnologias de Web Tradicional são aquelas que submetem o formulário para o back-end capturar todos os dados e armazenar em propriedades (como no Java, o .Net ou o PHP fazem). A questão é que inputs dentro da ShadowDOM não fazem parte de um formulário, por exemplo:

Nesse exemplo acima temos dois inputs: um com o name=fullname e o outro com o name=age

Ao clicar em Send, apenas o método no back-end myMethod será chamado, mas apenas o input fullname será enviado, pois um input que está dentro da ShadowDOM não participa do form submit.

Se você deseja que o seu componente possa ser usado por todos os tipos de tecnologia web (SPA e Web Tradicional), você deverá ter configurado no seu componente o parâmetro formAssociated que pode ser herdado do HTMLElement, você pode ver a configuração aqui: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals

É isso, espero que esses 10 pontos tenham te ajudado a pensar e se planejar melhor, quem dera eu soubesse muito dessas coisas antes, teria evitado muitos refactors.

Bom desenvolvimento \o/

⚠️
As opiniões e comentários expressos neste artigo são de propriedade exclusiva de seu autor e não representam necessariamente o ponto de vista da Revelo.

A Revelo Content Network acolhe todas as raças, etnias, nacionalidades, credos, gêneros, orientações, pontos de vista e ideologias, desde que promovam diversidade, equidade, inclusão e crescimento na carreira dos profissionais de tecnologia.