background, border-radius e box-shadow no CSS
Como pintar o fundo com cor, imagem e gradiente, arredondar cantos sem deformar e usar sombra para criar profundidade, com cada cor medida no pixel.
background pinta o fundo, border-radius arredonda os cantos e box-shadow
desenha a sombra. As três mudam a aparência da caixa sem mexer no tamanho dela.
A quarta propriedade desta lição, a border, é a exceção: ela soma na largura e
empurra os vizinhos.
Os exemplos desta lição montam o painel da Clínica Veterinária Pata Amiga: ficha de consulta, banner de campanha de vacinação, medidor de vacinas em dia e campo de busca. O box model já explicou como o tamanho de cada caixa é calculado; agora a pergunta é o que aparece dentro dela.
Como cada cor desta página foi medida
Nenhuma cor aqui é chute. Cada trecho foi renderizado no Chrome for Testing
151.0.7922.34, controlado pelo Playwright, com deviceScaleFactor: 1 para que
um pixel do CSS seja um pixel do arquivo. A captura vira PNG, o PNG passa pelo
pngjs, e aí dá para ler o valor exato de qualquer ponto.
import { chromium } from 'playwright-core';
import { PNG } from 'pngjs';
const browser = await chromium.launch();
const page = await browser.newPage({ deviceScaleFactor: 1 });
export async function pixels(seletor) {
const png = PNG.sync.read(await page.locator(seletor).screenshot());
return (x, y) => {
const i = (png.width * y + x) << 2;
return `rgb(${png.data[i]},${png.data[i + 1]},${png.data[i + 2]})`;
};
}Quando você ler rgb(127,134,127) mais adiante, esse é o número que saiu do
navegador, não uma aproximação.
background-color e as camadas que se empilham
background não é uma propriedade só: é uma pilha. Cada valor separado por
vírgula em background-image é uma camada, e a primeira da lista fica por
cima. A background-color é sempre a última — o chão de tudo.
Este banner tem três camadas: dois retângulos de cor sólida (escritos como gradiente de uma cor só, o truque mais curto para isso) e a cor de fundo.
.banner {
width: 300px;
height: 80px;
background-image:
linear-gradient(#0038ff, #0038ff),
linear-gradient(#ffd400, #ffd400);
background-size: 80px 100%, 160px 100%;
background-position: left, left;
background-repeat: no-repeat, no-repeat;
background-color: #16a34a;
}A camada azul tem 80px e ganha nos primeiros 80. A amarela tem 160px, mas só
aparece de 80 a 160, porque a azul está por cima dela. Do 160 em diante não
sobra imagem nenhuma e o verde da background-color aparece. Se você inverter a
ordem das duas linhas de background-image, o amarelo cobre o azul inteiro.
background-image, size, position e repeat
Com imagem de verdade, três propriedades resolvem quase tudo: onde ela começa
(background-position), quanto ela ocupa (background-size) e se ela se repete
(background-repeat). A diferença que mais confunde é contain contra cover.
Aqui a capa mede 200×120 e a imagem é um retângulo 300×100, ou seja, mais “deitada” que a caixa. O fundo amarelo só existe para denunciar o espaço vazio.
.capa {
width: 200px;
height: 120px;
background-color: #ffd400;
background-image: url('campanha.svg');
background-position: center;
background-repeat: no-repeat;
}
.contain { background-size: contain }
.cover { background-size: cover }
.auto { background-size: auto }Leia o topo da caixa. Com contain ele é amarelo: a imagem inteira coube, e
sobrou fundo em cima e embaixo. Com cover ele é azul: a imagem foi ampliada
até cobrir tudo, e o que passou do limite foi cortado. Regra prática: cover
para foto de capa, contain para logotipo, que não pode ser cortado.
linear-gradient: o ângulo, as paradas e a faixa dura
O ângulo do linear-gradient mede a direção para onde o gradiente vai, com
0deg apontando para cima e crescendo no sentido horário. Muita gente lê
90deg como “de cima para baixo”; ele é da esquerda para a direita.
Duas paradas no mesmo ponto criam um corte seco em vez de uma transição — é assim que se faz uma faixa de duas cores sem imagem nenhuma.
.g90 { background-image: linear-gradient(90deg, #0038ff 0 30%, #ffd400 30%) }
.g180 { background-image: linear-gradient(180deg, #0038ff 0 30%, #ffd400 30%) }Em g90 o canto de baixo à esquerda continua azul, porque o gradiente anda na
horizontal. Em g180 é o canto de cima à direita que continua azul. Se você tem
dúvida na hora de escrever, use as palavras: to right e to bottom fazem o
mesmo e não deixam margem para erro.
radial-gradient e conic-gradient em uso real
O radial-gradient serve para luz: uma mancha clara que some nas bordas, sem
imagem e sem opacity no elemento inteiro. O conic-gradient gira ao redor de
um centro, o que dá um medidor de progresso de graça.
.medidor {
width: 120px;
height: 120px;
border-radius: 50%;
background: conic-gradient(#0038ff 0 65%, #e2e8f0 65%);
}
.luz {
width: 200px;
height: 120px;
background-color: #0f172a;
background-image: radial-gradient(circle 70px at 30% 50%, rgba(255, 212, 0, 0.45), transparent);
}Sessenta e cinco por cento de 360 graus dá 234. A amostragem confirma: em 230
graus ainda é azul, em 250 já é cinza. No radial-gradient, o centro da luz
está em 30% de 200px, ou seja, em x=60 — e é lá que a cor mais se afasta do
fundo. Em x=140, exatos 80px de distância, a luz de raio 70px já acabou e o
valor voltou a ser o #0f172a puro.
border, outline e a diferença que aparece no foco do teclado
border faz parte da caixa: ela soma na largura e empurra os vizinhos.
outline é desenhada por fora, sem ocupar espaço. E uma sombra com spread e
sem borrado (0 0 0 3px) desenha um anel que também não ocupa espaço, e ainda
acompanha o border-radius.
A diferença aparece na hora de marcar o botão que recebeu o foco pelo teclado. A barra de ações da clínica é uma linha de três botões distribuída com Flexbox, então qualquer pixel a mais empurra os vizinhos.
<div class="barra">
<button class="botao">Agendar</button>
<button class="botao alvo">Histórico</button>
<button class="botao">Vacinas</button>
</div>O estado de foco entra em um botão só, o do meio. Cada linha da medição abaixo é
uma das três classes aplicada ao .alvo, e mais nada muda na página — por isso
qualquer deslocamento do terceiro botão veio do botão do meio ter engordado.
body { margin: 0 }
.barra { display: flex; gap: 12px; width: 600px }
.botao { padding: 10px 16px; border: 0; border-radius: 8px; background: #e2e8f0 }
.borda { border: 3px solid #1e5fd0 }
.outline { outline: 3px solid #1e5fd0 }
.anel { box-shadow: 0 0 0 3px #1e5fd0 }A borda engordou o botão de 84×35 para 90×41 e empurrou o terceiro botão seis
pixels para a direita. outline e anel de sombra não moveram nada. É por isso
que estado de foco nunca deve ser feito com border: a barra inteira treme cada
vez que o cursor anda com o Tab.
| recurso | ocupa espaço | segue border-radius |
serve para |
|---|---|---|---|
border |
sim | sim | separar áreas de forma permanente |
outline |
não | sim (Chrome/Firefox 2021, Safari 2023) | foco do teclado, :focus-visible |
box-shadow com spread |
não | sim | anel colorido, várias camadas |
border-radius com dois raios: a elipse que quase ninguém usa
border-radius: 40px desenha um quarto de círculo em cada canto. Mas a
propriedade aceita uma barra: antes dela vão os raios horizontais, depois os
verticais. Isso transforma o canto em um quarto de elipse, que é o que você
quer quando o elemento é largo e baixo, como um selo.
Para medir, andei pela linha de cima e pela coluna da esquerda de um selo de 200×80 até achar o primeiro pixel já quase todo azul — o ponto em que a curva do canto acabou. A última coluna conta quantos pixels da linha do topo são azul cravado, ou seja, quanta aresta reta sobrou entre os dois cantos.
.selo { width: 200px; height: 80px; background: #0038ff }
.circular { border-radius: 40px }
.eliptico { border-radius: 40px / 16px }
.oval { border-radius: 50% }Os 38 no lugar de 40 são a borda suavizada: a ponta da curva ainda mistura azul
com branco por alguns pixels, e a varredura para antes do azul cravado. No canto
de 40px essa faixa tem dois pixels de largura — o primeiro pixel 100% azul
está mesmo em 40.
O que importa é a proporção. Em 40px / 16px o canto se espalha 35px na
horizontal e só 15px na vertical: a elipse deitada, que é o formato de selo.
50% é metade de cada lado — raio de 100px na horizontal e 40px na vertical
nesta caixa de 200×80. Essa é a curva mais rasa das três, e quanto mais rasa a
curva, mais larga fica a faixa suavizada: aqui ela come seis pixels, e por isso
a leitura sai 94 em vez de 100. Repare também no efeito colateral, que está na
última coluna. Com o raio horizontal valendo metade da largura, os dois cantos
de cima se encontram no meio e não sobra aresta reta nenhuma: 50% deixa 2
pixels de topo reto contra os 120 do 40px. O selo inteiro virou uma elipse.
Ou seja, a cápsula — lados retos e pontas redondas — não é o 50%, é o
primeiro caso. Em 40px o raio bate exatamente a metade da altura, as pontas
fecham e os 120 pixels de topo reto continuam lá. Escrever um valor absurdo
como border-radius: 999px dá no mesmo: o CSS reduz todos os raios até
caberem, e eles param nos mesmos 40px.
O canto arredondado que não recorta o filho
Este é o erro que mais aparece em cartão com foto. O pai tem border-radius,
mas a imagem dentro dele continua quadrada e vaza pelo canto.
.moldura { width: 200px; height: 100px; border-radius: 16px }
.foto { width: 100%; height: 60px; background: #0038ff }Sem overflow: hidden no pai, o pixel (2,2) — que deveria estar fora da curva —
é azul: o filho passou por cima do arredondamento. Com overflow: hidden ele é
branco, ou seja, o recorte funcionou. A propriedade que resolve isso tem outros
efeitos colaterais, e eles estão em
overflow no CSS.
box-shadow: offset, blur, spread e a sombra interna
A ordem dos quatro números é sempre a mesma: deslocamento horizontal, vertical, borrado e espalhamento. Só os dois primeiros são obrigatórios.
Com a palavra inset, a mesma sombra é pintada para dentro da caixa. É o efeito
de campo afundado, útil na busca do painel.
.campo {
width: 240px;
height: 44px;
border-radius: 8px;
background: #f8fafc;
border: 1px solid #cbd5e1;
box-shadow: inset 0 2px 4px rgba(15, 23, 42, 0.12);
}O fundo do campo é #f8fafc, que é rgb(248,250,252). A dois pixels do topo a
leitura é rgb(231,233,236): a sombra interna escureceu ali. A oito pixels o
valor já voltou ao fundo puro. Repare que a sombra existe só na faixa de cima,
porque o offset-y é positivo — ela desce a partir da borda superior.
Empilhar sombras: a receita de elevação em três camadas
Sombra de verdade não é uniforme. Perto do objeto ela é escura e definida; longe
ela é clara e larga. Uma única box-shadow só consegue imitar um dos dois. A
saída é declarar três, separadas por vírgula, com blur crescente.
Medi as duas versões da ficha da clínica lendo os pixels abaixo da borda de
baixo do cartão. O número é o quanto o pixel escureceu em relação ao fundo
#f1f5f9: zero é fundo limpo, quanto maior mais escuro.
/* uma camada só */
.ficha-plana {
box-shadow: 0 10px 20px rgba(15, 23, 42, 0.22);
}
/* três camadas */
.ficha-elevada {
box-shadow:
0 1px 2px rgba(15, 23, 42, 0.12),
0 3px 6px rgba(15, 23, 42, 0.10),
0 12px 24px rgba(15, 23, 42, 0.12);
}As duas começam quase iguais no contato: 41 contra 44. A diferença está na queda. A sombra única desce devagar nos primeiros oito pixels (41, 39, 35, 29): perde menos de um terço da força onde deveria despencar. A de três camadas cai quase 60% no mesmo trecho (44, 34, 27, 18) e depois se arrasta mais longe do objeto, ainda marcando 1 aos 32 pixels, onde a outra já sumiu. Contraste alto perto e cauda longe: é isso que o olho lê como um objeto encostado na superfície.
Guarde a receita em variáveis CSS e você tem uma escala de elevação inteira sem repetir número:
:root {
--elev-1: 0 1px 2px rgba(15, 23, 42, 0.12), 0 3px 6px rgba(15, 23, 42, 0.10);
--elev-2: var(--elev-1), 0 12px 24px rgba(15, 23, 42, 0.12);
}
.ficha { box-shadow: var(--elev-1) }
.ficha:hover { box-shadow: var(--elev-2) }Uma variável pode chamar outra e as duas listas se juntam. O
getComputedStyle mostra as três camadas montadas, e a medição dos pixels dá
exatamente os mesmos números da versão escrita à mão.
O gradiente que fica cinza no meio, e o oklab resolvendo
Este é o defeito mais chato do linear-gradient, e ele não é culpa sua. Por
padrão o navegador interpola as cores em sRGB, canal por canal. Quando as duas
pontas são quase opostas, a média dos canais cai perto do cinza — e o meio da
faixa morre.
O banner da campanha de vacinação vai de azul a amarelo. É o pior caso possível.
.srgb { background-image: linear-gradient(90deg, #0038ff, #ffd400) }
.oklab { background-image: linear-gradient(in oklab 90deg, #0038ff, #ffd400) }Olhe o meio da primeira linha: rgb(127,134,127). O maior e o menor canal estão
a sete unidades de distância — vermelho e azul empatados, verde um fio acima.
Isso é cinza, medido, bem no meio de um banner que deveria ser colorido. Na
segunda linha o mesmo ponto é rgb(127,158,188), com 61 unidades entre o menor
e o maior canal: um azul acinzentado, mas ainda azul.
A correção é escrever o espaço de cor dentro do próprio gradiente: in oklab
antes do ângulo. O oklab foi desenhado para que a distância entre duas cores
corresponda ao que o olho percebe, então o caminho entre elas não passa pelo
buraco cinza. A mesma família de espaços aparece na função oklch, que a lição
de cores no CSS detalha.
O erro mais comum: o atalho background apagando a cor
Você define a cor do cartão numa regra e, mais tarde, adiciona um ícone com o
atalho background. O ícone aparece e a cor some. É o comportamento correto de
qualquer atalho em CSS: as partes que você não escreveu voltam ao valor
inicial, e o inicial de background-color é transparent.
.ficha {
width: 300px;
height: 100px;
background-color: #0038ff;
border-radius: 12px;
}
/* apaga a cor sem avisar */
.ficha.destaque { background: url('pata.svg') right 16px center / 48px no-repeat }
/* preserva a cor */
.ficha.certa {
background-image: url('pata.svg');
background-position: right 16px center;
background-size: 48px;
background-repeat: no-repeat;
}O getComputedStyle entrega a prova: na versão com o atalho, o
background-color virou rgba(0, 0, 0, 0), transparente, e o branco da página
atravessou o cartão. Na versão com as propriedades separadas, o azul continua
rgb(0, 56, 255).
Duas saídas: ou você usa sempre as propriedades longas quando já existe uma cor
declarada, ou você repete a cor dentro do atalho —
background: #0038ff url('pata.svg') right 16px center / 48px no-repeat. Essa
armadilha vale para todo atalho do CSS, de font a grid-area.
O que vem depois
A caixa já tem tamanho, fundo, canto e sombra. O próximo passo da
trilha de CSS é decidir como ela se comporta ao lado das
outras: display, e depois posicionamento. Se quiser ver a ordem completa dos
assuntos antes de seguir, o guia de CSS mostra onde cada peça
entra.
Prefere aprender em vídeo?
Tem uma aula sobre este assunto no nosso canal.
Perguntas frequentes
Qual a diferença entre box-shadow e filter drop-shadow?
Dá para arredondar só dois cantos?
Como faço uma borda com gradiente?
Sombra deixa a página mais lenta?
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 Chrome for Testing 151.0.7922.34, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — background — developer.mozilla.org
- MDN — box-shadow — developer.mozilla.org
- MDN — border-radius — developer.mozilla.org
- CSS Backgrounds and Borders Module Level 3 — W3C — w3.org
- CSS Images Module Level 4 — W3C — w3.org



