Vale lembrar que dá para combinar esses seletores.
Por exemplo, um seletor de elemento junto com a classe: p.intro seleciona os parágrafos que contém a classe intro. Qualquer outro elemento que tiver esta classe não será selecionado. Ou seja, se eu tiver este CSS:
p.intro {
color: red;
}
p {
color: green;
}
E este HTML:
<p>verde</p>
<p class="intro">vermelho</p>
<div class="intro">não muda de cor</div>
O primeiro parágrafo fica verde, o segundo fica vermelho e o div não muda de cor.
Da mesma forma, p#intro seleciona somente o parágrafo cujo id é intro.
E claro, dá para fazer combinações ainda mais complexas, como div.intro a[target="_blank"]. Neste caso, vai pegar os elementos a que contém o atributo target igual a _blank, e que estão dentro de uma div com a classe intro. Ou seja, com este CSS:
div.intro a[target="_blank"] {
color: red;
}
div a {
color: blue;
}
E este HTML:
<div class="intro">
<a href="/" target="_blank">vermelho</a>
<a href="/">azul</a>
</div>
<div>
<a href="/" target="_blank">outro azul</a>
<a href="/">e mais outro azul</a>
</div>
Somente o primeiro link fica vermelho, e todos os outros ficam azuis.
E também daria para fazer algo como a[target="_blank"].intro: seleciona somente elementos a que possuem o atributo target igual a _blank e a classe intro. Enfim, dá para combinar da maneira que precisar.
Quanto aos seletores de descendentes, existe o > que serve para selecionar somente o descendente direto.
Por exemplo, com este CSS:
span {
background-color: lightblue;
}
div span {
background-color: gray;
}
div > span {
background-color: yellow;
}
E este HTML:
<div>
<span>
Fundo amarelo
<span>Fundo cinza</span>
</span>
</div>
<span>Fundo azul claro</span>
O primeiro span é descendente direto do div, então o estilo definido em div > span se aplica e ele fica com fundo amarelo. Já o segundo span, apesar de estar dentro do div, não é descendente direto deste (pois ele está dentro do primeiro span), então o estilo div span é aplicado a ele, ficando com o fundo cinza. E o terceiro span não está dentro de nenhuma div, então ele fica com fundo azul claro.
E existem outros como os sibling combinators, que procuram elementos "irmãos" (que estão dentro do mesmo elemento, e na mesma hierarquia). Por exemplo, com este CSS:
h1 ~ span {
color: red;
}
E este HTML:
<div>
<span>antes</span>
<h1>abc</h1>
<span>vermelho 1</span>
<p>blabla <span>não sou sibling</span></p>
<span>vermelho 2</span>
</div>
Somente o segundo e o último span's ficam vermelhos, pois h1 ~ span pega apenas os elementos span que são "irmãos" de h1, aparecem depois dele e estão no mesmo nível da hierarquia (ambos possuem o mesmo elemento "pai", que no caso é a div). O primeiro span está no mesmo nível mas aparece antes, por isso não é afetado. E o terceiro span não está no mesmo nível, pois o elemento "pai" dele é o p.
E se mudarmos o seletor para h1 + span, somente o segundo span fica vermelho, pois agora ele pega somente o span que está imediatamente depois de h1.