Artigo original “Code smells in CSS” escrito por Harry Roberts, que gentilmente me permitiu traduzi-lo para o nosso idioma.

Chris Coyier recentemente respondeu a seguinte pergunta:

O que você diria se um código CSS está um lixo? Quais são os sinais de que o código é poluído ou que o programador front-end fez m3rd@? O que você analisaria nele para determinar se ele é bom ou ruim?

Pensei em estender a resposta de Chris adicionando um tempero próprio…

Meu cotidiano se passa trabalhando em casa para a BSkyB. Trabalho em grandes sites, dos quais o último me tomou mais de um ano para desenvolver o front-end (e ainda não terminou). Para mim CSS ruim é uma coisa muito específica e problemática; quando você está trabalhando em um site por meses a fio você não pode se permitir escrever um código poluído, seja ele CSS ou não, e qualquer código ruim precisa ser re-escrito.

Vou compartilhar apenas alguns pontos que eu analisei no CSS (e há, sem dúvida, alguns que deixei passar batido) para que você possa ter uma idéia quanto a qualidade, sua manutenção e sua integridade…

Desfazendo estilos

Qualquer CSS que desfaz estilos (exceto um reset) deve soar o alarme imediatamente. A própria natureza do CSS diz que parâmetros vão descendo em cascata e herdando outros parâmetros já previamente definidos. Conjuntos de regras só deveriam herdar e acrescentar às regras anteriores, nunca desfazê-las.

Quaisquer declarações CSS como esta:

border-bottom: none;
padding: 0;
float: none;
margin-left: 0;

geralmente não são boa coisa. Se você tem que sair removendo bordas é porque provavelmente as aplicou muito cedo. Na teoria pode ser complicado, então eu mostro um exemplo simples:

h2 {
    font-size: 2em;
    margin-bottom: 0.5em;
    padding-bottom: 0.5em;
    border-bottom: 1px solid #ccc;
}

Aqui estamos dando a todos os h2s nosso font-size padrão para tamanho de fonte, e margin para espaçamento, e também um pouco de padding e uma linha na borda inferior de modo a separá-los visualmente do próximo elemento na página. Mas, talvez tenhamos uma circunstância em que nós não queremos uma linha inferior, talvez tenhamos uma situação em que um h2 possa não ter border e padding. Então teríamos algo mais ou menos assim:

h2 {
    font-size: 2em;
    margin-bottom: 0.5em;
    padding-bottom: 0.5em;
    border-bottom: 1px solid #ccc;
}
.no-border {
    padding-bottom: 0;
    border-bottom: none;
}

Aqui temos 10 linhas de código e um nome de classe meio feio. Seria mais prático se fosse dessa forma:

h2 {
    font-size: 2em;
    margin-bottom: 0.5em;
}
.manchete {
    padding-bottom: 0.5em;
    border-bottom: 1px solid #ccc;
}

Agora temos 8 linhas, nada de desfazer estilos e um nome de classe, digamos, mais sensato.

À medida que você vai descendo na folha de estilos, deve-se somente acrescentar novos estilos, não desfazê-los. Ao perceber que está tendo que desfazer estilos enquanto vai descendo pelo documento, pode ser que tenha se precipitado ao defini-los.

Este exemplo foi muito tímido, mas ajuda a ilustrar perfeitamente meu ponto. Agora imagine um CSS com milhares de linhas de código como estas… seria encheção de linguiça e muita refação desnecessária. Simplifique as coisas. Não comece com estilizações complexas para não correr o risco de desfazer todo seu trabalho lá na frente, você vai acabar escrevendo mais CSS para conseguir menos estilo.

Ao detectar um CSS que desfaz um estilo anterior, pode ter certeza que é porque algo estava mal arquitetado e a ordem em que as coisas foram desenvolvidas/escritas precisa ser repensada.

Números mágicos

Estes são particularmente um pesadelo pra mim. Detesto números mágicos.

Um número mágico é um valor usado “porque ele simplesmente funciona”. Veja o seguinte exemplo:

.site-nav {
    [estilos]
}
.site-nav > li:hover .dropdown {
    left: 0;    
    position: absolute;
    top: 37px;
}

top: 37px; nesse caso é um número mágico. A única razão pela qual ele provavelmente funciona é porque os lis dentro do .site-nav têm que ter 37px de altura, e o menu .dropdown precisa aparecer na parte inferior do mesmo.

O problema é que 37px é totalmente circunstancial e, como tal, não devemos botar muita fé nesse número. E se alguém alterar o font-size do .site-nav e então tudo tiver 29px de altura? Este número não é mais válido e um outro programador de interface vai precisar saber do que se trata para poder atualizá-lo.

O que acontece quando o Chrome exibe as lis com 37px, mas o IE as mostra com 36px? Então esse número só funciona em uma situação.

Nunca, jamais use estes números porque eles simplesmente funcionam. Nesta situação seria muito melhor substituir top: 37px; por top: 100%;, que significa basicamente “toda a altura desde o topo”.

Números mágicos trazem vários problemas. Como acima, não são confiáveis mas, também por sua natureza “porque simplesmente funcionam”, são difíceis de comunicar “de onde” e “pra que vieram” a outro programador de interface. Se tivesse um exemplo bem mais complexo que usa um número mágico – onde esse número se torna inválido – você se depararia com um ou mais dos seguintes problemas:

  • Outro programador de interface não vai saber de onde veio esse número mágico, então ele o apaga e recomeça do zero.
  • Outro programador de interface é cauteloso e, por não saber de onde veio o número mágico, tenta resolver o problema sem mexer nele. Isto significa que um número mágico obsoleto e “bugado” permanece no código e o programador simplesmente o “hackeia”. É poluição no código.

Números mágicos são ruins. Logo, logo ficam obsoletos, confundem outros desenvolvedores, não são confiáveis e não podem ser explicados.

Não há nada pior do que pegar o código de outra pessoa e ver um número inexplicável. Assim que eu vejo um número mágico no CSS eu começo a me perguntar: Por que ele está aqui? O que ele faz? Pra que serve? Como conseguir o mesmo resultado sem esse número mágico?

Evite números mágicos sempre que puder.

Seletores qualificados

ul.nav{}
a.button{}
div.header{}

Basicamente são seletores desnecessariamente precedidos por um elemento. Isto é ruim porque eles:

  • Inibem totalmente sua reutilização em outro elemento.
  • Aumentam a especificidade.
  • Aumentam a taxa de carregamento do browser (diminuindo o desempenho).

Estas são todas as características ruins. Esses seletores pode, e deve ser:

.nav{}
.button{}
.header{}

Agora eu sei que posso aplicar .nav a uma ol, posso aplicar .button para um input e, quando o site for portado para HTML5, posso rapidamente trocar minha div cabeçalho por um header sem me preocupar em invalidar a folha de estilos.

É um problema pequeno no que diz respeito a desempenho mas, não deixa de ser um problema. Por que fazer o browser procurar pela classe .button em um link quando você pode fazê-lo buscar essa classe em qualquer elemento? Ao qualificar seletores você está aumentando a taxa de carregamento do browser.

Exemplos mais extremos:

ul.nav li.active a{}
div.header a.logo img{}
.content ul.features a.button

Todos estes seletores podem ser aparados ou totalmente reescritos:

.nav .active a{}
.logo > img {}
.features-button{}

O que nos ajuda em:

  • Melhorar o desempenho
  • Permitir uma maior portabilidade
  • Reduzir a especificidade

Ao detectar seletores superqualificados eu imediatamente quero saber por que eles são escritos dessa forma e como posso reescrevê-los da forma mais simplificada possível.

Valores absolutos (hard-coded)

Assim como os números mágicos, valores absolutos (ou hard-coded) também são ruins. Um valor absoluto pode ser algo assim:

h1 {
    font-size: 24px;
    line-height: 32px;
}

line-height: 32px; aqui não é legal, deveria ser line-height: 1.333

line-heights devem sempre ser relativas afim de torná-las mais tolerantes e flexíveis. Se mudarmos o font-size de um h1, queremos que sua line-height o acompanhe. Não ter uma line-height relativa significa que, se precisarmos modificar um h1, provavelmente vamos acabar com algo parecido com isto:

h1 {
    font-size: 24px;
    line-height: 32px;
}

/* Cabecalho padrao `h1` */
.titulo {
    font-size: 36px;
    line-height: 48px;
}

Aqui temos de continuar acrescentando line-heights fixas indefinidamente já que nossa declaração inicial é absoluta. Usando qualquer unidade relativa (em ou %), teríamos somente:

h1 {
    font-size: 24px;
    line-height: 1.333;
}

/* Cabecalho padrao `h1` */
.titulo {
    font-size: 36px;
}

Pode não ser grande coisa, mas em cada elemento de texto ao longo de um projeto grande, isso tem sim um grande impacto. E se aplica em muito mais do que apenas line-heights, basicamente qualquer valor absoluto na folha de estilo precisa ser tratado com desconfiança e cautela.

Valores absolutos não são flexíveis e, portanto, devem ser evitados. Uma das únicas coisas que devem realmente ter valores absolutos são sprites, que sempre precisam ter tamanho fixo.

Ao detectar um valor absoluto na folha de estilo eu quero saber por que ele foi necessário e como isso poderia ser evitado.

Força bruta (Brute forcing)

É similar aos valores absolutos, porém um pouco mais específico. Força bruta em CSS é quando você usa números mágicos absolutos, dentre outras técnicas, para forçar um layout a funcionar. Por exemplo:

.foo {
    float: left;
    height: 59px;
    margin-left: -3px;
    position: relative;
    z-index: 99999;
}

Este CSS é terrível. Todas essas declarações são rígidas demais, específicas demais. E são claramente usadas apenas para forçar algo muito específico a aparecer onde e como foi proposto.

Este tipo de CSS é um indicativo de layout mal codificado e/ou da falta de compreensão do “modelo de caixa” (box-model).

Layouts bem codificados não precisam de força bruta. Uma sólida compreensão do modelo de caixa, do próprio layout e analisar seus estilos com frequência vão fazer com que você nunca tenha que enfrentar uma situação como esta.

Ao detectar força bruta no CSS quero saber como isso aconteceu, e o quanto teremos que reprogramar até deixar tudo em ordem.

Seletores perigosos

Um “seletor perigoso” é aquele muito amplo, muito abrangente e, por assim dizer, muito vago. Um exemplo muito simples e óbvio poderia ser o seguinte:

div {
    background-color: #ffc;
    padding: 1em;
}

Este faz gritar no ouvido de qualquer desenvolvedor: “Por que diabos você iria querer todas as divs do seu site com fundo amarelo?!” Boa pergunta. Então por que alguém iria querer ter um seletor como aside{} por exemplo? Ou header{}, ou ul{}? Seletores como estes são, de longe, muito vagos e acabarão por nos levar a desfazer muitos estilos.

Vamos analisar o exemplo do header{} mais detalhadamente:

Muita gente usa um header para delimitar o cabeçalho principal de seus sites. Até aí tudo bem. Entretanto, se você estilizar o cabeçalho principal assim:

header {
    background-color: #BADA55;
    color: #fff;
    margin-bottom: 20px;
    padding: 1em;
}

…aí é problemático. O elemento header não significa “cabeçalho principal do site” e, de acordo com a especificação, pode ser usado várias vezes em diversos contextos. Neste caso ele deveria receber um seletor tipo .cabecalho-principal{}, por exemplo.

Para servir a um propósito tão específico, o tal seletor genérico é perigoso. Seus estilos irão aparecer onde não deveriam tão logo cada área do site receba um cabeçalho. E aí você se vê desfazendo estilos (adicionando mais código), a fim de combater esta situação.

Verifique se seus seletores têm intenção de seletor.

Tome o seguinte exemplo:

ul {
    font-weight: bold;
}
header .media {
    float: left;
}

Assim que vejo terminações com seletores muito vagos ou com classes muito abstratas como as acima, eu entro em desespero. São seletores muito amplos e vão nos trazer problemas. Assim que tentarmos reutilizá-los, veremos que eles estão herdando estilos desnecessários porque, em algum lugar, há um seletor realmente amplo que os abrange.

!important

Tudo bem em usar !important. É uma ferramenta importante. No entanto, !important só deve ser usado em determinadas circunstâncias.

!important só deve ser usado de forma proativa, e não reativa.

Isso significa que vai ter momentos em que você sabe que vai sempre, sempre querer que um estilo prevaleça, e você vai perceber isso.

Por exemplo, você sempre vai querer erros em vermelho, então a regra abaixo é totalmente válida:

.texto-erro {
    color: #c00 !important;
}

Se o erro ocorrer em uma div onde o texto é sempre azul, podemos quebrar essa regra sem problemas. A mensagem deve ser clara. Nós queremos erros sempre em vermelho porque se trata de um erro. Então podemos adicionar proativamente !important.

!important é ruim quando usado de forma reativa, ou seja, para evitar problemas de especificidade ou para forçar algo no layout.

Usando !important de forma reativa é apenas uma maneira de contornar problemas causados por um CSS mal codificado. Ele não corrige todos os problemas, só os sintomas. Os problemas ainda existem, mas agora com uma super-especificidade ainda mais difícil de contornar.

Não tenho dúvidas de que !important utilizado proativamente é uma boa prática. Quando percebo o uso reativo de !important sei de imediato que se trata de algum CSS mal arquitetado. A solução é refatorar, e não acrescentar mais e mais valores absolutos.

IDs

Este assunto é muito específico para mim, e para grandes equipes. Eu escrevi sobre como IDs são uma má idéia por causa de sua especificidade elevada. Eles nunca devem ser usados em CSS. Utilize IDs em HTML para elementos identificadores e ganchos de javascript, mas nunca em CSS.

As razões são simples:

  • IDs nunca podem ser usadas mais de uma vez em uma página.
  • Classes podem existir apenas uma vez ou um milhão de vezes em uma página.
  • IDs podem muitas das vezes ter seus atributos abstraídos em muitas classes reutilizáveis.
  • Um ID é 255 vezes mais específicos de uma classe…
  • Isso significa que você precisa de 256 classes enfileiradas para substituir um ID.

Se esse último ponto não o convenceu de não usar IDs, então…

Tão logo vejo uma ID em uma folha de estilo tento substituí-la por uma classe. A especificidade é como o centro de gravidade de um projeto, então é vital mantê-lo baixo.

Exercício divertido: tente resolver este problema de forma elegante. Dica: este não é elegante, nem esse.

Nomes de classe vagos

Um nome da classe “vago” é aquele que não é específico o suficiente para a sua finalidade. Imagine uma classe .cartao. O que isso faz?

Este nome é muito vago, e nomes de classe vagos são ruins por duas razões:

  • Você não consegue abstrair sozinho seu propósito.
  • É tão vago que poderia muito facilmente ser redefinido acidentalmente por outro desenvolvedor.

O primeiro ponto é o mais simples: O que .cartao quer dizer? O que significa? O que ela estiliza? É uma classe que você adiciona a um cartão de crédito em um site de poker? Refere-se a um texto ou imagem? Difícil saber, porque é muito vago. Vamos imaginar que significa cartão de crédito; esta classe seria melhor compreendida caso fosse .img-carto-de-credito{}. Mais extensa, sim. Mas muito melhor, com certeza!

O segundo problema com nomes de classe vagos é que eles podem muito facilmente (e acidentalmente) ser realocados/redefinidos. Digamos que você está trabalhando em um site de comércio eletrônico usando .cartao novamente, e se refere ao cartão de crédito do usuário ligado à sua conta. Agora imagine que outro desenvolvedor quer acrescentar alguma funcionalidade que possibilita o envio da compra a alguém como um presente, com a opção de adicionar um cartão com mensagem personalizada. Sua tentação pode ser usar .cartao novamente em algum lugar, o que seria errado mas, em determinados (embora improváveis) eventos isso pode ser intencional.

Tudo isso pode ser evitado pelo uso de nomes de classe mais específicos. Classes como .cartao, .usuario e afins são muito vagas, tornando-as difíceis de compreender num primeiro momento, e podem ser facilmente reutilizadas/substituídas acidentalmente.

Ao ver nomes de classe vagos eu começo a ter de pensar no que se refere, e me perguntar como podemos renomeá-lo. Nome de classe deve ser o mais específico possível.

Palavra final

Então, estes são alguns dos muitos pontos que percebo estar cheirando no CSS. São pontos que eu vejo diariamente e me esforço para evitar a todo custo. Ao trabalhar em projetos maiores, que duram meses, às vezes anos, isso se torna vital para manter a ordem. Ter esses conceitos em mente é fundamental. (Eu não tenho como enfatizar o quão pequeno esse sub-conjunto de conceitos é, frente a tantos outros acerca desse assunto.)

Agora, é claro, existem exceções para cada regra, mas é preciso avaliar caso a caso. Para a maior parte, no entanto, estes são todos os pontos que dou um duro danado para evitar.

Pratique o que eu prego…

Estou mais do que ciente que este site vai contra quase todas essas regras, então eu deixei um breve comentário sobre o assunto.

Artigo original “Code smells in CSS” escrito por Harry Roberts, que gentilmente me permitiu traduzi-lo para o nosso idioma.