Tableless

Busca Menu

Afinal, como usar herança no CSS?

Seja o primeiro a comentar por

Herança surgiu para ajudar desenvolvedores a escreverem mesmo e produzirem mais. Vamos ver aqui como esse conceito básico das folhas de estilo pode nos ajudar no dia-a-dia.

Geralmente quando alguém começa a desenvolver interfaces web, o processo é aos trancos e barrancos. Nessa situação, iniciantes buscam aprender como se faz aquela borda arredondada ou como aplicar transparência. Por fim, acabam deixando de lado conceitos básicos de CSS. Levando isso em conta, precisamos primeiramente aprender sobre especificidade e efeito cascata. Esses dois pontos são essenciais para aprendermos como escrever folhas de estilo usando herança.

A especificidade de seletores

Um documento web possui derivadas fontes de estilo, sendo elas:

  • a folha de CSS servida pela página;
  • configurações setadas pelo usuário;
  • o estilo default aplicado pelo user agent (ex: navegadores).

A primeira é a mais importante. A folha de estilo pode sobrescrever as configurações do usuário, que por sua vez pode sobrescrever os estilos aplicados user agent.

Apesar do CSS servido pela página web ser mais específico, é importante lembrar que o usuário tem a opção de forçar o estilo desejado por ele. Porém, isso não vale para regras que recebem !important.

A W3C criou uma forma de calcular a especificidade de seletores. Para entender de forma simples, basicamente distribuímos pesos diferentes as regras aplicadas:

  • CSS inline: 1000 pontos;
  • ID: 100 pontos;
  • Classes, pseudo-classe e atributos: 10 pontos;
  • Elementos: 1 ponto.

Na prática, podemos fazer cálculos como os exemplos a seguir:

  • p.foobar: 1 classe + 1 elemento = 11 pontos.
  • div#foobar .foo .bar: 1 ID + 1 elemento + 3 classes = 131

A especificidade pode dar grandes dores de cabeça em projetos complexos, ainda mais quando o número de desenvolvedores é maior. Não é rara a aplicação com um grande número de uso do !important. Isso funciona como uma forma de quebrar a especificidade para sobrescrever uma regra.

header h1 {
  color: red;
}

/* Essa regra será mais específica. */

h1 {
  color: red !important;
}

Evite ao máximo usar !important. Os 5 minutos que você economiza fazendo uso dele podem se tornar horas no futuro. Fica a dica:

Efeito cascata: o coração do CSS

Não é a toa que o a palavra cascade está no nome nas folhas de estilo. Essa técnica é utilizada para definir o estilo a um seletor mesmo em caso de conflitos.

Pergunta: você sabe o que acontece quando existem duas regras para um mesmo seletor?

A cascata define o peso de uma regra através das seguintes características:

  • importância;
  • origem;
  • especificidade;
  • ordem de declaração.

Saber como o efeito cascata funciona irá lhe poupar boas horas de trabalho.

Mas e como o navegador lida com conflitos de regras? Primeiramente, ele vasculha todas as regras que se aplicam ao elemento. Como segundo passo, ele irá classificar os níveis de importância e suas origens. Depois disso, irá ocorrer um match das declarações com o mesmo nível de importância. Por último, se houver duas regras com o mesmo peso, a que é declarada por último ganhará.

Você já deve ter percebido que quanto mais fácil esse processo para o navegador, mais performática será sua aplicação.

Caso queira saber mais sobre efeito cascata e herança, recomendo esse post do Tableless de 2009.

A herança

A palavra herança rapidamente remete ao paradigma de Orientação a Objetos. Caso você acompanhe discussões sobre front-end, já deve saber que o termo “orientação a objetos” não é bem visto tratando-se de CSS.

Assim como você herda métodos e atributos de objetos, no CSS você herda as regras de um elemento pai.

/* 
 * Todo o conteúdo textual do documento
 * terá 16px de tamanho, pois herdam do
 * `body`.
 */

body {
  font-size: 16px;
}

É importante lembrar que nem todas as propriedades serão herdadas por elementos filho. Geralmente as propriedades que se referem ao box-model (height, width, margin, padding) não aceitam herança. Caso você queira forçar a herança, pode usar o valor inherit. Aliás, você sabe a diferença entre initial e inherit?

Podemos fazer um uso inteligente de herança para economizar várias linhas de código. É muito mais fácil especificar valores para elementos pais e utilizá-los em seus filhos do que especificar um por um. Se você der uma olhada em códigos de frameworks de CSS, irá notar que herança é fortemente usada para melhorar a manutenção do projeto.

Herança com Sass

Pré-processadores deram super poderes para desenvolvedores escreverem CSS. Em nosso contexto, @extend e mixins nos trazem benefícios, mas que devem ser usados com bastante cautela.

@extend

Podemos extender placeholders (%placeholder) e classes. Muitos autores desencorajam desenvolvedores a usarem esse recurso. O motivo é o CSS gerado.

Por exemplo, digamos que temos uma classe .error e queremos usar os estilos dela em outra classe.

.error {
  color: red;
}

.icon--error {
  @extend .error;
}

O CSS gerado será o seguinte:

.error, .icon--error {
  color: red;
}

Ou seja, o Sass não “copia” os valores, ele apenas separa o valor em uma mesma regra.
Agora digamos que em outro contexto precisamos reescrever a cor de erro para um tom mais forte.

.other_context .error {
  color: darken(red, 10%);
}

Além de criar a regra para a classe .error, o pré-processador irá aplicar a regra para as outras “instâncias” da classe:

.other_context .error, .other_context .icon--error {
  color: #cc0000;
}

Isso é péssimo. Além de tirar o controle do desenvolvedor, isso irá gerar código desnecessário e prejudicar outras áreas de uma interface. Lembre-se: tome cuidado com o bug dos 4095 seletores.

Usando placeholders, o Sass irá realmente “copiar” os estilos para a classe que possui o @extend. Porém, se o placeholder for alterado em um contexto, isso também irá gerar uma regra para as classes que o estenderam.

%error,
.error {
  color: red;
}

.icon--error {
  @extend %error;
}

.other_context .error {
  color: darken(red, 10%);
}

O output será esse:

.error {
  color: red;
}

.icon--error {
  color: red;
}

.other_context .error {
  color: #cc0000;
}

Perceba que dessa vez a instância de .error, a classe .icon--error não herdou as regras em outro contexto.

Resumindo: evite o @extend. Se for usar, vá de placeholders.

Mixins

Outra forma criar herança é usando mixins. Com eles você pode realmente copiar propriedades e valores para uma classe.

@mixin error {
  color: red;
}

.icon--error {
  @include error;
}

.label--error {
  @include error;
}

O problema com essa abordagem é que ela não será tão performática, levando em conta que ela gerará duas regras com o mesmo código:

.icon--error {
  color: red;
}

.label--error {
  color: red;
}

Porém, mixins são eficientes se você precisar de parâmetros.

Vamos continuar com nosso exemplo. Se precisarmos de uma cor mais forte, poderemos passar essa opção por parâmetro.

@mixin error($critical: false) {
  @if $critical {
    color: darken(red, 10%);
  } @else {
    color: red;
  }
}

.icon--error {
  @include error;
}

.icon--critical-error {
  @include error($critical: true);
}

O Sass irá gerar o vermelho mais escuro quando passarmos o valor true para a variável $critical:

.icon--error {
  color: red;
}

.icon--critical-error {
  color: #cc0000;
}

Concatenação de classes

Se você é purista e não gosta de pré-processadores ou prefere não usar as “mágicas” do Sass, você pode fazer uso da concatenação/composição de classes no HTML. O Bootstrap usa essa abordagem.

Basicamente você terá estilo padrão em uma classe e usará outras, se necessário, para alterar o layout.

No HTML com um elemento de botão, teríamos a classe .button. Se precisarmos criar um botão de sucesso, usamos a classe .button--success juntamente com .button.

.button {
  display: inline-block;
  padding: 10px;
  color: black;
  background-color: white;
}

.button--success {
  color: white;
  background-color: green;
}

Na minha opinião essa é a forma mais correta de usarmos herança. Sempre opte por usar features nativas do CSS ao invés das mágicas do Sass.

Conclusão

Herança é uma abordagem extremamente necessária para começar a entender o desenvolvimento de CSS escalável. Depois de entender bem o conceito, estude padrões de escrita. Olhe o código fonte de frameworks para entender a arquitetura usada.

Nunca saia escrevendo CSS até dar certo. Entenda as regras e planeja seus passos. Pensar antes de começar sem dúvidas será mais produtivo.

Publicado no dia