Curso de CSS / Combinadores e pseudoclasses

Combinadores selecionam elementos com base em seus relacionamentos na estrutura HTML, enquanto pseudoclasses selecionam estados ou condições estruturais. Um seletor de filho é diferente de um seletor de descendente, e :first-child refere-se ao primeiro elemento filho em vez do primeiro elemento de um tipo específico.

Leia os relacionamentos de pai, filho, descendente e irmão

Na estrutura a seguir, section é o elemento pai de h2, parágrafo A, div e parágrafo C. O parágrafo B é um filho da div, que é um descendente de section. A e C são irmãos porque têm os mesmos pais, mas A e B não são irmãos porque têm pais diferentes. A estrutura HTML é mais importante do que a aparência na tela.

<section class="garden">
  <h2>Visitor Information</h2>
  <p>A: Operating hours</p>
  <div>
    <p>B: Greenhouse Guide</p>
  </div>
  <p>C: What you need</p>
</section>
section.garden
├─ h2
├─ p  ← A
├─ div
│  └─ p  ← B
└─ p  ← C
notação de relação seletor Parágrafo selecionado Parágrafo não selecionado
espaço: descendente .garden p A, B, C p fora desta seção
>: filho direto .garden > p A, C B está dentro de uma div e não é filho direto da seção
+: o irmão seguinte h2 + p A B tem pais diferentes e há uma div antes de C.
~: irmão subsequente h2 ~ p A, C B tem um pai diferente de h2.

Um combinador normalmente tem como alvo o elemento à sua direita. h2 + p estiliza o p imediatamente seguinte a um h2 como um irmão. Nós de texto em branco provenientes de indentação ou quebras de linha não interrompem essa relação de elemento-irmão. Para mais detalhes, veja Seletores CSS Básicos: Tipo, Classe, ID e Atributos.

Compare seletores de descendentes e de filhos

.garden p { color: #087f5b; }
.garden > p { border-left: 4px solid #087f5b; padding-left: 12px; }

A primeira regra deixa todos os p em garden verdes. Ela seleciona B mesmo que haja uma div no meio. A segunda regra adiciona uma borda à esquerda e espaçamento interno apenas aos filhos diretos A e C. Como color e border-left são propriedades diferentes, A e C podem ter tanto verde quanto a borda.

Um seletor de descendente é adequado para estilização de parágrafos compartilhados em toda uma seção. Um seletor de filho direto pode excluir parágrafos dentro de cartões aninhados. Mudanças na estrutura afetam essas correspondências; evite encadear combinadores de filho desnecessários que façam a folha de estilo depender de uma profundidade rígida. Veja MDN: Combinadores.

Compare irmãos imediatos e subsequentes

h2 + p { background-color: #fff4cc; }
h2 ~ p { font-style: italic; }

A primeira regra dá um fundo amarelo ao parágrafo A, que é o elemento irmão imediatamente a seguir ao título. A segunda coloca A e C em itálico, porque ambos são elementos p irmãos posteriores de h2. B tem outro pai e não corresponde. Nenhum destes combinadores seleciona irmãos anteriores.

Se você inserir <div>Additional notice</div> entre h2 e A, o fundo amarelo desaparecerá porque A não é mais o irmão imediato. No entanto, a condição de que ele segue h2 dentro do mesmo pai continua válida, então o itálico permanece. Ver essa diferença vai poupar você de ter que memorizar + e ~ como “seletores similares.”

Selecione estados e posições com pseudoclasses

a:hover { background-color: #fff4cc; }
a:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 3px; }
.plants li:first-child { font-weight: bold; }
.plants li:nth-child(odd) { background-color: #e9f7ef; }

:hover seleciona o estado em que o ponteiro está sobre o elemento. :focus-visible seleciona o estado no qual o navegador determina que a exibição de foco é necessária, sendo útil para verificar ao percorrer elementos com a tecla Tab. :focus seleciona o próprio estado focado, portanto, o significado dos dois não é exatamente o mesmo. Este exemplo especifica um contorno adicional sem remover o indicador de foco padrão. Você pode continuar praticando como distinguir cor e foco por estado em um link real em Estados de links e foco visível do teclado.

Quando você passa o mouse sobre o link, o fundo fica amarelo e, quando você navega até o link usando o teclado (tab), um contorno azul aparece. Em um ambiente de toque, o efeito de hover não pode ser garantido para o usuário. Deixe as informações essenciais, como horários de funcionamento ou nomes de links, ainda legíveis por padrão.

:first-child seleciona se é o primeiro elemento filho de seu pai. :nth-child(odd) seleciona casos em que a ordem dos elementos filhos é ímpar, como 1, 3 ou 5. A lista de plantas acima possui apenas li, então a hortelã número 1 fica em negrito e cria um fundo para os itens 1 e 3. Se você usar par (even) em vez de ímpar (odd), os números 2 e 4 serão os alvos.

p:first-child significa “um elemento p que é o primeiro filho de seu pai”, em vez de “o primeiro p”. O primeiro filho de section neste exemplo é h2, então .garden > p:first-child não corresponde a nenhum parágrafo. nth-child conta todos os elementos irmãos, não apenas os elementos com a mesma tag. Use uma condição como nth-of-type quando você precisar de posições dentro de um tipo de elemento. A referência da pseudo-classe MDN explica essas condições de estado e posição.

Execute o exemplo completo

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Small Garden</title>
  <style>
body { margin: 24px; font-family: sans-serif; line-height: 1.7; }
    .garden p { color: #087f5b; }
    .garden > p { border-left: 4px solid #087f5b; padding-left: 12px; }
    h2 + p { background-color: #fff4cc; }
    h2 ~ p { font-style: italic; }
    .plants li:first-child { font-weight: bold; }
    .plants li:nth-child(odd) { background-color: #e9f7ef; }
    a:hover { background-color: #fff4cc; }
    a:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 3px; }
  </style>
</head>
<body>
<h1>Small Garden</h1>
  <section class="garden">
    <h2>Visitor Information</h2>
    <p>A: We open at 9am every day.</p>
    <div>
      <p>B: Please observe quietly in the greenhouse.</p>
    </div>
    <p>C: Bring a water bottle and comfortable shoes.</p>
  </section>
  <ul class="plants">
    <li>1: Mint</li>
    <li>2: Basil</li>
    <li>3: Rosemary</li>
    <li>4: Time</li>
  </ul>
  <a href="#contact">Go to the contact section</a>
  <h2 id="contact">Contact</h2>
  <p>Ask at the information desk.</p>
</body>
</html>

Quando você salva e executa, A·B·C estão verdes, e apenas A·C tem uma borda à esquerda. A tem fundo amarelo, e A·C tem texto em itálico. Como h2 + p e h2 ~ p não estão limitados à seção .garden, o parágrafo após o título Contact também fica com fundo amarelo e em itálico. A lista de plantas tem o primeiro item em negrito e os itens ímpares têm um fundo claro. Verifique os links usando o mouse e teclado, respectivamente. As letras A~C na frente dos parágrafos são letras escritas diretamente no texto para comparar os resultados da seleção.

Altere a estrutura e teste cada condição

  1. Adicione uma div entre h2 e A. Verifique que apenas o fundo de A desaparece, mantendo o itálico e as bordas. Depois de praticar, exclua a div adicionada.
  2. Faça B ser um filho direto da seção, removendo apenas a tag div que envolve o parágrafo B. Certifique-se de que B também tenha uma borda esquerda e texto em itálico. Entre A, B e C, apenas A continua a corresponder à condição +. Após a confirmação, restaure a div.
  3. Substitua :nth-child(odd) por :nth-child(even). Verifique se o fundo da lista se move dos números 1 e 3 para os números 2 e 4, e se as letras em negrito para o primeiro filho ainda permanecem no número 1.
  4. Afaste o mouse, clique dentro da página e use tab para ir até o link. Certifique-se de que o contorno de foco esteja visível e de que você possa ir para a seção de contato com Enter. Se a barra de endereços do navegador estiver com foco, ele retorna à página primeiro.

Por favor, verifique cada condição separadamente. Alterar apenas o valor da cor para expandir a faixa de seleção ou adicionar hover ao problema de foco do teclado não resolverá a causa. Nas ferramentas de desenvolvedor, você pode comparar com a capacidade de forçar o estado a ser ativado, mas, no final, é melhor verificar os resultados visíveis ao usuário através do movimento real da tecla Tab e dos movimentos do mouse.