Unidades no CSS: px, rem, em, %, vw e ch
Quando cada unidade muda de valor, por que em acumula dentro de listas aninhadas e qual usar em fonte, espaçamento e largura sem quebrar o zoom.
Toda medida no CSS precisa de uma unidade, e a unidade decide de onde o
navegador tira o número. px entrega um valor fixo, rem olha para a raiz do
documento, em olha para o elemento pai, % muda de referência conforme a
propriedade e vw/vh olham para a janela. Escolher errado não gera erro no
console: gera um layout que quebra quando outra pessoa aumenta a fonte.
Os exemplos são sempre a mesma página: o painel de agendamento da Clínica
Veterinária Pata Amiga. Todos os números deste artigo saíram de
getComputedStyle e getBoundingClientRect rodando no Chromium 151.0.7922.34,
controlado pelo Playwright. Se class, padding e width ainda soam novos,
vale passar antes por o que é CSS e
box model.
px: o pixel do CSS não é o pixel da sua tela
px é a unidade absoluta do CSS. Absoluta quer dizer que ela não consulta nenhum
ancestral: 200px é 200px em qualquer lugar do documento. O que ela não
quer dizer é “200 pontinhos de luz do seu monitor”.
.selo-vacina {
width: 200px;
height: 120px;
}Esse mesmo selo foi renderizado três vezes, mudando só a densidade da tela, e depois recortado num PNG para contar os pixels de verdade do arquivo:
A largura em CSS não mudou. O arquivo triplicou. Num celular moderno, um px do
CSS costuma valer três pixels físicos — é por isso que a mesma página não fica
minúscula numa tela de alta densidade.
O px do CSS é uma medida de referência: 1/96 de polegada, um ângulo de
visão, não um ponto do hardware. Ele continua sendo a escolha certa para coisas
que não devem crescer com o texto: border, box-shadow, o traço de 1px que
separa duas linhas da agenda.
rem: o tamanho vem sempre da raiz, e só dela
rem significa root em: o tamanho da fonte do elemento <html>. Se a raiz
vale 16px, 1rem vale 16px em qualquer profundidade do documento. Sempre.
Esta é a lista de especialidades da clínica, com quatro níveis de aninhamento:
<ul class="especialidades">
<li>Clínica geral
<ul><li>Dermatologia
<ul><li>Alergia alimentar
<ul><li>Dieta de eliminação</li></ul>
</li></ul>
</li></ul>
</li>
</ul>html { font-size: 16px; }
.especialidades li { font-size: 1.2rem; }Medindo font-size de cada <li>, do primeiro ao quarto nível:
Quatro níveis, um número só. 1.2rem é 1,2 × 16px, e o pai não entra na conta.
Essa previsibilidade é o motivo de rem ser o padrão para font-size num
projeto inteiro.
em: o tamanho vem do pai — e vai se acumulando
Agora troque uma letra. O mesmo HTML, o mesmo seletor, em no lugar de rem:
html { font-size: 16px; }
.especialidades li { font-size: 1.2em; }O primeiro nível vale 19,2px porque o pai dele é o <ul class="especialidades">,
que nenhuma regra tocou e por isso ficou com os 16px herdados da raiz. O segundo
nível não usa 16px: usa 19,2px, o valor do pai dele. E multiplica de novo.
No quarto nível, o texto está com 33px — mais que o dobro do que você pediu,
sem que ninguém tenha escrito 33 em lugar nenhum.
É o efeito bola de neve. Ele só aparece quando a mesma propriedade font-size
em em é aplicada a elementos que se aninham — lista dentro de lista, comentário
dentro de comentário, cartão dentro de cartão.
Em outras propriedades, em olha para o próprio elemento
Aqui mora a confusão. Em font-size, em é o tamanho da fonte do pai. Em
qualquer outra propriedade, em é o tamanho da fonte do próprio elemento,
já calculado.
.card-pet { font-size: 20px; }
.card-pet .selo {
font-size: 0.5em;
padding: 1em;
margin-top: 2em;
}O font-size virou 10px porque leu os 20px do pai. O padding virou 10px
porque leu os 10px que o próprio selo acabou de ganhar. Não é inconsistência:
é a mesma regra, aplicada na ordem em que o navegador calcula as coisas.
E esse comportamento é exatamente o que torna em a melhor unidade para o
espaçamento interno de um componente:
.botao {
font-size: 1rem;
padding: 0.75em 1.5em;
border-radius: 0.5em;
}
.botao-grande { font-size: 1.5rem; }O botão grande é o mesmo elemento com as duas classes
(class="botao botao-grande"): ele herda o padding da primeira e só troca o
font-size na segunda.
Uma linha mudou o tamanho da fonte e o botão inteiro cresceu proporcional:
padding, cantos, tudo. Com padding: 12px fixo, o botão grande ficaria com o
texto espremido. Regra prática: rem para o tamanho da fonte, em para o que
gira em torno dela.
% muda de referência conforme a propriedade
Porcentagem é a unidade mais traiçoeira do CSS porque parece óbvia e não é. Ela sempre se refere a alguma coisa, mas o “alguma coisa” muda de propriedade para propriedade.
Esta é a ficha do paciente, com 600px de largura e 300px de altura:
.ficha { width: 600px; height: 300px; font-size: 20px; }
.ficha .foto { width: 50%; }
.ficha .moldura { padding-top: 10%; padding-left: 10%; }
.ficha .nome { font-size: 120%; line-height: 150%; }
.ficha .barra { height: 50%; }Olhe para padding-top. A ficha tem 300px de altura, então 10% “deveria” dar
30px. Deu 60px: padding e margin em porcentagem se referem à largura do
bloco contêiner, mesmo no topo e na base. É essa regra que faz o truque do
padding-top: 56.25% para manter um vídeo em 16:9.
E height: 50% só funciona porque a ficha tem altura declarada. Com o pai em
height: auto, a porcentagem não tem em que se apoiar. A mesma barra, dentro de
dois pais que só diferem nisso:
.ficha-fixa { width: 600px; height: 300px; font-size: 20px; }
.ficha-auto { width: 600px; font-size: 20px; }
.barra { height: 50%; }Os 23px são a altura do conteúdo — uma linha de texto de 20px. A declaração
height: 50% foi simplesmente descartada.
line-height: 1.5 e line-height: 150% não são a mesma coisa
Com porcentagem, o navegador calcula o valor uma vez, no elemento onde você escreveu, e transmite esse resultado em pixels para os filhos. Sem unidade, ele transmite o número e cada filho faz a própria conta.
.bloco { font-size: 16px; }
.bloco.numero { line-height: 1.5; }
.bloco.percentual { line-height: 150%; }
.bloco .titulo { font-size: 40px; }O título de 40px herdou uma entrelinha de 24px e ficou com as letras se tocando.
Por isso line-height sem unidade é a recomendação padrão.
vw, vh e a barra do celular: svh, lvh e dvh
vw e vh são 1% da largura e da altura da janela. Elas ignoram completamente o
elemento pai — e é aí que quebram.
.coluna { width: 400px; }
.banner-pc { width: 100%; }
.banner-vw { width: 100vw; }
.capa-vh { height: 100vh; }
.capa-svh { height: 100svh; }
.capa-lvh { height: 100lvh; }
.capa-dvh { height: 100dvh; }Dentro de uma coluna de 400px, 100% deu 400px e 100vw deu 800px — o dobro da
coluna, transbordando para fora dela. No viewport de celular, 100vw caiu para
390px enquanto 100% continuou nos 400px do pai. São referências diferentes, e
misturar as duas é a causa mais comum de rolagem horizontal indesejada.
Repare que 100vh, 100svh, 100lvh e 100dvh deram o mesmo número nas duas
medições. Isso acontece porque o navegador de teste não tem barra de endereço que
recolhe. Num celular de verdade elas se separam:
| unidade | mede a altura | quando usar |
|---|---|---|
vh |
a altura “grande”, como lvh na maioria dos navegadores |
evite em tela cheia no celular |
svh |
com todas as barras visíveis (a menor) | garante que nada fique escondido |
lvh |
com as barras recolhidas (a maior) | fundo que pode ser cortado |
dvh |
a altura de agora, muda durante a rolagem | herói de tela cheia |
Para a capa de tela cheia do agendamento, min-height: 100dvh resolve o clássico
“o botão fica escondido atrás da barra do navegador”.
ch e ex: medir em caracteres em vez de em pontos
1ch é a largura do caractere 0 na fonte em uso; 1ex é a altura da letra
x. As duas mudam quando a fonte muda:
.medida { display: inline-block; width: 1ch; }
.medida.x { width: 1ex; }
.sistema { font-family: system-ui, sans-serif; }
.georgia { font-family: Georgia, serif; }
.mono { font-family: "Courier New", monospace; }A mesma .medida de 1ch, com font-size: 16px, medida nas três fontes:
O uso clássico é limitar a largura de um texto corrido: max-width: 60ch para o
comunicado que a clínica manda no fim da consulta. Mas cuidado com a promessa —
60ch não é “60 caracteres”, exceto em fonte monoespaçada.
A frase do comunicado tem exatamente 60 caracteres — “Thor precisa ficar em
jejum de oito horas antes da cirurgia.” — e foi medida contra uma caixa de
60ch, nas mesmas duas fontes:
.caixa { width: 60ch; }Em Courier New os dois números batem exatamente: monoespaçada, todo caractere
tem a mesma largura. Em system-ui, a frase ocupa 424px numa caixa de 605px —
sobra espaço para umas 25 letras a mais, porque i, l e espaço são bem mais
estreitos que o 0. 60ch continua sendo um bom limite de leitura; só não
prometa contagem exata.
O erro que mais aparece: número sem unidade
Este é o erro que trava iniciante em CSS, e ele é silencioso. Não existe
SyntaxError, não existe aviso no console: o navegador simplesmente descarta a
declaração inválida e segue com o valor anterior.
.painel { width: 600px; }
.painel .sem-unidade { width: 300; }
.painel .com-espaco { width: 300 px; }
.painel .calc-errado { width: calc(100%-20px); }
.painel .calc-certo { width: calc(100% - 20px); }
.painel .zero { width: 0; }As três primeiras caixas ficaram com 600px, a largura total do painel, porque
width voltou a valer auto. Você olha para o CSS, lê width: 300, e jura que
está lá — mas o navegador nunca aceitou aquela linha.
Três detalhes que geram esse resultado:
- Sem unidade não vale.
300não é comprimento. - Espaço entre número e unidade não vale.
300 pxsão dois valores. calc()exige espaço em volta de-e+. Sem espaço,100%-20pxé lido como um único valor esquisito e a expressão inteira é rejeitada.
Dá para confirmar isso na hora, no console do navegador, sem inspecionar elemento nenhum:
CSS.supports('width', '300');
CSS.supports('width', '300px');
CSS.supports('width', 'calc(100%-20px)');
CSS.supports('line-height', '1.5');As duas exceções que confundem: 0 pode ir sem unidade (zero é zero em
qualquer escala) e line-height aceita número puro de propósito, como você
viu acima.
Fonte em px ignora quem aumentou a letra do navegador
Chegou a parte que decide o projeto. Nas configurações do navegador existem
duas formas de aumentar o texto, e elas não são a mesma coisa. O zoom de
página amplia tudo junto, inclusive o que está em px — por isso ele nunca
“quebra”. A outra é a preferência de tamanho de fonte, que só mexe no que é
relativo. Muita gente com baixa visão deixa essa preferência em 20px ou 24px e
nunca mais toca no zoom, porque zoom desalinha o layout inteiro.
Este é o cartão de aviso da clínica, escrito duas vezes: uma em px, outra em
rem.
.cartao-px { font-size: 14px; padding: 16px; width: 320px; }
.cartao-rem { font-size: 0.875rem; padding: 1rem; width: 20rem; }A mesma página, aberta com a preferência de fonte em 16px e depois em 24px:
Com a preferência em 16px, os dois cartões são idênticos — 352px, 14px de texto.
Com a preferência em 24px, o cartão em rem acompanhou: texto de 21px, respiro
de 24px, cartão de 528px. O cartão em px ficou exatamente igual: 14px de texto
para quem pediu letra maior.
Não aparece bug nenhum no seu monitor. Aparece na tela de quem precisa.
O truque do 62.5% ainda é seguro. Já 10px não é
Muita gente escreve html { font-size: 62.5% } para que 1rem vire 10px e a
conta mental fique fácil. Outra gente escreve html { font-size: 10px }
achando que é a mesma coisa. Não é:
html { font-size: 62.5%; } /* versão A */
html { font-size: 10px; } /* versão B */
.aviso { font-size: 1.4rem; }Na preferência padrão, as duas versões dão o mesmo resultado. Quando a pessoa
sobe para 24px, a versão A escala junto (raiz de 15px, aviso de 21px) e a versão
B congela. 62.5% é relativo; 10px é uma trava.
em na media query ignora o font-size da raiz
Um detalhe que pega gente experiente. Dentro de @media, em e rem usam o
tamanho inicial da fonte, e não o que você declarou em html:
html { font-size: 20px; }
@media (min-width: 40em) { .agenda { background: green; } }A quebra aconteceu em 640px, que é 40 × 16, e não em 800px, que seria 40 × 20.
Isso é bom: sua grade não se move quando alguém mexe na fonte. Só não conte com
o font-size da raiz para calcular breakpoint. Se isso ainda é novidade, a
lição de media queries entra em detalhe.
Tabela de decisão: qual unidade para cada propriedade
Este é o critério que eu uso e defendo, com o motivo de cada escolha:
| propriedade | unidade | por quê |
|---|---|---|
font-size de texto |
rem |
acompanha a preferência da pessoa e não acumula |
font-size de título fluido |
clamp() com rem e vw |
cresce com a tela sem travar o zoom |
padding e gap de componente |
em |
escala junto com a fonte daquele componente |
margin entre blocos |
rem |
espaçamento da página não depende do componente |
width de coluna de texto |
ch |
o limite real de leitura é em caracteres |
max-width de contêiner |
rem |
trabalha junto com os breakpoints |
width de bloco dentro do pai |
% |
segue o contêiner, não a janela |
border, box-shadow, outline |
px |
detalhe visual que não precisa crescer |
| altura de tela cheia no celular | dvh |
acompanha a barra que recolhe |
breakpoint em @media |
em ou px |
os dois usam a base de 16px |
Duas coisas que não entram nessa lista: pt, cm e in, que só fazem
sentido em folha de estilo para impressão, e px em font-size de texto
corrido, que é o assunto da seção anterior.
O que vem depois
Com as unidades resolvidas, o próximo salto é parar de repetir número no
arquivo: variáveis CSS guardam a escala de
espaçamento em um lugar só, e tipografia monta a
escala de tamanhos com clamp(). O mapa completo da matéria está no
guia de CSS e a ordem de estudo, na
trilha de CSS.
Prefere aprender em vídeo?
Tem uma aula sobre este assunto no nosso canal.
Perguntas frequentes
Posso usar px para tudo e resolver com media query?
rem e em dão o mesmo resultado quando a raiz vale 16px?
Qual a diferença entre 100vh e 100%?
Existe unidade certa para border e box-shadow?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Chromium 151.0.7922.34 via Playwright 1.62.1, Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — CSS values and units — developer.mozilla.org
- CSS Values and Units Module Level 4 — w3.org
- WCAG 2.2 — Resize Text (1.4.4) — w3.org



