Utilizando pseudo-elementos do CSS e interfaces no TypeScript
Tags: css, typescript, frontend, react
Published: 2024-02-11 06:25:58

_Hoje vamos misturar CSS com TypeScript no mesmo post para entender como construir interfaces usando pseudo-elementos e interfaces! (what?)_

---

Revisei pseudo-elementos no CSS durante o desenvolvimento do temporizador disponível [aqui nesse link](https://github.com/EdeiltonSO/IgniteTimer), mas não é de hoje que esses seletores me chamam a atenção. Acho interessantíssima a ideia de poder criar e estilizar um elemento que é renderizado em tela, sem a necessidade de adicionar elementos extras ao HTML ou ter que usar JavaScript.

Na especificação desse projeto, há uma tabela com uma coluna de status que tem uma bolinha (que pode assumir três cores diferentes) e o título do status, conforme mostrado abaixo, na coluna mais à direita:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nehvccyxu39yz1a7xxwa.jpg)

Para criar essa bolinha, talvez o seu primeiro pensamento seja usar um componente estilizado dentro do `td` de status da tabela (mesmo que não seja um componente em outro arquivo, já que é algo simples). Daí teríamos dois elementos: a bolinha e o texto, com um gap entre eles. 

Mas dá pra fazer melhor: pode ser só o texto com um `::before`. E se você ainda não sabe o que é isso, vem comigo que eu te explico — e se já sabe, vem relembrar!

## Utilizando pseudo-elementos

Resumidamente, `::before` e `::after` são pseudo-elementos do CSS criados, respectivamente, **antes** e **depois** da coisa que você tá estilizando. No exemplo abaixo (que utiliza Styled Components), estou criando a bolinha **antes** dessa div estilizada, já que ela fica à esquerda do texto:

```tsx
export const Status = styled.div`
  display: flex;
  align-items: center;
  gap: 0.5rem;

  ::before {
    content: '';
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: ${(props) => props.theme['yellow-500']};
  }
`
```

O background desse pseudo-elemento utiliza uma arrow function com `props.theme` somente por conta do Theme Provider da aplicação, mas você não precisa se preocupar com isso agora. Se preferir, por enquanto, imagine apenas uma cor fixa em hexadecimal e teremos o mesmo resultado.

Lembre-se que mesmo que você não queira exibir conteúdo algum dentro do pseudo-elemento, é sempre necessário usar o `content: '';`, caso contrário, ele não aparece na tela. Já na estrutura da página, cada linha da tabela será assim:

```tsx
<tr>
  <td>Tarefa X</td>
  <td>30 minutos</td>
  <td>Há 3 meses</td>
  <td>
    <Status>Concluído</Status>
  </td>
</tr>
```

Agora que o texto e a bolinha já aparecem na página, vamos pro próximo passo: mudar a cor de acordo com o status do componente.

## Adicionando uma interface em TypeScript

Como alterar a cor no arquivo de estilos de acordo com o status da tarefa? Fácil: passando propriedades, mais ou menos como fazemos em componentes JSX.

Para isso, você pode criar uma interface no arquivo de estilos e passar pro componente usando os *generics* do TypeScript. Confira abaixo:

```tsx
interface StatusProps {
  statusColor: 'green' | 'yellow' | 'red'
}

export const Status = styled.div<StatusProps>``
```

E não podemos esquecer de passar a propriedade ao usar o componente lá na estrutura da página:

```tsx
<Status statusColor="green">Concluído</Status>
```

“E aí, tá pronto?” 

Não.

## Definindo a constante de cores

A cor ainda não muda pois tá fixa dentro do estilo do Status (no primeiro snippet de código desse post) e as cores que estamos utilizando ficam em um objeto que tá em outro arquivo, pois a aplicação utiliza o ThemeProvider do Styled Components. Você pode conferir conferir as constantes de cor no arquivo [src/styles/themes/main.ts](https://github.com/EdeiltonSO/IgniteTimer/blob/main/src/styles/themes/main.ts). 

Para ter acesso a esses valores, uma solução é criar uma constante STATUS_COLORS, como é mostrado a seguir:

```tsx
const STATUS_COLORS = {
  green: 'green-500',
  yellow: 'yellow-500',
  red: 'red-500',
} as const
```

Por fim, lembra do background com o `props.theme` que poderia ser uma cor fixa? Agora precisamos tornar ela dinâmica, de acordo com o Theme Provider e a constante de cores que acabamos de definir. Veja no exemplo abaixo:

```tsx
::before {
  background: ${(props) => props.theme[STATUS_COLORS[props.statusColor]]};
}
```

Ou seja: pegue a propriedade `statusColor` (disponível nas `props` do componente), utilize como índice no objeto `STATUS_COLORS` e, com o valor retornado, acesse a cor desejada no `theme` do provider (também disponível via `props`, no arquivo [App.tsx](https://github.com/EdeiltonSO/IgniteTimer/blob/main/src/App.tsx) da aplicação).

Com isso — e contando que seu Theme Provider esteja configurado — o componente já deve alterar a cor da bolinha conforme o status passado por meios das propriedades. Lindo, né?

Vamos revisar o funcionamento:

1. O componente Status recebe a cor pela propriedade `statusColor` lá no JSX;
2. O estilo dele pega esse valor via *generic* do TypeScript — que está recebendo a interface `StatusProps`, definida nos estilos;
3. Essa interface fixa que os valores aceitos são os mesmos da constante `STATUS_COLORS`;
4. Os valores das chaves na constante `STATUS_COLORS` coincidem com os nomes das cores fornecidas pelo Theme Provider no arquivo App.tsx;
5. O Theme Provider recebe o objeto `mainTheme`, contendo os hexadecimais das cores.

## Facilitando a manutenção

Perceba que, na etapa 3 da revisão acima, os valores possíveis para a interface devem coincidir com a constante. Mas e se uma nova cor de status for adicionada ao componente? Precisamos editar a constante e a interface? 

Até aí sim, mas podemos evitar isso adaptando a interface `StatusProps` para que `statusColor` sempre se ajuste aos valores das chaves da constante `STATUS_COLORS`. Confira:

```tsx
interface StatusProps {
  statusColor: keyof typeof STATUS_COLORS
}
```

Depois de dessa alteração, sabemos que sempre vamos ter uma equivalência entre as chaves de `STATUS_COLORS` e a lista de cores aceitas pela nossa interface. Agora não precisamos mexer em dois lugares ao modificar a lista de cores — basta alterar a constante.

Pronto! Agora, com tudo configurado, basta chamar o componente Status passando a propriedade que corresponda ao status da tarefa.

Concluímos que, além de uma solução bastante elegante, os pseudo-elementos no CSS são ferramentas excelentes para adicionar elementos em tela sem poluir o HTML ou recorrer ao JavaScript. 

Em relação ao uso de interfaces em TypeScript e uma constantes de cores, percebemos que essas soluções tornam a implementação mais flexível e fácil de se adaptar a mudanças posteriores do projeto — o que torna a manutenção do código muito mais simples.

Curtiu aprender um pouco mais sobre o mundo do desenvolvimento front-end? Então me segue que em breve tem mais!