Vamos lá, CSS não é a coisa mais fácil do mundo. Existem diversas formas de resolver o mesmo problema, os browsers nem sempre possuem o mesmo comportamento, já chegamos num nível onde é impossível saber todas as regras que existem, etc. Na expectativa de ajudar o desenvolvimento, temos pré-processadores, pós-processadores, variáveis e metodologias para organização das classes, para citar alguns exemplos das ferramentas que temos para facilitar o uso do CSS.
Mas é claro, como quase tudo no mundo do frontend, as opções não param por ai. Dependendo do framework que você usa, também existem algumas alternativas diferentes para escrever CSS {como se já não fosse complexo o suficiente haha, mas vamos falar disso depois}.
Aqui na Alice, nós usamos o Vue e durante algumas semanas no nosso chapter de frontend, discutimos qual seria o padrão que iríamos adotar. Antes de tomar uma decisão, nós testamos as opções mais comuns e algumas obscuras que a comunidade ainda não está usando tanto. Como o nosso chapter de frontends não é muito grande, é importante para nós termos um padrão bem definido {já que cada pessoa está em um time diferente} e que seja fácil para quem não trabalha com frontend {já que nem todo time tem alguém com esse foco}.
No decorrer desse artigo, junto com os pontos que levantamos, tem algumas implementações básicas de um componente de botão {que chamei ele de base-button} que tem algumas propriedades: tamanho, ícone e loading que são as responsáveis por mudar o comportamento/CSS.
Então aperte o cinto que vamos seguir numa jornada do que nós encontramos em cada uma das possibilidades ✨
Mas antes, uma leve introdução ao vue file
Para quem nunca trabalhou com o Vue antes, é possível criar componentes usando arquivos .vue, que possuem o HTML dentro do <template>, o JS dentro de <script> e o CSS dentro de <style>.
Focando no assunto desse post, na parte de CSS nós temos algumas alternativas: escrever o CSS puro {assim como vemos da linha 16 a 18}, ou escrever com algum pré-processador.
Escolhendo a segunda o opção, tudo que nós precisamos fazer é adicionar um atributo lang na linha 15 com o nome da “linguagem” que queremos {Aqui na Alice nós usamos o SASS com a sintaxe scss, então a maioria dos exemplos daqui pra frente terão o CSS escrito dessa forma}.
Sabendo disso vamos começar falando de uma das nossas opções de escrever CSS, que não depende de estar usando ou não um pré-processador:
BEM
Acredito que essa seja uma das formas mais comum de escrever CSS. Por ser uma metodologia, não tem grandes segredos, mas não significa que seja fácil. Pensar no nome das coisas provavelmente é a parte mais difícil do desenvolvimento de software {risos}, e com o BEM é preciso pensar com carinho no nome das classes para que a estrutura faça sentido.
Para quem nunca trabalhou com a metodologia antes, vamos a uma explicação rápida:
Nós basicamente dividimos uma classe em três pedaços:
- O Block representa o bloco que vai ser estilizado {normalmente algum elemento que esteja envolvendo outros elementos/outras tags}.
- O Element é algum elemento ou tag que esteja dentro desse bloco, e para identificarmos ele facilmente, é separado por dois underscores.
- O Modifier significa que o estado daquele elemento mudou, o que significa que o nosso layout vai mudar graças; a separação dele é por dois dashes.
Isso significa que em uma classe eu posso ter dois elements? Ou dois modifiers? Não. A estrutura do BEM espera que você tenha um de cada, por isso para pessoas que não estão familiarizadas com a metologia, pode ser bem confuso como criar uma classe nova. {Nesse artigo tem uma explicação detalhada do uso do BEM e erros comuns, vale a leitura!}
De qualquer forma, se usado corretamente vários problemas de CSS são resolvidos, além de termos uma boa organização.
Agora vamos para a aplicação dessa metologia no Vue.
Começando pelas linhas 4, 5, 6, 7 — aqui nós temos classes condicionais baseadas em alguns atributos {que no nosso exemplo são props declaradas nas linhas 28 e 29}, uma coisa que podemos ver é que por serem condicionais, usamos o modifier para alterar visualmente o layout de cada uma delas {já que elas representam um estado do nosso botão}.
Na linha 12, temos uma classe com um element, pois esse componente de icone representa um elemento no nosso bloco {que nesse exemplo é o botão}.
Por fim, na linha 34 temos a declaração da lang, que como é importante dizer que só vai funcionar se o projeto está preparado para o pré-processador declarado. Se você começar um projeto usando o Vue CLI é uma das coisas que você pode ou não escolher. Se você quiser mudar o lang, na documentação do Vue tem um passo-a-passo.
Scoped CSS
Você pode se perguntar: por quê exatamente eu preciso do BEM? Não é só sair espalhando minhas classes por ai e tudo bem?
Conforme você vai escrevendo o seu CSS, no final das contas o Vue vai pegar o CSS de todos os componentes sendo usados naquela página e jogar ele no <head>. O que significa que qualquer componente vai ser afetado por qualquer regra definida em um componente. O BEM tenta resolver isso tendo classes especificas que não vão afetar outro componente tão facilmente.
É ai que entra o scoped CSS. Ele nos ajuda a resolver esse problema de escopo adicionando um atributo a mais nas nossas tags HTML para deixar as classes específicas para aquele componente.
Tudo o que precisamos para isso funcione é adicionar o atributo scoped à declaração de <style>, assim como na linha 34 do exemplo.
Para entender melhor vamos pegar a classe na linha 5.
O Vue vai transformar ela em algo como: .loading[data-v-763db97b]. Esse atributo novo sempre vai começar com data-v e vai terminar com uma hash aleatória, o que ajuda caso tenhamos classes iguais {elas não vão colidir graças ao hash} e impede que um componente distante seja afetado.
O problema dessa solução é que mesmo com esse atributo a mais na tag HTML, as regras afetam os componentes filhos. Olhando no exemplo ao lado, se o <base-icon> tivesse a classe “icon” e removêssemos a linha 12, as regras de CSS dessa classe afetariam o componente filho mesmo assim.
Podemos resolver isso facilmente evitando seletores iguais {até mesmo usando uma metologia como o BEM que vimos anteriomente}, para não correr o risco de herdar regras que não gostaríamos.
CSS Modules
Assim como a solução anterior, o CSS Modules tenta nos ajudar com esse problema de conflitar regras de CSS ou herdar regras que não gostariamos.
A diferença dele para o scoped, é que não teremos um atributo novo no HTML e sim uma classe gerada pelo Vue, composta pelo nome do componente + o nome da classe que nós colocamos no CSS + uma hash aleatória.
Para que essa solução funcione, precisamos adicionar o atributo module à declaração de <style>, assim como na linha 35 do exemplo.
Uma das coisas mais importantes aqui é que a sintaxe de declarar uma classe sempre vai ser :class=”$style.xpto” e com o nome da classe sempre em camelCase. E no caso de classes condicionais como na linha 5, 6 e 7, precisamos usar a classe dentro de colchetes.
Para entender melhor, vamos pegar a linha 5 de exemplo. No fim das contas o Vue vai gerar uma classe parecida com BaseButton_loading_1VuSe. Isso vai resolver o nosso problema de conflito de regras e não é algo que interfere nos filhos.
Mas e se você quisesse que isso acontecesse? Até agora pontuei que um componente filho herdar as regras do pai era uma coisa ruim, mas dependendo da situação é exatamente isso que nós queremos para não repetir código.
Nesse caso, você pode passar as classes como prop para o componente filho, ou usar mixins de um pré-processador e importar nos dois componentes.
Achou que tinha acabado?
Achou errado!
As três soluções que vimos até agora, são as formas oficiais do Vue de definir CSS, mas a partir daqui vamos entrar no mundo obscuro das formas alternativas.
Nós separamos duas para testar e ambas são frameworks CSS-in-JS, o que significa que nós escrevemos o CSS no JS e não precisamos mais da tag <style> nos nossos componentes.
Aphrodite
Começando pelo Aphrodite {que não tem muita coisa sobre o uso dele com o Vue na internet e eu usei esse artigo como referência para a minha implementação}.
Inicialmente, nós precisamos importar duas coisas do framework: o StyleSheet e o css {como vemos na linha 8}.
O StyleSheet vai ser o responsável por gerar as nossas regras de CSS baseado na estrutura de objeto com o nome da classe sendo a chave, e o valor sendo os atributos de CSS {linha 10 à 19}.
E para que nós possamos usar essas classes no HTML, primeiro precisamos expor elas no JS {como vemos da linha 22 à 24} e usamos o css na linha 2 {separando os nomes das classes por vírgula se necessário}.
Agora vamos voltar o mesmo componente de botão dos exemplos anteriores, mas dessa vez usando Aphrodite:
Temos algumas limitações com essa solução, e de cara o que mais me incomodou foi que por padrão todas as regras são geradas com !important. A linha 18 poderia estar importando só de ‘aphoridite’, mas para não ter o comportamento de !important, eu acabei importando as dependências necessárias de outro pacote.
Também não existe um jeito oficial de definir classes aninhados {ou seja, uma classe dentro de outra classe}, com isso precisamos ter classes diferentes para um estado específico de um elemento, como por exemplo na linha 27 e 28, onde o componente de ícone que nos exemplos anteriores só tinha uma classe, aqui nós precisamos de duas.
Com essa implementação, as classes geradas são completamente aleatórias {algo parecido com _w3twfb}, o que dificulta um pouco encontrar onde que essas regras foram definidas, mas tem a vantagem de serem nomes curtos.
Por fim, a implementação de exemplo não é a mais otimizada possível, dá para criar um plugin que importe o Aphrodite em todos os nossos componentes.
Styled Components
Por fim chegamos na última opção da nossa lista, o Styled Components, que nos permite criar componentes estilizados.
Na linha 22 e 33 temos dois componentes sendo definidos usando o styled importado do framework. O primeiro parâmetro é a tag HTML que vai ser renderizada, o segundo são as props daquele componente. A partir dai é possível retornar todas as regras de CSS necessárias {e condicionar elas dependendo de uma propriedade, o que nos dá muita liberdade}.
Por mais diferente que essa implementação seja, esses dois componentes são iguais a qualquer outro definido por um arquivo vue {claro, com as suas limitações, ele é um componente puramente visual, então não vamos ter métodos ou outros tipos de atributos nele} e precisa ser declarado como na linha 42 para ser usado pelo HTML.
Eles também não precisavam estar dentro do vue file {só coloquei aqui para facilitar o exemplo rs}. Por serem componentes normais, poderiam estar em arquivos separados para serem importados pelo base-button.
É uma solução simples para componentes básicos.
Conclusão
Depois de muita discussão e uni-duni-tê, nós escolhemos usar o CSS Modules como padrão aqui na Alice {pessoalmente, eu ainda prefiro usar o BEM, mas vamos manter isso entre eu e vc, blz? haha}.
Pelas nossas conversas, foi a opção que mais fez sentido para o nosso momento e para os nossos produtos, onde muitas pessoas precisam mexer. Mesmo sendo uma sintaxe diferente do normal, é algo rápido de aprender/entender e ganhamos uma certa liberdade com qual nome de classes vamos colocar {além de impedir certos problemas de herdar regras desnecessárias/ter seletores de tag HTML}.
Mas por que fizemos essa escolha? As outras opções são ruins?
Particularmente as soluções não oficiais não me agradam, pois é uma dependência a mais que precisamos colocar no projeto, temos uma curva de aprendizado para escrever o CSS dentro do JS, e do meu ponto de vista não é mais vantajoso do que usar uma solução oficial do Vue.
Porém, não existe solução perfeita, não tem uma forma melhor do que a outra, no fim das contas é necessário considerar o que mais faz sentido para o projeto e para as pessoas que vão dar manutenção no código.
Hoje o CSS Modules faz sentido pra gente, mas quem sabe daqui um tempo não faça mais. É importante ter a mente aberta para qualquer solução, e do meu ponto de vista, manter o mais simples possível. 🙂
Que tal fazer parte desse time?
Estamos buscando pessoas que topem o desafio de transformar a saúde no Brasil através da tecnologia. Clica aqui para saber mais das vagas que temos em aberto! 🙂