Especificidade

O podcast do CSS - 003: Especificidade

Suponha que você esteja trabalhando com o seguinte HTML e CSS:

<button class="branding">Hello, Specificity!</button>
.branding {
  color: blue;
}

button {
  color: red;
}

Há duas regras que segmentam o mesmo elemento. Cada regra contém uma declaração que quer definir a cor do botão: uma tenta colorir o botão de vermelho e a outra tenta colorir de azul. Qual declaração é aplicada ao elemento?

Entender o algoritmo de especificidade do CSS é fundamental para entender como o CSS decide entre declarações concorrentes.

A especificidade é um dos estágios distintos da cascata, que foi abordada no último módulo, sobre a cascata.

Pontuação de especificidade

Cada regra de seletor em uma origem recebe uma pontuação. Você pode pensar na especificidade como uma pontuação total, e cada tipo de seletor ganha pontos para essa pontuação. As declarações de regras com a maior especificidade vencem.

Com a especificidade em um projeto real, o equilíbrio é garantir que as regras de CSS que você espera aplicar sejam aplicadas , mantendo as pontuações baixas para evitar complexidade. A especificidade só deve ser tão alta quanto precisamos, em vez de buscar a maior especificidade possível. No futuro, alguns CSSs genuinamente mais importantes podem precisar ser aplicados. Se você buscar a maior especificidade, vai dificultar esse trabalho.

A especificidade não é um número decimal, mas uma tríade que consiste em três componentes: A, B e C.

  • A: especificidade semelhante a ID
  • B: especificidade semelhante a classe
  • C: especificidade semelhante a elemento

Geralmente, ela é representada usando a notação (A,B,C). Por exemplo: (1,0,2). A notação alternativa A-B-C também é usada com frequência.

Um diagrama mostrando os três componentes da especificidade (A,B,C). Para cada componente, o diagrama mostra o que ele representa e alguns exemplos de seletores que o afetam.
Um diagrama que demonstra qual componente de especificidade vários seletores afetam.

Como comparar especificidades

As especificidades são comparadas comparando os três componentes em ordem: a especificidade com um valor A maior é mais específica; se os dois valores A estiverem empatados, a especificidade com um valor B maior será mais específica; se os dois valores B também estiverem empatados, a especificidade com um valor C maior será mais específica; se todos os valores estiverem empatados, as duas especificidades serão iguais.

Por exemplo, (1,0,0) é considerada uma especificidade maior que (0,4,3) porque o valor A em (1,0,0) (que é 1) é maior que o valor A de (0,4,3) (que é 0).

Os seletores influenciam a especificidade

Cada parte da tríade de especificidade começa com um valor de 0, então a especificidade padrão é (0,0,0). Cada parte de um seletor aumenta a especificidade que, dependendo do tipo de seletor, incrementa o valor de A, B ou C.

Seletor universal

Um seletor universal (*) não adiciona especificidade, deixando o valor na especificidade inicial de (0,0,0).

* {
  color: red;
}

Seletor de elemento ou pseudo-elemento

Um elemento (tipo) ou pseudo-elemento seletor adiciona especificidade semelhante a elemento que incrementa o componente C em 1.

Os exemplos a seguir têm uma especificidade geral de (0,0,1).

Seletor de tipo

div {
  color: red;
}

Seletor de pseudo-elemento

::selection {
  color: red;
}

Seletor de classe, pseudo-classe ou atributo

Um seletor de classe, pseudo-classe ou atributo adiciona especificidade semelhante a classe que incrementa o componente B em 1.

Os exemplos a seguir têm uma especificidade de (0,1,0).

Seletor de classe

.my-class {
  color: red;
}

Seletor de pseudo-classe

:hover {
  color: red;
}

Seletor de atributo

[href='#'] {
  color: red;
}

Seletor de ID

Um seletor de ID adiciona especificidade semelhante a ID, que incrementa o componente A em 1, desde que você use um seletor de ID (#myID) e não um seletor de atributo ([id="myID"]).

No exemplo a seguir, a especificidade é (1,0,0)

#myID {
  color: red;
}

Outros seletores

O CSS tem muitos seletores. Nem todos eles adicionam especificidade. Por exemplo, a :not() pseudo-classe não adiciona nada ao cálculo de especificidade.

A especificidade da pseudo-classe :not() é substituída pela especificidade do seletor mais específico no argumento de seletores separados por vírgulas, fornecendo a mesma especificidade como se tivesse sido escrita :not(:is(argument)).

div:not(.my-class) {
  color: red;
}

A :is() pseudo-classe também não adiciona ao cálculo de especificidade. Como :not(), ela usa a especificidade do argumento mais específico.

:is(h1, h2, h3) {
  color: blue;
}

Este exemplo tem uma especificidade de (0,0,1) porque tem apenas um tipo de seletor (type).

Com a adição de um id, a especificidade aumenta para (1,0,0).

:is(h1, h2, h3, #my-heading) {
  color: blue;
}

A :where() pseudo-classe é diferente. Não importa a especificidade de qualquer um dos argumentos, ela sempre tem uma especificidade de (0,0,0).

:where(h1, h2, h3, #my-heading) {
  color: blue;
}

Os estilos de baixa especificidade aplicados usando :where() permitem substituir esses estilos com seletores básicos mais tarde na sua folha de estilo:

:where(#my-content) {
   color: red;
}

p {
   color: blue;
}

Mesmo que :where() tenha um id na lista de argumentos, ele ainda tem apenas uma especificidade de (0, 0, 0), então o seletor p básico o substitui e o texto será blue.

Teste seu conhecimento

Teste seu conhecimento sobre a pontuação de especificidade

Qual é a especificidade de a[href=/learn/css/%22#%22%5D%3C%2Fcode>?

(0,0,1)
O a vale (0,0,1), mas o [href=/learn/css/%22#%22%5D%3C%2Fcode> vale (0,1,0).
(0,1,0)
Tente de novo. O a vale (0,0,1), mas o [href=/learn/css/%22#%22%5D%3C%2Fcode> vale (0,1,0).
(0,1,1)
O a vale (0,0,1) e o [href=/learn/css/%22#%22%5D%3C%2Fcode> vale (0,1,0), o que resulta em uma especificidade total de (0,1,1).

Fatores que não afetam a especificidade

Há alguns equívocos comuns sobre os seguintes fatores que afetam a especificidade.

Atributos de estilo in-line

O CSS aplicado diretamente ao atributo style de um elemento, não afeta a especificidade, já que é uma etapa diferente em cascata que é avaliada antes da especificidade.

<div style="color: red"></div>

Para substituir essa declaração em uma folha de estilo, é necessário recorrer para que a declaração vença em uma etapa anterior de cascata.

Por exemplo, você pode adicionar !important a ela, para que ela se torne parte da origem !important criada.

Declarações !important

Um !important no final de uma declaração CSS não afeta a especificidade mas coloca a declaração em uma origem diferente origem, ou seja, Authored !important.

No exemplo a seguir, a especificidade de .my-class não é relevante para que a declaração !important vença.

.my-class {
  color: red !important;
  color: white;
}

Quando duas declarações são !important, a especificidade entra em jogo novamente, já que a etapa de origem da cascata ainda não conseguiu determinar o vencedor.

.branding {
  color: blue !important;
}

button {
  color: red !important;
}

Especificidade no contexto

Quando um seletor complexo ou composto é usado, cada parte desse seletor é adicionada à especificidade. Considere este exemplo de HTML:

<a class="my-class another-class" href="#">A link</a>

Esse link tem duas classes. A regra no CSS a seguir tem uma especificidade de (0,0,1):

a {
  color: red;
}

Se você referenciar uma das classes no seletor, ela agora terá uma especificidade de (0,1,1):

a.my-class {
  color: green;
}

Adicione a outra classe ao seletor, agora ela tem uma especificidade de (0,2,1):

a.my-class.another-class {
  color: rebeccapurple;
}

Adicione o atributo href ao seletor, agora ele tem uma especificidade de (0,3,1):

a.my-class.another-class[href] {
  color: goldenrod;
}

Por fim,adicione uma :hover pseudo-classe a tudo isso, o seletor acaba com uma especificidade de (0,4,1):

a.my-class.another-class[href]:hover {
  color: lightgrey;
}

Teste seu conhecimento

Teste seu conhecimento sobre a pontuação de especificidade

Qual dos seguintes seletores tem uma especificidade de (0,2,1)?

article > section
Os elementos adicionam especificidade semelhante a elemento (componente `C`). Há dois elementos no seletor, o que faz com que ele tenha uma especificidade de (0,0,2).
article.card.dark
Os elementos adicionam especificidade semelhante a elemento (componente `C`) e as classes adicionam especificidade semelhante a classe (componente `B`). Com duas classes e um elemento, esse seletor tem uma especificidade de (0,2,1).
article:hover a[href]
Os elementos adicionam especificidade semelhante a elemento (componente `C`), e as pseudo-classes e atributos adicionam especificidade semelhante a classe (componente `B`). Há dois seletores de elementos (2 × (0,0,1)), um seletor de atributo (que vale (0,1,0)) e um seletor de classe (que vale (0,1,0)). Isso faz com que esse seletor tenha uma especificidade total de (0,2,2).

Aumentar a especificidade de forma pragmática

Suponha que você tenha um CSS como este:

.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

Com HTML como este:

<button class="my-button" onclick="alert('hello')">Click me</button>

O botão tem um plano de fundo cinza, porque o segundo seletor tem uma especificidade de (0,1,1). Isso ocorre porque ele tem um seletor de tipo (button), que é (0,0,1) e um seletor de atributo ([onclick]), que é (0,1,0).

A regra anterior, .my-button, é igual a (0,1,0) porque tem um seletor de classe, que é uma especificidade menor que (0,1,1).

Se você quiser dar um impulso a essa regra, repita o seletor de classe desta forma:

.my-button.my-button {
  background: blue;
}

button[onclick] {
  background: grey;
}

Agora, o botão terá um plano de fundo azul, porque o novo seletor tem uma especificidade (0,2,0)

Um empate na especificidade volta para a próxima etapa na cascata

Continuando com o exemplo de botão por enquanto, mude o CSS para este:

.my-button {
  background: blue;
}

[onclick] {
  background: grey;
}

O botão tem um plano de fundo cinza, porque ambos os seletores têm uma especificidade idêntica de (0,1,0).

Se você mudar as regras na ordem de origem, o botão ficará azul.

[onclick] {
  background: grey;
}

.my-button {
  background: blue;
}

Isso ocorre porque ambos os seletores têm a mesma especificidade. Nesse caso, a cascata volta para a etapa de ordem de aparência.

Recursos