Tempo de leitura: 12 minutos
E aí desenvolvedor como estão suas habilidades em front-end? Como você já deve saber, front-end trata da parte mais visual de uma aplicação, ou seja, aquilo que o usuário vê, e com certeza você precisa escrever alguns estilos para deixar seu site mais atraente e compatível com todos os dispositivos.
E mesmo usando as ferramentas mais modernas de hoje em dia para criar essas aplicações, CSS ainda é o único jeito de adicionar estilos na web (e recentemente, até mesmo em aplicações nativas).
Atualmente, temos somente duas categorias para gerenciar estilos:
Pré-processadores de CSS: (ferramentas como SASS, LESS, etc)
Bibliotecas de CSS-in-JS, que é, relativamente, uma nova abordagem (bibliotecas como free-style e muitas outras)
Porém, no artigo de hoje vamos ficar na primeira categoria, e caso você esteja trabalhando ou aprendendo algo relacionado a segunda, talvez você não aproveite todas as dicas que vamos lhe ensinar.
Tudo pronto por aí? Vamos aprender agora quais regras devemos aprender para desenvolver um CSS inteligente e robusto.
// 1. Prefira usar classes
// 2. Utilize a mesma nomenclatura para nome de arquivos
// 3. Use nomenclatura consistente
// 4. Evite vazamentos de estilos para fora do componente
// 5. Evite vazamentos de estilos dentro do componente
// 6. Mantenha no mesmo lugar os arquivos de um componente
// 7. Sempre respeite os limites do componente
1. Prefira usar classes
Apenas para remover o óbvio do caminho.
Não use ID (ex: #header), porque, aonde você pensa que pode ter apenas uma instância de algo, em uma escala infinita, você irá se provar errado.
E se você estiver pensando em alguma forma de usar ID, presta atenção: Não existe ocasião onde usar ID seja melhor do que usar classe, ou seja, nunca use.
E claro que, você não deve usar elementos (ex: p) diretamente. É válido usá-los hierarquicamente em um componente, pois geralmente você acaba tendo que resetar esses estilos em um componente que não quer usá-lo.
Claro que, algumas propriedades como font, line-height e colors (conhecidas como propriedades herdadas, do inglês inherited properties) na tag body podem ser exceção se você quiser, mas, se você levar a sério a isolação de componentes, é completamente possível não precisar dessa declaração global.
Com raras exceções, seus estilos devem sempre usar classes.
2. Utilize a mesma nomenclatura para nome de arquivos
Todos os arquivos que afetam um componente específico, devem conter o mesmo nome do componente. Sem exceção.
Se você estiver visualizando o projeto no navegador e algum componente está com um comportamento estranho, você clica com o botão direito e inspeciona o mesmo, você verá algo como:
Você decora o nome do componente, volta para seu editor, usa seu atalho mágico para abrir um arquivo do projeto, começa a digitar ”head..”, e então aparece:
Header.js
Header.scss
Header.spec.js
Header.fixtures.json
Essa estreita relação para componentes correspondendo com o nome de arquivos é muito útil quando você é novo no projeto e não conhece a arquitetura dele de dentro para fora.
Na verdade, você não precisa conhecer, você precisa apenas achar o componente certo para poder trabalhar.
Existe uma razão natural para isso (as vezes, não é óbvia imediatamente): Um único arquivo de estilo deve conter estilos para apenas um escopo. Porque?
Vamos supor que nós temos um formulário de login, que só é usado no componente Header. Do lado do JS, ele é definido no arquivo chamado Header.js e não é exportado para lugar nenhum.
De início, é tentador chamar esse formulário de .myapp-LoginForm, e usar ele tanto no Header.js quanto no Header.scss. Mas vamos supor, que um novo desenvolvedor fique responsável por arrumar um pequeno erro de layout neste formulário.
No navegador dele, ele vai inspecionar o elemento para ter uma idéia de onde começar. Quando ele vai procurar no projeto, ele não consegue achar nada como LoginForm.js ou LoginForm.scss. Então ele começa utilizando grep ou qualquer magia negra possível para tentar achar o arquivo que contém essa declaração.
Neste caso, se realmente valer a pena criar um escopo para .myapp-LoginForm, separe todas as declarações em um novo componente.
3. Use nomenclatura consistente
CSS tem um único escopo global para nomes de classes e outros identificadores (como id’s, nomes para animação e etc).
Assim como aconteceu com o PHP a muito tempo atrás, a comunidade lidou com isso simplesmente usando longos e estruturados nomes para emular escopos (BEM é um exemplo). Nós escolhemos uma convenção de nomes e ficamos com ela até o final.
Vamos pegar um exemplo, vamos dizer que temos o componente .myapp-Header-link. Cada uma das 3 partes desse nome tem um significado:
myapp essa primeira parte serve para isolar nossa aplicação de outras que estiverem no mesmo DOM.
Header essa parte serve para isolar nosso componente de outros componentes da nossa aplicação
link é um nome local (dentro do escopo do nosso componente) para adicionar estilos a algo
Sendo assim, o elemento principal do componente Header, pode utilizar da classe myapp-Header.
Para um componente simples, isso é tudo que você precisa.
Seja qual for a convenção de nome que você escolha, seja consistentes utilizando ela. Um ponto extra sobre as 3 partes do nome da nossa classe, além de terem uma função, elas na verdade, querem significar algo.
Apenas olhando para a classe, você já sabe aonde ela pertence. A nomenclatura deve ser o mapa que nos guia pela estrutura do projeto de dentro dos nossos estilos.
De agora em diante, vamos assumir o esquema de nomenclatura app-Component-class, que pessoalmente, tem funcionado muito bem em vários projetos. Mas fique a vontade para escolher uma de sua preferência.
4. Evite vazamentos de estilos para fora do componente
Agora que nós estabelecemos nossa nomenclatura, precisamos usar ela para encapsular nossos componentes.
Se cada componente usa um nome de classe prefixado com um escopo único, nós podemos garantir que seus estilos nunca irão afetar elementos vizinhos. Isso funciona muito bem (veremos algumas ressalvas), mas ter que digitar o escopo toda vez se torna muito cansativo.
Uma solução simples para isso é declarar o prefixo no início do seu arquivo. No exemplo abaixo, só precisamos digitar o escopo uma única vez:
.myapp-Header { background: black; color: white; &-link { color: blue; } &-signup { border: 1px solid gray; } }
O exemplo acima está usando SASS, mas o símbolo & — talvez seja novo para você — funciona igualmente para todos os pré-processadores de CSS atuais (PostCSS, LESS e Stylus). Para completar o exemplo acima, SASS irá compilar para:
.myapp-Header { background: black; color: white; } .myapp-Header-link { color: blue; } .myapp-Header-signup { border: 1px solid gray; }
Todos os nossos padrões funcionam bem com essa abordagem, até mesmo as diferentes classes para declarar estado nos componentes (como os modificadores BEM):
.myapp-Header { &-signup { display: block; } &-isScrolledDown &-signup { display: none; } }
Que será compilado para:
.myapp-Header-signup { display: block; } .myapp-Header-isScrolledDown .myapp-Header-signup { display: none; }
Até mesmo media queries funcionam:
.myapp-Header { &-signup { display: block; @media (max-width: 500px) { display: none; } } }
Que se transforma em:
.myapp-Header-signup { display: block; } @media (max-width: 500px) { .myapp-Header-signup { display: none; } }
Com essa abordagem, fica fácil usar nomes longos e únicos para classes, sem precisar se preocupar com a repetição da digitação desses nomes.
5. Evite vazamentos de estilos dentro do componente
Falamos que prefixar o nome da classe com o nome do componente funciona muito bem para criar um escopo para os estilos. Lembra quando eu disse que havia ressalvas?
Veja os estilos a seguir:
.myapp-Header { a { color: blue; } }
E o componente com a seguinte estrutura:
+----------------------------+ | Header | | | | [home] [blog] [about] | <-- esses são elementos +----------------------------+
Parece tudo bem, certo? Apenas os elementos dentro do Header irão ser afetados pela cor azul, o CSS gerado é:
.myapp-Header a { color: blue; }
Mas e se o layout mudar? Imagine o cenário:
+-----------------------------------------+ | Header +-----------+ | | | LoginForm | | | | | | | [home] [blog] [about] | [info] | | <-- elementos | +-----------+ | +-----------------------------------------+
O seletor .myapp-Header a também afeta os elementos dentro do LoginForm, e aqui, nós quebramos nosso isolamento. Parece que prefixar um escopo único no nome da classe funciona muito bem para elementos vizinhos, mas não para elementos filhos.
Podemos arrumar isso de duas formas:
1. Nunca usar nomes de tag’s HTML nos nossos seletores. Se tudo dentro de Header for transformado em algo como, nós nunca teremos esse problema.
Mas as vezes você tem sua marcação semântica criada da maneira correta, e não quer ficar adicionando classes nos elementos, nesse caso, vamos para a próxima opção:
2. Você pode usar o seletor de elementos filhos
Se ajustarmos nosso exemplo para usar a última opção, ficaria assim:
.myapp-Header { > a { color: blue; } }
Que irá garantir o encapsulamento (e ainda podendo fazer seleções baseado na estrutura semântica do seu componente), o código gerado seria:
.myapp-Header > a { color: blue }
6. Mantenha no mesmo lugar os arquivos de um componente
Quando estamos trabalhando em um componente, uma ajuda tremenda é se tudo relacionado ao componente — JS, CSS, testes, documentação, etc — estiverem bem próximos:
ui/
├── layout/
| ├── Header.js // JS do componente
| ├── Header.scss // CSS do componente
| ├── Header.spec.js // testes do componente
| └── Header.fixtures.json // se o teste precisar de mock
├── utils/
| ├── Button.md // documentação
| ├── Button.js // …e por aí vai, você entendeu?
| └── Button.scss
Quando você está trabalhando no código, você abre seu editor, e tudo relacionado ao componente está ali, sem precisar procurar no projeto inteiro.
Existe uma união natural do seu JS e CSS para formar o DOM, é válido dizer que, se você mexer em um, cedo ou tarde vai mexer no outro.
Pense nisso como o princípio de referência de localização para componentes UI.
7. Sempre respeito os limites do componente
Assim como nós estilizamos .myapp-Header > a, quando nós aninhamos componentes, talvez iremos precisar aplicar alguns estilos para componentes filhos, imagine o seguinte cenário:
+---------------------------------+ | Header +------------+ | | | LoginForm | | | | | | | | +--------+ | | | +--------+ | | Button | | | | | Button | | +--------+ | | | +--------+ +------------+ | +---------------------------------+
De primeira, fica claro que estilizar .myapp-Header .myapp-Button é uma má ideia, na verdade nós queremos .myapp-Header > .myapp-Button. Mas, nesse caso, quais estilos nós queremos aplicar para nossos elementos filhos?
Note que, o LoginForm está encaixado do lado direito do Header. Intuitivamente, um dos estilos seria:
.myapp-LoginForm { float: right; }
Nós não violamos nenhuma de nossas regras, mas também, deixamos o LoginForm bem mais difícil de se re-usar: Se uma página qualquer quiser usar o LoginForm, mas sem o float: right, estamos sem sorte!
A solução pragmática para isso (parcialmente), é modificar nossa declaração anterior para aplicar os estilos somente no escopo que ele precisa. Ficando assim:
.myapp-Header { > .myapp-LoginForm { float: right; } }
De fato, isso até certo ponto está certo, só não podemos quebrar os estilos do componente em si:
// NÃO FAÇA ISSO .myapp-Header { > .myapp-LoginForm { color: blue; padding: 20px; } }
Nós não queremos permitir isso, com o código acima, LoginForm.scss não é mais o único lugar que você precisaria olhar para ver as modificações na aparência do componente LoginForm.
Fazer mudanças dessa maneira volta a ser frágil, aquela sensação de que algo pode sair errado. Então, onde é que nós traçamos a linha do que pode ou não pode ser modificado?
Nós queremos respeitar os limites internos de cada componente filho, já que não queremos saber sua implementação interna. É uma caixa preta que não precisamos saber.
Agora, o que está fora do componente filho, é a área do componente pai. A distinção entre dentro e fora vem de um dos conceitos mais fundamentais do CSS: o box-model.
Só significa que você está dentro de um país, quando você passa suas bordas.
Estabeleceremos aqui que, um elemento pai pode afetar os estilos dos seus elementos filhos diretos até a borda do seu país.
Isso quer dizer, ele deve se limitar as declarações de posicionamento e dimensão (tais como position, margin, display, width, float, z-index, etc) e propriedades que vão além da borda (tais como border, padding, color, font, etc) estão fora de questionamento.
Para deixar claro, aqui vai mais um exemplo do que não deve ser feito:
// NÃO FAÇA ISSO .myapp-Header { > .myapp-LoginForm { > a { // precisa saber a implementação do LoginForm ;__; color: blue; } } }
Claro que, existem alguns casos extremos como:
- box-shadow — Um tipo de sombra pode ser parte integral do design do componente, e portanto, pensamos que ele deve conter esses estilos. Novamente, esse efeito visual é renderizado fora da borda, então, na verdade, deve ser declarado no componente pai.
- color, font e outras propriedades herdadas — .myapp-Header > .myapp-LoginForm { color: red }, alcançam o interior do componente filho, mas por outro lado, essa outra declaração pode ser equivalente .myapp-Header { color: red }, o que também está correto.
- display — Se o elemento filho usa Flexbox, possivelmente ele está confiando que o elemento pai contém display: flex. Entretanto, o elemento pai pode escolher se deve esconder o elemento filho usando display: none.
Conclusão das regras para desenvolver um CSS inteligente e robusto
Gostou das dicas? Compartilha! =)
Deseja construir sites com layouts incríveis?
Conheça o Pacote Full Stack que já mudou a vida de milhares de pessoas e pode mudar a sua também!