Deixando de só pseudo-entender as pseudo-classes de CSS
Tags: css, frontend, webdev, braziliandevs
Published: 2020-04-14 22:30:41

**Resumo**: Um guia das pseudo-classes em CSS para todos os níveis.

Oi gente, resolvi criar um post detalhado sobre pseudo-classes em CSS com o objetivo de ajudar pessoas desenvolvedoras a aprender ou entender mais sobre essas classes. Algumas são utilizadas com frequência como `:hover` ou `:first-child`, mas será que ainda temos dúvidas sobre a diferença de `:first-child` e `:first-of-type?`? Vamos descobrir 👀

## O que são pseudo-classes em CSS?

Pseudo-classes são classes pré-definidas em CSS que, de acordo com certas condições, modificam o estilo de elementos HTML.
Essas condições mudam de acordo com cada classe. Diferente das classes normais em CSS, nós não podemos escolher um nome personalizado para cada uma e as classes utilizam dois pontos (:) no lugar do tradicional ponto final (.).

Para facilitar a leitura desse artigo vou distinguir as pseudo-classes em quatro grupos:
1. Pseudo-classes dinâmicas;
2. Pseudo-classes estruturais; 
3. Pseudo-classes relacionais;
4. Pseudo-classes textuais;

Agora vamos entender quais são as principais pseudo-classes de cada grupo e como podemos utilizá-las.

### 1. Pseudo-classes dinâmicas
Essas pseudo-classes são muito utilizadas e (na minha opinião) as mais conhecidas. Alguns exemplos são:

```css
:hover
:active
:focus
:disabled
:checked
:required
```

Se você já desenvolveu um elemento de navegação/menu em CSS você provavelmente já utilizou alguma dessas classes. Se você está começando e nunca viu nenhuma delas, não se preocupe! 😉

#### :hover
O hover é bem conhecido e tem como finalidade alterar o estilo de algum elemento quando o mouse passa por cima dele. Por exemplo, queremos o texto dos links mude pra azul quando passamos o mouse por cima, pra isso podemos utilizar o *:hover*. O hover pode ser utilizado com qualquer elemento HTML.

```css
a:hover {
  background-color: blue;
}
```

#### :active 
Essa pseudo-classe modifica o estilo de links que estão ativos, ou seja, que acabaram de ser clicados. **Atenção: o :active só funciona para links!**

```css
a:active {
  background-color: blue;
}
```

#### :checked
O checked funciona somente para inputs dos tipos checkbox ou radio. Essa pseudo-classe modifica o estilo quando o input foi selecionado (checked) pelo usuário.

```html
<input type="checkbox" name="checkbox" id="checkbox">
<label for="checkbox">Eu amo morangos Toyonoka!</label>
```
```css
input:checked ~ label {
  background-color: green;
}
```

No exemplo, modificamos a cor de fundo da label mais próxima quando o input foi selecionado.

#### :required
O required pode ser utilizado em qualquer elemento parte de um form/formulário, como inputs, selects e textareas. Ele modifica o estilo somente dos campos que não podem estar vazios para a submissão válida do formulário.

```html
<input type="text" name="name" placeholder="Name" required>
```
```css
input:required {
  background-color: red;
}
```

**As próximas pseudo-classes podem ser utilizadas somente em alguns elementos, segundo a [W3C](https://www.w3.org/TR/html50/disabled-elements.html), eles são:**
* button
* input
* select
* textarea
* optgroup
* option
* fieldset

#### :focus
O focus modifica o estilo de um elemento que está em foco, alguns exemplos são quando navegamos em um site usando a tecla *tab* no lugar do mouse, ou quando selecionamos um input para preencher um formulário. Através dessa pseudo-classe podemos indicar qual elemento de uma navegação ou de um formulário possuem o foco do mouse/teclado. Por questões de acessibilidade, é importante **sempre** utilizar essa pseudo-classe para estilizar os elementos que estão em foco.

```html
<button>Meu botão</button>
```
```css
button:focus {
  background-color: purple;
}
```

#### :disabled
O disabled modifica o estilo de um elemento que está desabilitada no HTML. Para um elemento estar desabilitado é necessário utilizar o atributo *disabled* em HTML.
 
```html
<button disabled>Meu botão</button>
```
```css
button:disabled {
  background-color: gray;
}
```

### 2. Pseudo-classes estruturais
Vou ser bem sincera, essas pseudo-classes tem meu coração e foram a principal motivação para escrever esse texto ❤️. Acredito que essas pseudo-classes tem um grande potencial de melhorar e simplificar a vida de nós, pessoas desenvolvedoras de CSS. Várias delas não são tão conhecidas ou nós esquecemos de utilizá-las em nossa rotina de trabalho.

As pseudo-classes estruturais modificam o estilo de acordo com a quantidade de elementos HTML similares na página ou de acordo com o conteúdo do HTML. Alguns exemplos são:

```css
:first-child
:first-of-type()
:last-child
:last-of-type()
:only-child
:only-of-type()
:nth-child()
:nth-of-type()
:nth-last-child()
:nth-last-of-type()
```

Nas próximas seções, vamos discutir as diferenças entre as pseudo-classes que terminam com *-of-type* e as *-child*. Em resumo, pensando que queremos selecionar elementos filhos de uma `<div>`, as pseudo-classes terminadas em *-of-type* consideram **elementos HTML iguais, do mesmo "tipo"**, como um conjunto de `<p>`. Já as terminadas em *-child* **não levam o "tipo" do elemento HTML em consideração**.

#### Pseudo-classes :first-child e :first-of-type
As duas pseudo-classes *:first-* modificam o primeiro elemento com alguma condição, e apesar se serem parecidas, tem uma grande diferença. Vamos ao exemplo:

Temos o elemento pai `<main>` com elementos filhos `<h1>` e `<p>`:

```html
<main>
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca.</p>
  <p>Frederico também tem 7 anos e é meio siamês.</p>
  <p>Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```

E o seguinte CSS:

```css
main :first-child {
  color: orange;
}
```

E agora? Quem vai ficar com texto laranja?!? 🍊

Nesse caso, o `<h1>` "Meus Gatos" vai ficar com texto laranja. Isso acontece pois o *:first-child* vai selecionar **o primeiro filho** do elemento pai (`<main>`).
Pensando na lógica de programação normal, seria como se tivessemos um *while* percorrendo os filhos do `<main>` e o elemento na posição zero seria selecionado, independente de qual elemento HTML ele é. 

Mas e se a gente usasse o seguinte CSS? O que acontece?

```css
main p:first-child {
  color: orange;
}
```

Agora **nenhum elemento vai ter texto laranja**! Como no CSS do exemplo nós usamos *p:first-child* e o primeiro filho do `<main>` não é um `<p>`, nada acontece. Voltando pra comparação com a lógica de programação, é como se antes de selecionar o valor com index 0 no `<main>` (que é o `<h1>`) teriámos que ver se o seu tipo é igual o do seletor CSS (que é `<p>`). Somente se os dois fossem iguais alguma ação aconteceria.

Por outro lado, se estivessemos usando *:first-of-type*, seria o primeiro `<p>`, com o texto "Mila tem 7 anos..." que ficaria com o texto azul. O *:first-of-type* vai selecionar o **primeiro elemento que é um `<p>`**, logo, o texto sobre a Mila. 

```css
main p:first-of-type {
  color: blue;
}
```

#### Pseudo-classes :last-child e :last-of-type
Essas pseudo-classes são quase idênticas as *:first-child* e *:first-of-type*. A única diferença é que elas selecionam os últimos elementos do pai e não os primeiros.

#### Pseudo-classes :only-child e :only-of-type
Essas pseudo-classes também são parecidas com as anteriores, mas selecionam somente elementos que são filhos únicos. Acho que aqui podem ocorrer dúvidas, então vamos a outro exemplo para deixar claro a diferença entre *:only-child* e *:only-of-type*.

```html
<main>
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca.</p>
  <p>Frederico também tem 7 anos e é meio siamês.</p>
  <p>Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```

Observando o HTML podemos concluir que temos vários `<p>`, e logo, eles não são filhos únicos. Mas temos somente um `<h1>`, que é o filho único desse tipo de elemento HTML. Agora abstraindo um pouco, vamos imaginar que você tem filhos pets, três gatos e uma cachorra. Todos eles são irmãos entre si, mas a cachorra não tem irmãos do "tipo" dela, que é cachorro. O `<h1>` do exemplo anterior é como se fosse a cachorra 🐶.

```css
main :only-child {
  color: orange;
}
```

Ao usar o *:only-child* **nenhum** elemento vai ter texto laranja, já que todos eles tem algum irmão. Mas se usarmos *:only-of-type* no lugar, o `<h1>` terá texto azul, já que ele é o único irmão que é o elemento HTML `<h1>`.

```css
main :only-of-type {
  color: azul;
}
```

#### Pseudo-classes :nth-child e :nth-of-type
Essas pseudo-classes possuem um parâmetro que pode ter vários formatos:
* um número, como 2, 3, ou 5, que selecionará o elemento irmão na posição 2, 3, ou 5. 
* uma função (*não vou entrar em detalhe sobre as funções nesse artigo, mas quem sabe em breve faço outro falando só sobre elas* 💁🏻‍♀️)
* a palavra odd(ímpar) ou even(par)

Pensando na lógica de programação tradicional o parâmetro dessas pseudo-classes nada mais é do que uma condição, um "if". Por exemplo:

```html
<main>
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca.</p>
  <p>Frederico também tem 7 anos e é meio siamês.</p>
  <p>Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```
```css
main :nth-child(2n) {
  color: orange;
}
```

Parecido com o exemplo anterior de *:first-child* né?
No exemplo acima o parâmetro que eu usei foi o *2n*, que vai selecionar todos os elementos múltiplos de dois. Ou seja, vai começar pelo segundo elemento, depois pro quarto, sexto.. e assim por diante.
No exemplo eu usei a pseudo-classe *:nth-child*, que como no exemplo anterior da *:first-child*, vai selecionar o segundo filho do elemento `<main>` **independente** de qual elemento HTML ele for, que nesse caso é o `<p>` com o texto "Mila tem 7 anos..." e depois o `<p>` com o texto "Jade possivelmente..."

Se a gente trocar o CSS por:

```css
main :nth-of-type(2n) {
  color: blue;
}
```

Quem ficará com o texto azul será o segundo `<p>`, que tem o texto "Frederico também tem 7 anos...". O *:nth-of-type(2n)* irá começar a selecionar a partir do **segundo `<p>` que é filho do elemento `<main>`**. Caso tivessemos mais um `<p>` após o texto da Jade, ele também estaria laranja.

#### Pseudo-classes :nth-last-child() e :nth-last-of-type()
O nome dessas pseudo-classes soa bem confuso pra mim, mas não é pra ter medo! Elas são uma mistura de algumas das pseudo-classes que acabamos de aprender. A *:nth-last-child* e a *:nth-last-of-type* são **muito** parecidas com as *:nth-child()* e *:nth-of-type()*. A única diferença que que elas funcionam de baixo para cima em vez de cima para baixo. Por exemplo:

```html
<main>
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca.</p>
  <p>Frederico também tem 7 anos e é meio siamês.</p>
  <p>Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```
```css
main :nth-last-child(2n) {
  color: orange;
}
```

Dessa vez vamos pensar de baixo pra cima, começando com o último parágrafo "Jade possivelmente...", todos os filhos de `<main>` múltiplos de 2, independente do tipo de elemento HTML, vão ficar com o texto laranja devido ao *:nth-last-child*. Isso significa que o parágrafo do Frederico e o `<h1>` teriam texto laranja. Ah, importante, nesse caso e em todos os outros que usamos alguma *:nth-* pseudo-classe o primeiro filho é sempre 1 e não 0.

```css
main :nth-of-type(2n) {
  color: blue;
}
```

Já o *:nth-of-type* vai deixar todos os filhos de `<main>` múltiplos de 2 **e** do mesmo tipo de elemento HTML com texto azul. O que significa que o parágrafo do Frederico seria o único afetado.

### 3. Pseudo-classes relacionais
Essas pseudo-classes não são tão populares quanto as anteriores, mas são muito úteis no dia-a-dia. Elas são:

```css
:not()
:empty()
```

#### :not
A pseudo-class *:not* é exatamente aquilo imaginamos pelo seu nome, ela seleciona todos os elementos, menos aqueles que forem passados como parâmetro. Pensando em lógica de programação, é basicamente um else no seletor.
Por exemplo, queremos que a fonte do texto dentro de `<main>` seja 1rem, mas não queremos que o `<h1>` seja modificado. Para isso, podemos usar a pseudo-classe *:not*:

```html
<main>
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca.</p>
  <p>Frederico também tem 7 anos e é meio siamês.</p>
  <p>Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```
```css
main :not(h1) {
  font-size: 1rem;
}
```

Agora vamos imaginar que queremos que todos os `<p>` dentro de `<main>` sejam azuis, menos o último.

```css
main p:not(:last-of-type) {
  color: blue;
}
``` 

Essa pseudo-classe pode ser usada em combinação com outras. Como no exemplo acima, onde misturamos as pseudo-classes *:not* e *:last-of-type*. Não precisa ter medo de misturar!

Eu acho isso bem legal e meio louco, mas é muito útil 😍
Algo importante de se notar é que **não podemos usar *:not* dentro dela mesma**, algo como

```css
main p:not(:not(:last-of-type)) {
  color: red;
}
```

**Não** é válido! E também não conseguimos usar com os pseudo-seletores *:after* e *:before* (que merecem outro texto só deles!).

#### :empty
A pseudo-classe *:empty* lembra um pouco o funcionamento da *:not*. Sou uma grande fã da *:empty*! No meu caso específico eu estava trabalhando numa empresa que usava um tema do Hugo, onde eu escrevo o conteúdo em markdown e o Hugo transforma em HTML. Nosso tema ainda estava em desenvolvimento e as vezes elementos vazios de paragráfo eram gerados. Esses `<p>` vazios ainda estavam estilizados com margem, o que gerava espaço em branco que atrapalhava o design dos sites. Ao adicionar

```css
p:empty {
  margin: 0;
}
```

Consegui resolver o problema visual desses `<p>` vazios enquanto o tema ainda era ajustado.

### 4. Pseudo-classes textuais
Essas são as pseudo-classes que eu menos uso, tanto no trabalho quando em projetos pessoais. Mas isso não quer dizer que elas não são importantes 😎.

#### Pseudo-classe :first-letter 
Essa pseudo-classe é exatamente o que ela parece, ela muda o estilo da primeira letra de um elemento HTML.

```html
<main>
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca.</p>
  <p>Frederico também tem 7 anos e é meio siamês.</p>
  <p>Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```
```css
p:first-letter {
  color: red;
}
```

#### Pseudo-classe :first-line
Parecida com a anterior, a *:first-line:* modifica somente a primeira linha do elemento HTML.

```html
<main>
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca. Frederico também tem 7 anos e é meio siamês. Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```
```css
p:first-line {
  color: red;
}
```

No caso do exemplo anterior, somente a primeira linha será vermelha. O mais legal dessa pseudo-classe é que a primeira linha nunca é um valor fixado. Se você modificar o tamanho da tela, seja no navegador ou entrando pelo seu celular, você vai ver que o conteúdo da primeira linha vai mudar, mas o estilo *sempre* será aplicado somente na primeira linha. Tá vendo só? Por isso eu amo CSS 💖.

#### Pseudo-classe :lang 
O que dizer dessa pseudo-classe que eu mal conheço mas admiro muito? Eu nunca usei essa pseudo-classe, mas desde o primeiro momento em que conheci ela achei super incrível. A *:lang* seleciona elementos HTML que possuem esse atributo, que é responsável por indicar em qual idioma está o conteúdo HTML. Vamos a um exemplo:

```html
<main>
  <h1>Meus Gatos</h1>
  <p lang="pt">Mila tem 7 anos e é laranja e branca. Frederico também tem 7 anos e é meio siamês. Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
 <p lang="en">Mila is 7 years old and is orange and white. Frederico is also 7 years old and is kind of siamese. Jade possibly has 5 years old, is black and has green eyes.</p>
</main>
```
```css
:lang(pt) {
  color: red;
}

:lang(en) {
  color: blue;
}
```

No exemplo acima, o parágrafo em português será vermelho e o em inglês será azul. Achei isso bem legal 🎉.

Além disso, essa pseudo-classe é aplicada a **todos os elementos filhos de um elemento HTML com atributo lang**. No exemplo abaixo, o `<p>` teria texto vermelho mesmo sem um atributo *lang* explícito. Como `<main>` tem o atributo *lang*, os filhos vão receber o estilo.

```html
<main lang="pt">
  <h1>Meus Gatos</h1>
  <p>Mila tem 7 anos e é laranja e branca. Frederico também tem 7 anos e é meio siamês. Jade possivelmente tem 5 anos, é preta com olhos verdes.</p>
</main>
```
```css
:lang(pt) {
  color: red;
}

```

## E agora?
Legal, agora a gente já falou de todas as principais pseudo-classes e alguns exemplos de uso. Talvez você esteja pensando "eu acho que entendi, mas gostaria de treinar um pouco o uso delas". Talvez não, mas separei aqui algumas dicas de como treinar o uso dessas classes de uma forma divertida:

### Fazer um desenho em CSS. 
Chegamos a questão que eu adoro perguntar pras pessoas "Você já fez um desenho com CSS??". Se já, fico feliz que você sabe do que eu estou falando 🙌🏼, que tal criar outro e tentar adicionar várias pseudo-classes?
Senão, [segue aqui um tutorial que eu elaborei e pode ser útil](https://dev.to/giuliacardieri/desenhos-em-css-na-vida-real-util-ou-so-diversao-4pm).

Lembrando que desenhos em CSS são algo útil para praticar habilidades específicas de CSS, como o uso de pseudo-classes ou de CSS grid. Só fazer desenhos em CSS não garante que você conseguirá fazer uma aplicação real usando boas práticas, afinal um desenho tem preocupações diferentes de uma aplicação. Por isso eu também recomendo que você pratique fazendo um projeto pessoal (ou algum projeto teste de alguma empresa).

### Projeto pessoal
Um projeto pessoal não precisa ser algo super complexo ou com um objetivo sério como muita gente acredita. Eu sou uma grande fã de projetos que parecem bobos mas adicionam conhecimento. Já pensou em criar um site pro seu Gato? Ou criar um desenho em CSS e imaginar um site a partir dele? 

*Fazendo uma propaganda*: logo que eu estava começando eu criei um site chamado [Dinokiki](https://dinokiki.com) (cuidado que faz barulho) ele é só um site de um dinossauro que grita "kiki" em diferentes vozes quando é clicado. É útil pra uma empresa? Não. Foi útil pra mim? Muito.
Um projeto pessoal é uma ótima forma de aplicar boas práticas e estudar novas tecnologias, além de complementar seu portfolio!

Espero que esse artigo tenha ajudado você a entender um pouco mais sobre pseudo-classes em CSS. É um dos meus primeiros artigos. Estou aberta a feedbacks construtivos, se você tem algum por favor me manda um oi aqui, no twitter ou por email.

Até a próxima!