Use variáveis CSS como uma pessoa sênior com essas 5 dicas 🔥
Tags: css, webdev, programming, beginners
Published: 2023-11-23 04:58:54

## Sumário

- [101](#101)
- [Design tokens](#tokens)
- [Composição de variáveis](#composition)
- [Blocos enxutos](#blocks)
- [Variáveis como props](#props)
- [Variáveis privadas](#private)
- [Materiais de estudo](#study)

<br />

---

<br />

## 101 <a id="101" href="#101">&#128279;</a>
Repetição é bem comum no CSS, ainda mais em fontes, cores e espaçamentos. Repetição nem sempre é desperdício, se a gente quer ser consistente com o nosso design, repetição favorece a percepção de padronização das pessoas usuárias. No caso abaixo, temos um _timing_ de transição padrão pras animações dos componentes:

```css
.btn {
  transition: background 200ms ease-in;
}

.link {
  transition: color 200ms ease-in;
}
```

&nbsp;

Pra garantir a acessibilidade, temos que possibilitar que usuários que se distraem com animações ou possuem algum tipo de sensibilidade à movimento possam desativá-los via configuração do _user-agent_, que no caso é o navegador:

```css
.btn {
  transition: background 200ms ease-in;
}

@media (prefers-reduced-motion: reduce) {
  .btn {
    transition: background linear;
  }
}

```

&nbsp;

Nesse caso, pra cada classe que tivesse esse timing teríamos que criar também dentro da `@media` pra que a pessoa usuária conseguisse mudar. E se além disso mudarmos o tema pra escuro? Teríamos que colocar todas as classes com cores dentro da `@media (prefers-color-scheme: dark)`?

Com variáveis CSS poderíamos fazer diferente!
Primeiro guardamos a nossa configuração de _timing_ e _easing_ num local que ela ficasse disponível "globalmente". Geralmente usamos `:root {}` que é um equivalente à `html {}`, mas que possuí ainda mais precedência.

```css
:root {
  --timing: 200ms ease-in;
}
```

&nbsp;

E ai podemos atualizar todas referências desse valor no código usando a função `var()`

```css
.btn {
  transition: background var(--timing);
}

.link {
  transition: color var(--timing);
}
```

&nbsp;

Só isso já nos daria a vantagem de, se caso for necessária a mudança de valor dessa propriedade, mudarmos em um lugar só, mas o pulo do gato não chegou ainda, se liga:

```css
:root {
  --timing: 200ms ease-in;
  @media (prefers-reduced-motion: reduce) {
    --timing: linear;
  }
}
```

Se a _media condition_ `(prefers-reduced-motion: reduce)` retornar `true`, como a variável `--timing: linear` foi redeclarada *depois*, ela vai *sobrescrever* a declaração `--timing: 200ms ease-in;`. Percebe que não só economizamos linhas de CSS com isso, mas colocamos dentro do mesmo bloco toda a lógica desse comportamento?


<br />

---

<br />

## Design tokens <a id="tokens" href="#tokens">&#128279;</a>

Design tokens são os átomos que compõe os componentes de uma aplicação. Um botão por exemplo é feito com um tamanho de fonte específica, cores próprias e que reagem às mudanças de estado, bordas que seguem esses padrões de cores, um `padding` padrão nas bordas e um `gap` representando um _gutter_ em botões que também tem ícones. Todas essas configurações podem ser consistentes em outros componentes e podem ser pré-estabelecidas e guardadas em variáveis - tokens.

&nbsp;

Um exemplo do uso de tokens é a criação de um padrão de cores a ser usada numa aplicação. Usando variáveis CSS além de defini-las com um nome apropriado, deixando elas fáceis de reproduzir, mudar e manter, podemos altera elas facilmente em diferentes condições, como a mudança pra um tema escuro.

Abaixo um exemplo do framework de variáveis CSS [OpenProps usando variáveis CSS e _media features_ pra implementar um modo escuro](https://open-props.style).


![Documentação da lib Open Props na seção Theming 101](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mvbortjsay8sk94iu8ez.png)

&nbsp;

No exemplo acima foi colocado o seletor `:root` dentro de uma media query com `prefers-color-scheme: dark`. Com isso, se a _media condition_ for `true`, o `:root` contendo as variáveis com o tema escuro irá sobrescrever o anterior contendo o tema claro padrão.

Podemos criar múltiplos temas em componentes usando o mesmo sistema:

```css

.button {
  color: var(--btn-text, #1A1A1A);
  background: var(--btn-bg, #FAFAFA);
}

.button[danger] {
  --btn-text: #FAFAFA;
  --btn-bg: #FF0000;
}

```

&nbsp;

No HTML:
```html
<div>
  <button class="button">Tema claro</button>
  <button class="button" danger>Tema vermelho</button>
</div>
```

&nbsp;

Mas e se eu quisesse alterar um tema? Pra um elemento como um _alert_ ou _callout_ seria interessante adicionar um pouco de opacidade à cor de fundo, mas só isso. Como você faria?

> **TailwindCSS** resolveu isso com composição de variáveis.



<br />

---

<br />

## Composição de variáveis <a id="composition" href="#composition">&#128279;</a>

No artigo [_Composing the Uncomposable with CSS Variables_ de Adam Wathan, o criador do TailwindCSS](https://adamwathan.me/composing-the-uncomposable-with-css-variables/) ele aborda justamente essa questão.

TailwindCSS é um plugin de PostCSS que gera classes utilitárias _on-demand_ à partir de um Design System. Possuindo um DS, ele também possui os tokens que eu estava falando no tópico anterior:


![Print do site do Tailwind com os tokens de imagem](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u4zw2wuewt5eehrurmau.png)

&nbsp;

Vamos supor que você aplica a classe `.text-blue-300` que deixa a cor do texto num tom de azul e, ao mesmo tempo, aplica a classe `.text-opacity-50` que deixa a cor do texto com opacidade de 50%.

A solução de Watham foi criar uma função de cor e no argumento de opacidade incluir uma variável CSS ao invés de um valor. Dessa forma, se `.text-opacity-` tivesse no mesmo escopo que uma classe de cor, ela "injetaria" a variável que ela contém na classe.

Abaixo, o exemplo do artigo citado acima em que a classe de opacidade "injeta" o valor de `--text-opacity` quando usada em um elemento junto com a classe de cor:

```css
.text-blue-300 {
  --text-opacity: 1;
  color: rgba(144, 205, 244, var(--text-opacity));
}

.text-opacity-50 {
  --text-opacity: 0.5;
}
```

&nbsp;

Classes aplicadas ao mesmo elemento estão sob o mesmo escopo, ou seja, elas podem influenciar umas as outras através de variáveis CSS:

![Exemplo visual de como variáveis podem influenciar classes diferentes dentro do mesmo escopo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6e2pxk5qqrgt2km9cah0.png)

&nbsp;

Dito isso, será que é possível criar diversas variantes de um componente apenas injetando variáveis neles [com classes modificadoras](https://getbem.com/introduction/#:~:text=%2C%20header%20title-,Modifier,-A%20flag%20on)?

> É. Bootstrap 5 fez isso com maestria.

<br />

---

<br />

## Blocos enxutos <a id="blocks" href="#blocks">&#128279;</a>

O componente de botão do Bootstrap possui 9 variantes, sendo uma delas um link, como eles fazem?

![Print da documentação que mostra as 9 variantes de botões do bootstrap com código fonte](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lijygqm3uay63hr7r4fo.png)

&nbsp;

Esse é o código fonte do [`bootstrap/dist/css/bootstrap.css`](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.css#L2975C1-L2975C1). Algumas propriedades foram omitidas por brevidade:

```css
.btn {
  --bs-btn-padding-x: 0.75rem;
  --bs-btn-padding-y: 0.375rem;
  --bs-btn-font-family: ;
  --bs-btn-font-size: 1rem;
  --bs-btn-font-weight: 400;
  --bs-btn-line-height: 1.5;
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-bg: transparent;
  --bs-btn-border-width: var(--bs-border-width);
  --bs-btn-border-color: transparent;
  --bs-btn-border-radius: var(--bs-border-radius);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
  --bs-btn-disabled-opacity: 0.65;
  --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);

  display: inline-block;
  padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);
  font-family: var(--bs-btn-font-family);
  font-size: var(--bs-btn-font-size);
  font-weight: var(--bs-btn-font-weight);
  line-height: var(--bs-btn-line-height);
  color: var(--bs-btn-color);
  border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);
  border-radius: var(--bs-btn-border-radius);
  background-color: var(--bs-btn-bg)
}
```

&nbsp;

No código acima, percebemos que:
- São definidos valores padrão dentro do escopo do componente, inclusive valores que referenciam outras variáveis, dessa forma evitando a repetição de tokens com diferentes nomenclaturas, possibilitando uma referência cruzada.

- Embaixo são definidas as propriedades consumindo desses tokens.

Agora vamos ver a composição da variante `.btn-primary`:

```css
.btn-primary {
  --bs-btn-color: #fff;
  --bs-btn-bg: #0d6efd;
  --bs-btn-border-color: #0d6efd;
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: #0b5ed7;
  --bs-btn-hover-border-color: #0a58ca;
  --bs-btn-focus-shadow-rgb: 49, 132, 253;
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: #0a58ca;
  --bs-btn-active-border-color: #0a53be;
  --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-bg: #0d6efd;
  --bs-btn-disabled-border-color: #0d6efd;
}
```

&nbsp;

> Só tem variáveis CSS. 

Através da especificidade a variante altera os valores **sem adicionar ou redeclarar nenhuma propriedade**, de forma extremamente enxuta (DRY).

Agora, o que você faria se precisasse entregar um layout como o abaixo?

![10 cards enfileirados em duas linhas contendo 5 cards cada. Cada card tem uma cor distinta.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvusy36sa7ntnh67ml1a.png)

&nbsp;

Usando essa mesma estratégia, criamos uma classe `.card` em que `background-color` recebe uma variável e pra cada `nth-child(n)`a gente muda a cor da variável:

```css
.card {
  height: var(--size); width: var(--size);
  background-color: var(--color, gray);
  border-radius: 8px;
}

.card:nth-child(1) { --color: maroon; }
.card:nth-child(2) { --color: red; }
.card:nth-child(3) { --color: purple; }
.card:nth-child(4) { --color: fuchsia; }
.card:nth-child(5) { --color: green; }
.card:nth-child(6) { --color: lime; }
.card:nth-child(7) { --color: yellow; }
.card:nth-child(8) { --color: navy; }
.card:nth-child(9) { --color: blue; }
.card:nth-child(10) { --color: aqua; }
```

&nbsp;

> Mas se forem 20 cards, 30 cards? O CSS vai ficar enorme né? Se pelo menos tivesse como a gente passar as cores pelo elemento, como as props no React.
>
> 😈 e se eu te disser que tem como?

<br />

---

<br />

## Variáveis como props <a id="props" href="#props">&#128279;</a>

Diferente das outras estratégias, essa começa na marcação. Mantemos o código da variável `.card`, mas mudamos como inserimos o valor da cor:

```html

<li class="card" style="--color: tomato"></li>

```

&nbsp;

> CSS inline. Podem rir de mim, fãs de Tailwind.

&nbsp;

O conceito "parece" ruim, mas não é. Primeiro que diferente de inserir declarações de CSS (propriedade e valor), sobrescrevendo o CSS local, não sobrescrevemos ou adicionamos nada no CSS do elemento, apenas valores. Dessa forma mantemos a mesma integridade do CSS da folha de estilos, ainda com um ganho de especificidade da declaração inline.

Outro ponto é que em linguagens de templating como JSX ou afins, podemos literalmente alterar os estilos de forma lógica sem, muitas vezes, a necessidade de uma solução CSS-in-JS:

Qual a diferença disso...
```JSX
<Contact color="darkgreen" />
```

&nbsp;

Pra isso...
```html
<div class="contact" style="--color: darkgreen">
  <!-- ... -->
</div>
```

&nbsp;

Ou até mesmo isso...?
```JSX
<div class="contact" style={{ '--color': contact.color }}>
  <!-- ... -->
</div>
```

&nbsp;

{% details **Off-topic &rarr; É possível usar HTML da mesma forma que usamos props em Styled Components** %}

&nbsp;

O que te impede de fazer isso?
```html
<button variant="primary" dark>Follow for more!</button>
```

&nbsp;

E isso aqui?

```css
button[variant=primary] {
   background-color: var(--background, red);
   color: var(--background, black);

   &[dark] {
      --background: darkred;
      --color: white;
   }
}
```

- Não use se você precisar ter um código W3C compliant
- '&' é CSS válido, viu? 

{% enddetails %}

&nbsp;

Vamos supor que ao invés de um botão completamente limpo quiséssemos que a classe base `.btn` começasse com estilos padrão. Pra isso, podemos usar o segundo parâmetro da função `var()`, que serve como _fallback_ caso a variável inicial não esteja declarada.

O problema dessa abordagem é que em variantes e outros estados precisamos sempre preencher com um valor padrão caso `--background` ou `--color` nunca sejam declarados:

```css
.btn {
   background: var(--background, #FAFAFA);
   color: var(--color, #1A1A1A);
}

.btn:focus {
  outline: 1px solid var(--background, #FAFAFA);
}
```

&nbsp;

> Existe forma melhor de fazer isso? A membro da W3C e convidada do CSS Working Group [Lea Verou](https://lea.verou.me/) veio com uma abordagem muito boa sobre.


<br />

---

<br />

## Variáveis privadas <a id="private" href="#private">&#128279;</a>

Algumas alternativas pro problema de redeclaração de parâmetros de _fallback_ em variáveis CSS seriam:

**1 - Declaração de _fallback_ no escopo do componente**
```css
.btn {
   --background: #FAFAFA;
   --color: #1A1A1A;
   background: var(--background);
   color: var(--color);
}

.btn:focus {
   --background: #FAFAFA;
  outline: 1px solid var(--background);
}
```

Funcional, porém impede o componente de herdar `--color`de um elemento pai, somente através de especificidade, o que pode se tornar difícil de gerenciar pra seletores muito complexos.

&nbsp;
 
**2 - Criação de uma variável de _fallback_ usando o parâmetro _default_ do `var()`**

```css
.btn {
   --background-initial: #FAFAFA;
   --color-initial: #1A1A1A;
   background: var(--background, var(--background-initial));
   color: var(--color, var(--color-initial));
}

.btn:focus {
   --background-initial: #FAFAFA;
  outline: 1px solid var(--background, var(--background-initial));
}
```

Nessa implementação conseguimos herdar a propriedade `--background` e `--color` do elemento pai, mesmo sem uma especificidade superior, porém é bem verbosa né?

[A Lea Verou cunhou o conceito de _*pseudo private custom properties*_](https://lea.verou.me/blog/2021/10/custom-properties-with-defaults/), que funcionam similarmente à variáveis privadas em linguagens de programação (vide o sublinhado `--_` no nome.

Nesse conceito criamos uma variável de _fallback_ como anteriormente, mas diferente dessa ela terá o próprio valor padrão:

```css
.btn {
   --_background: var(--background, #FAFAFA);
   --_color: var(--color, #1A1A1A);
   background: var(--background);
   color: var(--color);
}

.btn:focus {
  outline: 1px solid var(--_background);
}
```

Dessa forma conseguimos:

1. Herdar as propriedades `--background` e `--color`.
2. Definir um fallback pra cada propriedade.
3. Fazê-lo de forma muito menos verbosa.

&nbsp;

---

&nbsp;

Todas essas estratégias me ajudaram a pensar e escrever CSS de forma muito mais enxuta e criativa, permitindo com que eu criasse layouts complexos com uma carga cognitiva menor e de forma mais extensível e customizável.

Um exemplo é esse bento layout responsivo com apenas 43 linhas de CSS:

{% codepen https://codepen.io/camilo-micheletto/pen/YzBYPvv %}

&nbsp;

> Não sabe o que é um bento layout? Tá na mão.

{% embed https://dev.to/lixeletto/bento-grid-layout-responsivo-em-40-linhas-de-css-b2j %}


<br />

---

<br />

## Materiais de estudo <a id="study" href="#study">&#128279;</a>

- Composing the Uncomposable With CSS Variables - Adam Watham - [artigo, em inglês](https://adamwathan.me/composing-the-uncomposable-with-css-variables/)

- Custom properties with defaults - Lea Verou - [artigo, em inglês](https://lea.verou.me/blog/2021/10/custom-properties-with-defaults/)

- Using CSS custom properties like this is a waste - Kevin Powell - [vídeo, em inglês](https://www.youtube.com/watch?v=_2LwjfYc1x8)

- Bootstrap custom properites - Bootstrap Documentation - [artigo, em inglês](https://blog.getbootstrap.com/2022/05/16/using-bootstrap-css-vars/)

- Getting started with CSS Custom Properties - Andy Bell - [artigo, em inglês](https://andy-bell.co.uk/getting-started-with-css-custom-properties/)

- Variáveis CSS, um guia prático - Tárcio Zemel - [artigo, em português](https://desenvolvimentoparaweb.com/css/variaveis-css-guia-pratico/)