Seletores e cascata CSS: estilize sem adivinhar
Aprenda a encontrar elementos com seletores CSS e entenda como a cascata escolhe a regra vencedora, com uma vitrine testada no navegador.
Seletores CSS encontram os elementos que você quer estilizar; a cascata decide qual valor fica quando mais de uma regra tenta alterar a mesma propriedade. Com essas duas ideias, você consegue olhar uma página e explicar por que cada cor apareceu, em vez de trocar valores até alguma coisa funcionar.
Nesta lição, a gente vai montar dois cartões da Livraria Beira-Rio. O HTML, o
CSS e as medições foram executados no Google Chrome 151.0.7922.170. Se a sintaxe
de class e de data-* ainda parece nova, passe antes por
tags e atributos HTML e volte para continuar.
O endereço que leva a regra até o elemento
Imagine a separação de encomendas de um prédio. “Entregue no prédio” é amplo; “entregue nos apartamentos do sexto andar” reduz o grupo; “entregue no 604” aponta para um destino específico. Um seletor CSS faz esse trabalho de localização: ele descreve quais elementos da árvore HTML combinam com a regra.
Nesse mapa, o prédio é o documento, os apartamentos são os elementos, e nomes
como .produto-card ou [data-categoria="livro"] são critérios do endereço. O
bloco entre chaves contém as declarações que serão entregues. A comparação tem
um limite importante: o navegador não “entende a intenção” do endereço. Ele
compara a sintaxe do seletor com a árvore HTML, seguindo regras formais.
Comece pela vitrine abaixo. Há dois article, ambos com a classe
produto-card, mas apenas o primeiro tem o atributo data-categoria="livro".
<div class="vitrine">
<article class="produto-card destaque" data-categoria="livro">
<h2>O Cortiço</h2>
<p class="status">Últimas unidades</p>
<button class="botao-comprar" type="button">Adicionar</button>
</article>
<article class="produto-card" data-categoria="papelaria">
<h2>Caderno</h2>
</article>
</div>Agora aplique três formas de localizar essas peças:
.produto-card {
padding: 12px;
}
[data-categoria="livro"] {
background: rgb(255 247 214);
}
.vitrine > .produto-card {
border-left: 6px solid rgb(30 100 50);
}.produto-card é um seletor de classe e encontra os dois cartões. O trecho
entre colchetes é um seletor de atributo e encontra apenas o livro. O > é
um combinador de filho direto: ele só aceita cartões que estejam logo dentro
da vitrine, sem outro elemento entre os dois.
Uma forma objetiva de conferir o endereço é usar querySelectorAll. Ele usa a
mesma sintaxe de seletores e devolve quantos elementos combinaram:
console.log('cartões:', document.querySelectorAll('.produto-card').length);
console.log(
'livros:',
document.querySelectorAll('[data-categoria="livro"]').length,
);
console.log(
'filhos diretos:',
document.querySelectorAll('.vitrine > .produto-card').length,
);Essa saída é um hábito de depuração útil: antes de discutir cor, confirme se o seletor chegou aos elementos esperados. Um seletor que encontra zero elementos não perde na cascata; ele nem entra na disputa.
Como ler uma regra CSS sem decorar
Uma regra completa tem seletor e bloco de declarações. Em cada declaração, vem
uma propriedade, dois-pontos, um valor e ponto e vírgula. Leia a primeira
linha abaixo como uma frase: “para todo elemento com a classe status, defina a
propriedade color como este tom de verde”.
.status {
color: rgb(12 96 56);
font-weight: 700;
}O seletor responde quem. color e font-weight respondem o quê. Os
valores respondem como fica. Separar as três perguntas torna uma regra longa
menos assustadora e ajuda a pesquisar a parte certa na documentação.
Os seletores que mais aparecem no começo são estes:
| tipo | sintaxe | o que encontra na vitrine |
|---|---|---|
| elemento | article |
todos os artigos |
| classe | .produto-card |
todo cartão com essa classe |
| id | #ofertas |
o único elemento com esse id |
| atributo | [data-categoria="livro"] |
o cartão marcado como livro |
| pseudo-classe | .botao-comprar:hover |
o botão enquanto o ponteiro está sobre ele |
Você pode juntar critérios. .produto-card.destaque exige as duas classes no
mesmo elemento. Também pode descrever relações: .produto-card .status encontra
um descendente com classe status em qualquer profundidade; o > exige um
filho direto. Essa diferença fica importante no erro reproduzido daqui a pouco.
Quando duas instruções chegam ao mesmo cartão
O nome Cascading Style Sheets pode ser traduzido como “folhas de estilo em cascata”. Cascata é o algoritmo usado para ordenar declarações concorrentes. No exemplo a seguir, as duas regras encontram o mesmo parágrafo e tentam definir a mesma propriedade:
.status {
color: rgb(12 96 56);
--regra-status: "primeira regra";
}
.status {
color: rgb(148 44 44);
--regra-status: "segunda regra de mesma força";
}As duas têm a mesma origem, importância e força do seletor. Nesse empate, a ordem de aparição decide, e a segunda declaração vence. Uma propriedade personalizada guarda o nome da vencedora para a gente conferir no estilo calculado do navegador:
const status = document.querySelector('.status');
const estilo = getComputedStyle(status);
console.log('cor:', estilo.color);
console.log('regra:', estilo.getPropertyValue('--regra-status').trim());Então a última regra sempre vence? Não. Antes da ordem, a cascata considera a
origem da declaração, se existe !important, as camadas, a especificidade e,
quando aplicável, a proximidade de escopo. A ordem no arquivo é o desempate
final. A lição de especificidade no CSS abre
essa conta sem recorrer a “tentativa e erro”. Por enquanto, mantenha seletores
curtos e classes reutilizáveis.
Herança é outro mecanismo. Certas propriedades, como color, passam do pai
para os descendentes quando eles não receberam um valor próprio; border e
margin, em geral, não passam. A herança fornece um valor, e a cascata resolve
as declarações que realmente disputam aquela propriedade. Os nomes se
relacionam, mas não são sinônimos.
Uma pseudo-classe mostra uma situação passageira
Pseudo-classe é um seletor que representa um estado. :hover combina enquanto
o dispositivo aponta para o elemento; :focus-visible ajuda a mostrar onde
está o foco do teclado; :checked reconhece um controle marcado. Ela não cria
uma classe no HTML.
.botao-comprar {
border: 0;
padding: 8px 12px;
background: rgb(37 99 235);
color: white;
transform: translateY(0);
}
.botao-comprar:hover {
background: rgb(30 64 175);
transform: translateY(-2px);
}No teste automatizado, o navegador leu os estilos antes e depois de posicionar o ponteiro no botão:
const botao = document.querySelector('.botao-comprar');
function estadoDoBotao() {
const estilo = getComputedStyle(botao);
return {
background: estilo.backgroundColor,
transform: estilo.transform,
};
}
console.log(estadoDoBotao());O -2 no último número da matriz confirma o deslocamento vertical de dois
pixels. Em telas de toque, hover não deve carregar uma informação essencial,
porque nem todo dispositivo mantém esse estado. Use-o como reforço visual.
O espaço que faz o seletor procurar outra pessoa
Agora reproduza um erro pequeno e muito comum. O cartão do livro tem as classes
produto-card e destaque no mesmo article. Alguém escreve este seletor:
.produto-card .destaque {
outline: 3px solid tomato;
}O espaço diz “procure um elemento .destaque dentro de
.produto-card”. Só que não existe esse descendente; as duas classes estão na
mesma pessoa. O navegador não exibe uma mensagem de erro, porque o seletor é
válido. Ele apenas combina com zero elementos.
console.log(
'com espaço:',
document.querySelectorAll('.produto-card .destaque').length,
);
console.log(
'sem espaço:',
document.querySelectorAll('.produto-card.destaque').length,
);Corrija removendo o combinador:
.produto-card.destaque {
outline: 3px solid tomato;
}O padrão de investigação é mais importante que decorar o caso: abra o painel
Elements, busque o seletor ou rode document.querySelectorAll, confira a
quantidade, e só depois analise a cascata. Isso separa “não encontrei o alvo” de
“encontrei, mas outra regra venceu”.
Sua missão: criar um estado verificável
Copie um dos cartões e troque o atributo dele para
data-categoria="promocao". O novo requisito é pintar apenas essa categoria e
dar uma borda ao cartão que também tiver a classe destaque.
<article class="produto-card destaque" data-categoria="promocao">
<h2>Memórias Póstumas de Brás Cubas</h2>
<p class="status">30% de desconto</p>
</article>[data-categoria="promocao"] {
background: rgb(254 226 226);
}
.produto-card.destaque {
border-left: 6px solid rgb(185 28 28);
}O critério de sucesso tem duas partes: document.querySelectorAll precisa
devolver 1 para cada seletor, e o inspetor do navegador deve mostrar as duas
declarações ativas no mesmo article. Depois, mova a regra do atributo para o
começo do arquivo. O resultado não muda, porque ela não disputa a mesma
propriedade da borda.
Na sequência, aprenda por que largura, preenchimento e borda mudam o tamanho real de cada cartão em box model no CSS. Quando o layout chegar a fileiras e alinhamento, continue em Flexbox no CSS. E, se quiser alterar uma classe em resposta a um clique, conecte este fundamento a como alterar CSS com JavaScript.
Perguntas frequentes
Qual é a diferença entre seletor e propriedade CSS?
A última regra CSS sempre vence?
Posso usar o mesmo nome de classe em vários elementos?
Por que um espaço muda tanto um seletor CSS?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Google Chrome 151.0.7922.170, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — CSS selectors — developer.mozilla.org
- MDN — Cascade, specificity, and inheritance — developer.mozilla.org
- Selectors Level 4 — W3C — w3.org
- CSS Cascading and Inheritance Level 5 — W3C — w3.org


