Entendendo unidades CSS e quando utilizá-las
Tags: css, iniciante
Published: 2021-08-17 22:08:55

_Nesse artigo vou explicar sobre as unidades CSS px, %, em, rem, ch, e ex e exemplos de casos de uso delas. Esse artigo será escrito em duas partes, detalhes do que vou abordar na segunda no final do texto._

-----------------------

## Introdução
**TLDR, porque eu escrevi esse artigo e o que eu decidi omitir dele.**

No Twitter e nos grupos do Telegram que eu participo vejo com muita frequência dúvidas sobre como e onde usar certas unidades CSS ou pessoas utilizando e manipulando unidades de forma incorreta. Para deixar claro, o que considero como uso incorreto de unidades CSS é utilizá-las de maneira que prejudique a acessibilidade e realizar operações com elas que o browser não consegue computar ou que são baseadas em números mágicos ou lógicas obscuras. Penso que o motivo dessas dúvidas é que geralmente as explicações sobre essas unidades vem desacopladas da explicação de como elas são aplicadas no dia-a-dia e, os casos de uso que a gente realmente entende como usar elas no seu potencial máximo vão ocorrendo com tempo e experiência. Abaixo vou explicar um pouco sobre cada unidade de medida e colocar como exemplo (quando possível) como e porque utilizei cada uma delas.

Por motivos de brevidade e utilidade omiti unidades com escopo muito específico (como Q, Hz) e unidades novas em fase experimental (Unidades do módulo CSS nível 4, lh, rlh, cap, etc).

--------------------------------
<br/>

## Unidades absolutas e relativas
Nas sessões abaixo vocês vão cansar de ler sobre unidades absolutas e relativas. Unidades relativas são unidades cujo valor depende de um fator externo, como por exemplo a % que depende do valor do elemento pai, do `vh` que varia de acordo com a altura do viewport, e assim por diante. Unidades absolutas são unidades ancoradas em medidas físicas reais, é o caso do `px` (píxeis) e do `cm` (centímetros).

--------------------------------
<br/>

## px
Um píxel é o conjunto de três pontos minúsculos - um vermelho, uma verde e um azul (RGB) - que juntos compõe tudo aquilo que você enxerga na tela que você está lendo esse post. Cada cor desse píxel exibe de 0 a 255 gamas de cores, somando um pouco mais que 16 milhões de possibilidades de cor. Por causa da ~bobagem~ moda do pixel-perfect, muitas pessoas se sentiam inclinadas a declarar todas as medidas de todos elementos com os mesmos pixeis utilizados no layout.
Com o surgimento de diversos tamanhos de dispositivos, cada um com diferentes formatos e taxas de densidade de pixel por polegada, ficou muito difícil manter esse valor absoluto pra elementos que precisam se adaptar de acordo a tela.

**Quando usar?**
Generalizando - use pra elementos e medidas que não irão mudar, independente do contexto ou tamanho de _viewport_ (tela do dispositivo). Um bom exemplo de uso são bordas e alguns box-shadows e pra definir valores mínimos e máximos de elementos.

Geralmente defino um tamanho mínimo e máximo para botões pra manter uma superfície clicável grande em dispositivos pequenos mesmo que o texto seja pequeno e para restringir o tamanho do botão dentro de elementos de bloco, pois se deixar ele fica gigante, da largura do elemento pai.

![Botão ocupando toda largura do container sem ter max-width](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6566mvt2n63eprjn1nq.png)
![Max width restringindo o tamanho total do botão](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3f6a3b7oqzundxy9b37f.png)

> A preocupação de se um elemento tem a largura ideal para ser clicado pelos dedos é um tema real de UX chamado _finger-friendly design_, [esse artigo da Smashing Magazine](https://www.smashingmagazine.com/2012/02/finger-friendly-design-ideal-mobile-touchscreen-target-sizes/) explica muito bem o conceito.


**Quando não usar?**
Bom, seria muito fácil assumir que tudo que não se encaixa nos requisitos de "Quando não usar?" entraria aqui, mas quero ressaltar que **jamais** se deve definir um tamanho de fonte em píxel.
Vamos supor que você coloque a `font-size` do elemento `<p>` em 16px. Em um dispositivo retina (as telas da apple por exemplo), existem 4 vezes mais pixeis dentro da mesma polegada do que em telas comuns, mas apesar da diferença de contexto, seu valor continuará estático. Caso pessoas com deficiências visuais apliquem zoom na sua página, os textos declarados com `px` não aumentarão com o zoom, causando um problema f*dido de acessibilidade.

> **O que a WCAG (Diretrizes de Acessibilidade para o Conteúdo da Web) diz sobre zoom e font-size?**
- [WCAG 1.4.4](https://www.w3.org/TR/WCAG21/#resize-text): Os usuários devem ser capazes de redimensionar textos sem tecnologia assistiva em até 200 por cento, sem perda de conteúdo ou funcionalidade. (Nível AA).
- [WCAG 1.4.10](https://www.w3.org/TR/WCAG21/#visual-presentation): Os usuários devem ser capazes de redimensionar o texto sem serem forçados a usar a barra de rolagem horizontal e verticalmente para ler o conteúdo. (Nível AA).

> Isso é de suma importância pois usuários com baixa acurácia visual simplesmente não vão conseguir consumir seu conteúdo, é ruim pra você e especialmente ruim pra essas pessoas.

> Você pode ler mais sobre como solucionar esse problema com zoom [nesse post incrível do Adrian Roselli](https://adrianroselli.com/2019/12/responsive-type-and-zoom.html).

--------------------------------
<br/>

## %
Porcentagens são **quase** sempre relativas às medidas do elemento pai delas. Se você declarar uma `width` de 20% em um elemento, a largura desse será 20% da largura do pai, mesma coisa com outras propriedades como `height` e `margin / padding`. Agora vem a parte do quase:

- Um `line-height` de 150% é o 1.5x maior que o valor de `line-height` base, mesma coisa com o `font-size`. Com base eu me refiro valores padrão de cada propriedade CSS, essas propriedades possuem um valor inicial próprio, definido pelo browser (user agent) ou pré-definidos pelo usuário (root).

- Um `transform: translateX(-100%);` significa que o elemento irá se movimentar para esquerda uma quantia relativa *ao seu próprio tamanho*, então se o elemento tiver 15px, ele se movimentará 15px para a esquerda.

**Quando usar?**
O melhor caso de uso que eu posso imaginar é a largura e altura de elementos aninhados, ou seja, que o pai não seja o `<body>`. Isso porque quase todos elementos que estão dentro de um pai não podem ter largura ou altura *maiores* que a do pai, facilitando muito na criação de elementos responsivos. Se você tem a certeza que os elementos diretamente no body não vão extrapolar o tamanho do viewport (a não ser que seu layout seja horizontal e não vertical), se todos elementos internos desse são uma porcentagem do tamanho do pai, esses dificilmente vão estourar o tamanho do container.

Um exemplo que gosto muito é usar porcentagem com CSS grid. O ideal é sempre usar a unidade `fr` (vou falar dela mais adiante), porém nesse caso em específico ele é muito poderoso.

Caso: Vamos supor que temos um layout que todo conteúdo fica no meio do site, como acontece em redes sociais como o Tumblr, porém no celular o espaço das laterais tem que diminuir expressivamente pro conteúdo caber. Esse layout é conhecido como _holy grail layout_, por ser um padrão amplamente adotado na web ainda hoje.

A maioria das pessoas logo pensa em colocar `padding` e `media queries` ou `margin: 0 auto`, mas tem essa solução com grid que é bem elegante:

```css
.container {
  display: grid;
  grid-template-columns: 1fr min(100%, 600px) 1fr;
}

.conteudo {
  grid-column: 2;
}
```

A função `min()` vai selecionar entre 600% e 100% qual o menor valor que ela irá retornar, em telas grandes a coluna do meio terá no máximo 600px e em telas menores que isso ela terá 100% da largura. Como a unidade `fr` calcula o espaço disponível, como 100% ocupa todo o espaço horizontal, as laterais serão iguais a zero.

![Exemplo do uso da função min deixando a largura da coluna central do grid em 100% em dispositivos menores](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tdv459dvobyjq3vy2auj.png)

Você pode ler mais sobre essa estratégia [nesse post maravilhoso do Josh Comeau](https://www.joshwcomeau.com/css/full-bleed/).


**Quando não usar?**
Evite usar com parâmetros relacionados a fonte e tome sempre o cuidado de declarar um valor máximo e mínimo para componentes que são elementos e não containers, um botão com uma `width` de 20% pode ficar ridiculamente maior em telas grandes.

Outra coisa que não faz mais sentido fazer é declarar `font-size: 100%;` no elemento `body`, `html` ou `:root`. Isso era feito pois como a porcentagem nesse caso diz respeito às definições de usuário, isso garantiria a flexibilidade da fonte. Porém o `rem` já faz isso direto da caixa.



-----------------------
<br />

## rem
O `rem` é o _root font-size_, ou seja, 1rem é igual ao tamanho total do `font-size` do elemento raiz e, na ausência desse, o tamanho inicial da propriedade `font-size`. Ambos valores serão 16px a não ser que o usuário personalize o tamanho da fonte do sistema ou utilize zoom.

O `rem` corresponde a uma fração desse valor, então 1.5rem equivalem a 24px, visto que:

> 16px * 1.5 = 24px

**Quando usar?**
Para o tamanho de fontes, raramente há um caso de uso que algo que não seja tamanho de fonte precise se pautar em `rem`, visto que por mais que essa medida seja relativa, seu valor muda apenas em casos muito específicos.

**Quando não usar?**
Não consigo pensar em nenhuma contra indicação de uso, mas da maneira de utilizar. Evitem de toda a forma converter rem em px, caso seu design system utilize uma escala modular onde todo `font-size` é maior que o próximo de acordo com uma proporção, crie uma variável base e use a função `calc()`, você criar listas com números quebrados gigante é difícil de manter, entender e justificar.
Para mais informações sobre esse caso, [esse é um ótimo artigo sobre modular scale com CSS](https://every-layout.dev/rudiments/modular-scale/).

Outra contraindicação é o "hack" de definir o `font-size` no elemento `:root` para "facilitar" a conversão de `px` pra `rem`:
```css
:root {
  font-size: 10px;
}

.container {
  font-size: 1.5rem; // equivalente a 15px
}
```

Isso é extremamente prejudicial pra acessibilidade e cai no problema do zoom que eu citei lá na sessão sobre `px`.

--------------------------------
<br/>

## em
O `em` funciona quase da mesma forma que o `rem`, porém ao invés de ser igual ao `font-size` definido no `:root` ela é igual ao `font-size` do elemento em que ela for utilizado.

Se tivermos um `p` dentro de uma `div`

```html
<div class="text-container">
  <p class="text"> Lorem ipsum dorsit amet</p>
<div>
```

O valor do `font-size` na `div` for 22px e o `line-height` for 1em
```css
.text-container {
  font-size: 22px;
}

.text {
  line-height: 1em;
}
```

O 1em vai ser 100% do `font-size` do container do elemento pois, se não declarado, o elemento `p` vai herdar o `font-size` de `.text-container`. Caso tanto o `font-size` quanto o `line-height` estiverem na classe `.text`, você vai obter o mesmo resultado.

**Quando usar?**
Como a gente sempre precisa ficar atento às fontes declaradas nos elementos pai pra ter controle sobre como o `em` escala, eu geralmente uso essa unidade pra ícones e elementos que precisam escalar junto com o texto, como botões e inputs.

No exemplo abaixo eu criei um botão que aumenta e diminui o `padding` de acordo com o `font-size`, o `padding` está declarado em `em`.
{% codepen https://codepen.io/camilo-micheletto/pen/PomgzVB %}

**Quando não usar?**
Não usar em elementos que estão em contextos muito aninhados de `font-size` pois ficará difícil mapear qual `font-size` o `em` determinado elemento está se baseando. Todas as afirmações sobre `rem` se aplicam também.


--------------------------------
<br/>

## ch e ex
Coloquei as duas unidades juntas pois elas funcionam de maneira parecida - ambas são baseadas em medidas dos caracteres da primeira `font-family` disponível.

O `ch` é referente a largura do '0' (zero) da fonte, já o `ex` é referente a altura do caractere 'x' minúsculo.

**Quando usar?**
Como 1ch diz respeito a mais ou menos um caractere (o valor é bem aproximado e varia dependendo da fonte), é bem interessante pra `width` de campos de texto. [Estudos indicam](https://baymard.com/blog/line-length-readability) que uma linha de texto que tem entre 50 e 60 caracteres de largura apresenta uma legibilidade melhor do que linhas mais curtas ou mais largas.

Essa regra combina muito com a função `clamp()`
```css
p {
  width: clamp(50ch, 100%, 60ch);
}
```

Eu particularmente gosto de usar pra propriedade `gap` quando uso textos dentro de um elemento flex ou grid, pois como ela escala menos que o `rem` (A cada 1 rem são 16px), você consegue fazer espaçamentos de texto menores sem escrever números muito quebrados.

**Quando não usar?**
Não consegui pensar em nenhuma contraindicação, tem muitos casos de uso que outras unidades funcionam melhor, mas nunca tive problema em utilizar ela em outros contextos não ligados a texto, se você já teve, comente, vai ser maneiro.


--------------------------------
<br/> 

Como isso aqui já está virando um livro, resolvi quebrar em duas publicações, na próxima vou falar de:

- Números inteiros
- Unidades de viewport
- Unidades de ângulo
- Unidades de resolução
- Unidades para impressos

---------------------------
<br />

## Fontes e leituras adicionais

- [O site da W3 (Não confundir com W3Schools)](https://www.w3.org/TR/css-values-3/#font-relative-lengths)
Sim, eu li esse calhamaço, mas não é tão ruim não tá? Recomendo demais.

- [Episódio do The CSS Podcast sobre unidades CSS](http://thecsspodcast.googledevelopers.libsynpro.com/tcp-css-podcast-episode-008)
Nunca achei que eu ia aprender tanto com um podcast sobre CSS, o Adam e a Una são dois nerds, provavelmente vou revisar esse texto quando escutar novamente.

*Observação: *
Se nesse texto eu:
- Falei merda
- Caguei regra
- Perdi a oportunidade de usar de linguagem neutra
- Cometi erros de português
- Poderia ter explicado algo melhor ou falei algo que não fez sentido

Ou se só ficou alguma dúvida mesmo.
Pode comentar aqui ou me chamar no [Twitter](https://mobile.twitter.com/lixeletto) que eu vou amar ok?

Obrigadíssimo 😙