Como desenvolvedor front-end com quase 15 anos de experiência, testemunhei a evolução do desenvolvimento web em primeira mão. Para mim, percorreu um longo caminho desde os dias de upload de arquivos modificados via FTP (sim, o GitHub foi lançado há 15 anos, mas só o descobri em 2011) até a era moderna de interfaces responsivas, bibliotecas de interface do usuário e sites gerados diretamente de Figma.
No entanto, ainda encontro projetos que empregam estilos aninhados como:
.some-class ul li div a { /* some style */ }
ou,
#nav .nav-link svg { /* some style */ }
Pode parecer chocante, mas essas práticas de codificação permeiam tudo, desde projetos multimilionários e de rápido crescimento até startups humildes.
Vamos nos aprofundar no motivo pelo qual essa abordagem pode apresentar problemas.
Estilos profundamente aninhados geralmente levam a conflitos de estilo, especialmente se você tiver um grande projeto. O CSS, como uma folha de estilo em cascata, desce em cascata e se aplica aos elementos dependendo de sua especificidade. Estilos profundamente aninhados podem substituir involuntariamente outros estilos devido à sua especificidade.
Considere este exemplo:
.some-class ul li div a { color: red; } ... .some-class a { color: blue; }
Você pode esperar que todos os links em .some-class
sejam azuis. No entanto, devido à maior especificidade da primeira regra, qualquer link aninhado em ul > li > div
será vermelho, não azul. Isso pode levar a inconsistências visuais inesperadas e muita perda de tempo na depuração.
Entender o conceito de especificidade (ou o 'peso' dos estilos) em CSS é crucial para entender por que o aninhamento profundo pode ser problemático. A especificidade determina qual regra CSS se aplica se várias regras competirem por um único elemento. É calculado com base no tipo e quantidade de seletores.
A especificidade é calculada com base em um sistema de ponderação de quatro categorias:
Aqui, considere a regra:
body #content .data img:hover { /* some style */ }
A especificidade é 0 1 2 2 . Isso é um ID ( #content ), duas classes ( .data e :hover ) e dois elementos ( body e img ).
Agora, considere a regra:
#nav .nav-link svg { /* some style */ }
A especificidade aqui é 0 1 1 1 . Isso é um ID ( #nav
), uma classe ( .nav-link
) e um elemento ( svg
).
A especificidade não opera em um sistema de "transferência" como os números decimais tradicionais. Por exemplo, um seletor com uma especificidade de 0 1 0 11 não é igual a uma especificidade de 0 1 1 1 , embora em um sistema decimal 11 e 1+1 sejam equivalentes.
Finalmente, o seletor universal ( *
), combinadores ( +
, >
, ~
, ' ') e a pseudoclasse de negação ( :not()
) não têm efeito na especificidade. Dentro do argumento :not()
, no entanto, os seletores são contados normalmente.
Para aprendizes visuais, recomendo este vídeo sobre CSS Specificity.
Entender a especificidade do CSS e como ele é calculado permite escrever CSS melhor e mais previsível e depurar problemas quando os estilos não estão sendo aplicados conforme o esperado.
!important
Regra e Especificidade Às vezes, os desenvolvedores recorrem ao uso da regra !important
quando enfrentam dificuldades com conflitos de especificidade CSS. Essa regra torna uma propriedade CSS extremamente específica, o que significa que substituirá quase qualquer outra declaração.
Por exemplo:
#nav .nav-link svg { color: blue; } .nav-link svg { color: red !important; }
Apesar da primeira regra ter uma especificidade maior devido ao seletor de ID, a cor do svg
seria vermelha por causa do !important
na segunda regra.
Embora !important
possa ser uma solução rápida ao enfrentar problemas de especificidade, não é recomendável usá-lo extensivamente. O uso excessivo pode afetar a capacidade de manutenção, a previsibilidade e o desempenho. Em projetos maiores, o uso excessivo de !important
geralmente indica uma dificuldade com o gerenciamento da especificidade do CSS. Em vez de recorrer a !important
, geralmente é melhor investir tempo refatorando seu CSS e reduzindo o uso de seletores excessivamente específicos.
Você pode conferir seu produto agora mesmo 🙂. Eu verifiquei o meu:
Embora !important
possa ser uma solução rápida tentadora; é como usar uma marreta para quebrar uma noz. Uma abordagem mais sustentável é manter seus seletores o mais simples e planos possível, o que torna seu CSS mais fácil de entender, gerenciar e estender no futuro. E lembre-se, a melhor maneira de vencer uma guerra !important
é não começar uma em primeiro lugar.
Outro problema com estilos profundamente aninhados é o impacto no desempenho na renderização do navegador. Quando um navegador aplica estilos a um elemento, ele percorre o DOM da direita para a esquerda, começando com o seletor de teclas (em nossos exemplos, a
e svg
) e percorrendo os ancestrais até encontrar uma correspondência ou chegar ao topo. Quanto mais aninhado o estilo, mais demorada essa travessia, afetando potencialmente o desempenho e diminuindo o tempo de carregamento da página em projetos de grande escala.
Quando você especifica uma regra CSS, como:
.some-class ul li a { /* some style */ }
Você pode visualizar essa regra começando na parte inferior da árvore (a partir da tag a
) e subindo pela árvore (via li
, ul
e .some-class
).
O navegador primeiro procurará por todos (quero dizer, TODOS) a
elementos a e, em seguida, verificará se essas tags a
estão dentro dos elementos li
. Em seguida, ele irá verificar se estes elementos li
estão dentro ul
E por último, irá verificar se estes ul
estão dentro de um elemento com a classe .some-class
.
É assim que os navegadores leem os seletores CSS e a razão pela qual seletores complexos podem levar a uma renderização de página mais lenta. O navegador precisa fazer várias verificações para cada elemento para ver se ele se encaixa na regra especificada. Quanto mais profunda a regra, mais verificações o navegador precisa fazer, o que pode afetar o desempenho.
Módulos CSS permitem que você escreva CSS em módulos individuais, que são definidos localmente para o componente em que você está trabalhando. Isso significa que os estilos em um módulo CSS são aplicáveis apenas a esse módulo específico e não vazarão ou afetarão outros elementos da página.
Vamos explorar como os módulos CSS usam nomes de classe com hash para garantir o encapsulamento de estilo. Quando você estiver usando Módulos CSS, os nomes de classes que você definir em seu arquivo CSS terão hash no tempo de compilação. Esse hash cria um nome de classe exclusivo que corresponde ao seu componente. Vejamos um exemplo:
Suponha que você tenha um módulo CSS definido como tal:
/* Button.module.css */ .button { color: white; background-color: blue; }
E você o usa em seu componente assim (prefiro importar o objeto de estilos como s
em vez de styles
— uma dica rápida que economiza tempo de digitação e aumenta a eficiência da codificação):
import React from 'react'; import s from './Button.module.css'; const Button = () => { return ( <button className={s.button}>Click me</button> ); }; export default Button;
Quando seu aplicativo é compilado, seu HTML renderizado pode se parecer com isto:
<button class="Button_button__3FQ9Z">Click me</button>
Neste caso, Button_button__3FQ9Z
é o nome da classe com hash que foi gerado a partir do seu módulo CSS. Observe que a estrutura exata e o comprimento do hash podem variar com base na configuração do seu projeto.
Esse nome de classe exclusivo garante que os estilos definidos em Button.module.css
se apliquem apenas a esse botão e não afetem nenhum outro elemento em seu aplicativo. Ele também garante que nenhum outro estilo possa afetar esse botão, a menos que eles tenham como alvo explícito o nome da classe com hash. Esse encapsulamento de estilos é um dos principais benefícios dos Módulos CSS.
Outra maneira popular de lidar com CSS é usando bibliotecas CSS-in-JS, como styled-components oumotion . Essas bibliotecas permitem que você escreva seu CSS diretamente no seu JavaScript, o que traz vários benefícios:
Aqui está um exemplo de como você pode usar styled-components em um aplicativo React:
import React from 'react'; import styled from 'styled-components'; const Button = styled.button` color: white; background-color: ${(props) => props.primary ? 'blue' : 'gray'}; `; const App = () => { return ( <div> <Button primary>Primary Button</Button> <Button>Secondary Button</Button> </div> ); }; export default App;
Neste exemplo, o componente Button
tem estilos dinâmicos que mudam com base em seu prop primary
.
Se você não estiver trabalhando com uma estrutura JavaScript compatível com Módulos CSS ou similar, ainda poderá gerenciar seu CSS com eficiência usando metodologias de nomenclatura como BEM (Bloco, Elemento, Modificador).
BEM significa "Block Element Modifier" e é uma metodologia que ajuda você a criar componentes reutilizáveis e compartilhamento de código em CSS. Veja como você pode estruturar seu CSS usando BEM:
/* Block */ .top-menu { } /* Element */ .top-menu__item { } /* Modifier */ .top-menu__item_active { }
No BEM, o 'Bloco' é a entidade autônoma que tem significado por conta própria, o 'Elemento' é uma parte do Bloco que não tem significado autônomo e está semanticamente vinculado ao seu Bloco, e o 'Modificador' é um sinalizador no um Bloco ou Elemento que é usado para mudar a aparência ou o comportamento.
O uso de uma metodologia consistente como o BEM pode facilitar a compreensão e a manutenção do seu CSS, principalmente em projetos maiores.
Existem várias maneiras de gerenciar CSS em projetos maiores, desde módulos CSS e bibliotecas CSS-in-JS até metodologias de nomenclatura como BEM. A chave é encontrar uma abordagem que se encaixe bem com sua equipe e projeto e aplicá-la de forma consistente. Lembre-se, escrever CSS é tanto escrever um código que seja eficiente e de alto desempenho quanto escrever um código que seja compreensível e de fácil manutenção.
Codificação feliz!