rem vs em em uma demo aninhada
rem é relativo ao tamanho de fonte da raiz (o elemento <html>, 16px por padrão). em é relativo ao tamanho de fonte atual, que no caso de font-size é o do pai. Aninhe a mesma classe três vezes e a diferença aparece:
Os tamanhos impressos são o que o navegador calculou. Cada 1.25em multiplica o pai: 20px, depois 25px, depois 31.25px. Cada 1.25rem olha só para a raiz: 20px, 20px, 20px. Essa multiplicação é o motivo de tamanhos de fonte em em saírem do controle em listas e menus aninhados, e de a maioria dos sites definir tamanhos de fonte em rem.
A que cada unidade é relativa
| Unidade | Relativa a | 1 unidade por padrão |
|---|---|---|
px | nada (um pixel CSS) | 1px |
rem | tamanho de fonte do elemento raiz (html) | 16px |
em | tamanho de fonte do elemento; no próprio font-size, o do pai | 16px no nível mais alto |
% | depende da propriedade (veja abaixo) | |
vw / vh | 1% da largura / altura da viewport | |
dvh, svh, lvh | 1% da altura dinâmica, pequena ou grande da viewport | |
vmin / vmax | 1% do lado menor / maior da viewport | |
ch | a largura do "0" na fonte atual | cerca de meio em |
ex | a altura do x na fonte atual | cerca de meio em |
Um pixel CSS não é um pixel do dispositivo: em um celular com tela 3x, 1px tem 3 pixels do dispositivo de largura. Ele mantém mais ou menos o mesmo tamanho visual entre telas, e é por isso que o px continua bom para bordas e sombras.
em para espaçamentos que acompanham o texto
em não é só para tamanhos de fonte. Em padding, margin, width ou border-radius, ele significa "vezes o tamanho de fonte deste elemento". Isso faz uma única classe de botão funcionar em qualquer tamanho:
O padding de cada botão é metade do tamanho de fonte na vertical e um tamanho de fonte na horizontal, então os três parecem o mesmo botão em três tamanhos. Com padding em px, você precisaria de uma regra de padding para cada tamanho.
Repare que os botões não herdam o tamanho de fonte da página por padrão (os navegadores dão aos controles de formulário um tamanho próprio), então o botão do meio aqui tem 13.33px no Chrome, e não 16px. Acrescente font: inherit a botões e inputs quando quiser que combinem com a página.
Porcentagens: depende da propriedade
% não tem um significado único. Cada propriedade diz do que ela é porcentagem:
| Propriedade | % é relativo a |
|---|---|
width, max-width, left, right | a largura do pai (do bloco que o contém) |
height, top, bottom | a altura do pai, e só se o pai tiver uma altura definida |
padding, margin (os quatro lados) | a largura do pai |
font-size | o tamanho de fonte do pai (150% = 1.5em) |
line-height | o tamanho de fonte do próprio elemento |
transform: translate() | o tamanho do próprio elemento |
width: 50% dá 200px, metade do pai. padding-top: 5% dá 20px: 5% da largura do pai, não da altura. E height: 50% não faz nada, porque o pai não tem altura definida, então o filho só tem a altura do texto mais esse padding. A página de width e height trata dessa regra em detalhe.
vw, vh e a barra de endereço do celular
1vw é 1% da largura da viewport e 1vh 1% da altura. Eles são úteis em seções de tela cheia e em tamanhos fluidos (muitas vezes dentro de clamp()). No celular, o vh tem uma pegadinha: a barra de endereço do navegador entra e sai conforme você rola, e 100vh é a altura sem a barra, então um destaque com height: 100vh fica cortado embaixo quando a barra aparece. As unidades mais novas resolvem isso:
| Unidade | Altura usada |
|---|---|
100vh | a viewport grande (barras escondidas) na maioria dos navegadores móveis |
100svh | a viewport pequena (barras visíveis): sempre cabe |
100lvh | a viewport grande, de forma explícita |
100dvh | a viewport dinâmica: muda conforme as barras se movem |
.hero {
min-height: 100vh; /* fallback for older browsers */
min-height: 100svh; /* fits the screen even with the address bar visible */
}
O vw tem a própria pegadinha: 100vw inclui a largura de uma barra de rolagem vertical quando ela ocupa espaço (como no Windows), então width: 100vw pode ficar mais largo que a página e criar uma barra de rolagem horizontal. Use width: 100% para "a largura toda do pai".
ch para um comprimento de linha legível
ch é a largura do caractere "0", então max-width: 65ch limita um parágrafo a cerca de 65 caracteres por linha, um comprimento confortável de leitura, em qualquer tamanho de fonte:
Qual unidade usar
| Para | Use | Por quê |
|---|---|---|
| Tamanhos de fonte | rem | Consistente em todo lugar, respeita a configuração de fonte do leitor |
| Padding e gaps em componentes | rem ou em | em quando deve crescer com o texto do próprio componente |
| Bordas, linhas finas, sombras | px | Devem continuar finas em qualquer tamanho de fonte |
| Larguras de layout | %, fr, max-width em rem ou ch | Seguem o contêiner, limitam o comprimento da linha |
| Seções de tela cheia | svh / dvh (com vh de reserva) | Lidam com a barra de endereço do celular |
| Tipografia fluida | clamp() misturando rem e vw | Cresce com a tela dentro de limites |
Por que o rem respeita os leitores e o px não
Os navegadores têm uma configuração de tamanho de fonte padrão, usada por pessoas que precisam de texto maior. Ela muda o tamanho de fonte da raiz, então tudo em rem e em cresce junto. Um font-size: 14px continua com 14px, seja qual for a escolha do leitor. O zoom do navegador também escala o px, mas é na configuração de tamanho de fonte que muitos leitores se apoiam. Esse é o principal motivo para definir tamanhos de fonte em rem.
Alguns sites definem html { font-size: 62.5%; } para que 1rem valha 10px e a conta fique mais fácil. Isso continua respeitando a configuração do leitor (62.5% do que ele escolheu), mas lembre de definir um font-size legível no body, senão todo elemento sem tamanho próprio mostra texto de 10px.
Erros comuns
- Tamanhos de fonte em
emem elementos aninhados. Eles se multiplicam a cada nível. Useremnos tamanhos de fonte. height: 50%que não faz nada. O pai precisa de uma altura definida para uma altura em porcentagem funcionar.- Esperar que
padding-top: 10%siga a altura do pai. Porcentagens de padding e margin verticais usam a largura do pai. width: 100vwcriando uma barra de rolagem horizontal. Ele inclui a barra de rolagem. Use100%.height: 100vhcortado no celular. Use100svhou100dvh.- Definir
html { font-size: 12px }. Um valor em px na raiz sobrescreve a configuração de fonte do leitor na página toda. Deixe a raiz em paz ou use uma porcentagem.
Perguntas frequentes
Qual a diferença entre rem e em?
rem é relativo ao tamanho de fonte do elemento raiz (<html>), então 1.5rem tem o mesmo tamanho em toda a página. em é relativo ao tamanho de fonte do próprio elemento (no caso de font-size, ao do pai), então se multiplica quando os elementos são aninhados.
Quantos pixels tem 1rem?
16px por padrão, porque os navegadores definem o tamanho de fonte da raiz como 16px. Se o usuário aumentar o tamanho de fonte padrão nas configurações do navegador, ou se você definir html { font-size: ... }, o 1rem muda junto.
Devo usar rem ou px no tamanho da fonte?
Use rem nos tamanhos de fonte. Ele respeita a configuração de tamanho de fonte padrão do leitor no navegador, que o px ignora. O zoom do navegador escala os dois, mas nem todo leitor usa zoom.
Quando usar em em vez de rem?
Use em em espaçamentos que devem acompanhar o texto do próprio elemento, como o padding de um botão ou o tamanho de um ícone ao lado do texto. A mesma classe fica certa em qualquer tamanho de fonte. Use rem nos tamanhos de fonte e nos espaçamentos da página, para nada se multiplicar.
Qual a diferença entre 100vh e 100dvh?
Nos navegadores móveis, 100vh é a altura com a barra de endereço escondida, então uma seção 100vh fica mais alta que a tela visível enquanto a barra aparece. 100dvh (altura dinâmica da viewport) acompanha a barra quando ela aparece e some, e 100svh é sempre a altura menor.