Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
Erro resolvidoIntermediáriocódigo testado

margin-top não funciona: a margem que colapsa no CSS

Por que o espaço que você pediu some, dobra ou empurra o pai inteiro: as quatro situações de colapso de margem, cada uma medida em pixels e corrigida.

Rodolfo Mori13 min de leitura

O margin-top funciona. O que muda é a conta: quando duas margens verticais se encostam, o navegador não soma as duas — ele fica com uma só, do tamanho da maior. Isso se chama colapso de margem, está na especificação do CSS desde 1998, e explica quase todo espaço que “sumiu” na sua página.

Os exemplos aqui são da página de serviços de uma clínica veterinária, a Pata Amiga: cartões de banho e tosa, vacinação e consulta. Cada número deste artigo saiu de getBoundingClientRect() e getComputedStyle() rodando no Chromium 151.0.7922.34, dirigido por Playwright. Nada foi estimado no olho.

O sintoma: 70px pedidos, 40px entregues

Dois cartões, um embaixo do outro, dentro de uma seção:

html
<section class="servicos">
  <article class="servico" id="banho">
    <h3>Banho e tosa</h3>
    <p>Agende pelo WhatsApp da clínica.</p>
  </article>

  <article class="servico" id="vacina">
    <h3>Vacinação anual</h3>
    <p>Carteirinha digital inclusa.</p>
  </article>
</section>

E o CSS, com a margem declarada nos dois sentidos — o jeito que quase todo mundo escreve na primeira vez:

css
body { margin: 0; }

.servicos { background: #f2f2f2; }

.servico {
  background: #fff;
  padding: 16px;
  margin-top: 40px;
  margin-bottom: 30px;
}

No papel, entre o primeiro e o segundo cartão existem 30px de margin-bottom mais 40px de margin-top. Setenta pixels. Vamos medir:

js
const banho = document.querySelector('#banho');
const vacina = document.querySelector('#vacina');
const servicos = document.querySelector('.servicos');

const espaco =
  vacina.getBoundingClientRect().top - banho.getBoundingClientRect().bottom;

console.log(`espaco real entre os cartoes: ${espaco}px`);
console.log(`topo do .servicos: ${servicos.getBoundingClientRect().top}px`);
Chromium 151.0.7922.34 espaco real entre os cartoes: 40px topo do .servicos: 40px

Dois defeitos numa página de dez linhas. O espaço entre os cartões virou 40px em vez de 70px, e a .servicos — que não tem margem nenhuma declarada — nasceu 40px abaixo do topo da janela. São duas situações diferentes de colapso acontecendo ao mesmo tempo. Vamos separar as quatro.

o que o CSS pede Banho e tosa margin-bottom: 30px margin-top: 40px Vacinação anual 70px de espaço no papel
plaintext
<text x="370" y="22" font-size="13" fill="currentColor">o que o navegador desenha</text>
<rect x="370" y="40" width="230" height="46" rx="6" stroke="currentColor" fill="none" stroke-width="2"/>
<text x="384" y="68" font-size="12" fill="currentColor">Banho e tosa</text>
<rect x="370" y="86" width="230" height="40" stroke="currentColor" fill="none" stroke-width="1" stroke-dasharray="5 4"/>
<text x="384" y="111" font-size="11" fill="currentColor">uma margem só: 40px</text>
<rect x="370" y="126" width="230" height="46" rx="6" stroke="currentColor" fill="none" stroke-width="2"/>
<text x="384" y="154" font-size="12" fill="currentColor">Vacinação anual</text>
<text x="370" y="230" font-size="12" fill="currentColor">40px na tela</text>

Caso 1: as margens de dois irmãos viram uma só

O primeiro caso é o do exemplo acima: a margem de baixo de um elemento encosta na margem de cima do irmão seguinte. Em vez de somar, o navegador fica com a maior das duas.

Rodei o mesmo par de cartões com sete combinações de valores, incluindo margem negativa, para ver a regra inteira:

js
import { chromium } from 'playwright-core';

const navegador = await chromium.launch();
const pagina = await navegador.newPage();
console.log('Chromium', navegador.version(), '\n');

const paginaComMargens = (cima, baixo) => `<style>
  body { margin: 0; }
  .servicos { background: #f2f2f2; }
  .servico { background: #fff; padding: 16px; margin-top: ${cima}px; margin-bottom: ${baixo}px; }
</style>
<section class="servicos">
  <article class="servico" id="banho"><h3>Banho e tosa</h3></article>
  <article class="servico" id="vacina"><h3>Vacinação anual</h3></article>
</section>`;

console.log('margin-bottom | margin-top | soma | distância real');

for (const [baixo, cima] of [[30, 40], [40, 30], [10, 10], [0, 40], [60, 0], [-20, 40], [-20, -30]]) {
  await pagina.setContent(paginaComMargens(cima, baixo));
  const distancia = await pagina.evaluate(() => {
    const a = document.querySelector('#banho').getBoundingClientRect();
    const b = document.querySelector('#vacina').getBoundingClientRect();
    return b.top - a.bottom;
  });
  console.log(
    `${(baixo + 'px').padStart(13)} | ${(cima + 'px').padStart(10)} |` +
      ` ${(baixo + cima + 'px').padStart(5)} | ${(distancia + 'px').padStart(14)}`,
  );
}

await navegador.close();
Chromium 151.0.7922.34

margin-bottom | margin-top | soma | distância real 30px | 40px | 70px | 40px 40px | 30px | 70px | 40px 10px | 10px | 20px | 10px 0px | 40px | 40px | 40px 60px | 0px | 60px | 60px -20px | 40px | 20px | 20px -20px | -30px | -50px | -30px

Três regras saem dessa tabela, e são as três que a especificação descreve:

  • Duas margens positivas: vale a maior. 30 com 4040, não 70.
  • Uma positiva e uma negativa: aí sim o navegador soma. 40 com -2020 — é assim que margem negativa puxa um bloco para cima do outro.
  • Duas negativas: vale a mais negativa. -20 com -30-30.

Repare na linha do 10px com 10px: o resultado é 10px. Se você espaçou uma lista inteira de horários com margin: 10px 0 e achou o espaçamento apertado demais, era isso.

Caso 2: a margem do filho vaza e empurra o pai

Este é o que assusta mais, porque o elemento que se move não tem margem nenhuma no CSS. Um cartão com um título dentro, e nada além disso:

html
<div class="painel">
  <div class="cartao">
    <h3>Consulta de rotina</h3>
  </div>
</div>
css
body { margin: 0; }

.painel { background: #eef; }
.cartao { background: #fff; }

.cartao h3 {
  margin-top: 32px;
  margin-bottom: 0;
}

A intenção era afastar o título do topo do cartão em 32px. Medindo:

js
const b = (s) => document.querySelector(s).getBoundingClientRect();

console.log(`topo do .painel: ${b('.painel').top}px`);
console.log(`topo do .cartao: ${b('.cartao').top}px`);
console.log(`do topo do .cartao ate o topo do titulo: ${b('h3').top - b('.cartao').top}px`);
console.log(`altura do .cartao: ${b('.cartao').height}px`);
console.log(`altura do titulo: ${b('h3').height}px`);
Chromium 151.0.7922.34 topo do .painel: 32px topo do .cartao: 32px do topo do .cartao ate o topo do titulo: 0px altura do .cartao: 22px altura do titulo: 22px

Leia com calma: dentro do cartão o título está a 0px do topo. Os 32px foram parar fora, empurrando o .cartao, o .painel e tudo o que estava acima — e nenhum dos dois tem uma linha de margem no CSS. O cartão, por sua vez, tem exatamente a altura do título.

Quando não existe nada separando a borda de cima do pai da borda de cima do primeiro filho — nem padding, nem border, nem uma linha de texto — as duas margens se encostam e colapsam. A margem resultante fica por fora do pai.

Embaixo acontece o mesmo com o margin-bottom do último filho, com uma condição a mais: o pai precisa ter altura automática. Troquei o exemplo para margin-bottom: 24px no título e dei height: 60px ao .painel: a base do título passou a ficar 38px acima da base do painel, ou seja, a margem ficou presa lá dentro. Altura fixa (ou min-height) impede que uma borda encoste na outra, e sem encostar não existe colapso.

Caso 3: um elemento vazio funde as próprias margens

Um <div> separador entre os horários de atendimento, sem conteúdo nenhum dentro:

html
<div class="horario" id="manha">Segunda a sexta, 8h as 12h</div>
<div class="divisoria" id="div"></div>
<div class="horario" id="tarde">Segunda a sexta, 14h as 18h</div>
css
.horario { background: #fff; padding: 8px; margin: 0; }

.divisoria {
  margin-top: 40px;
  margin-bottom: 24px;
}

Você declarou 64px de espaço total. Mas a .divisoria não tem altura, nem padding, nem border — então a margem de cima dela encosta na de baixo, por dentro do próprio elemento, e as duas colapsam:

js
const b = (s) => document.querySelector(s).getBoundingClientRect();

console.log(`altura da .divisoria: ${b('#div').height}px`);
console.log(`distancia entre os dois horarios: ${b('#tarde').top - b('#manha').bottom}px`);
Chromium 151.0.7922.34 altura da .divisoria: 0px distancia entre os dois horarios: 40px

Sessenta e quatro declarados, quarenta na tela: a maior das duas margens do próprio elemento, e nada além dela. Esse comportamento tem nome na especificação, self-collapsing block, e vale para qualquer elemento que não tenha altura, conteúdo, padding nem border — e que também não crie um contexto de formatação próprio, detalhe que volta na dobra do flow-root.

Caso 4: o colapso atravessa o elemento vazio e pega os vizinhos

Pior: uma vez que o elemento vazio colapsou consigo mesmo, a margem que sobrou continua encostada nos dois vizinhos. Dei margem aos horários também — margin-bottom: 20px no de manhã, margin-top: 16px no da tarde — e testei três variações da divisória:

js
// mesma `pagina` do Playwright aberta na dobra do caso 1
const comDivisoria = (extra) => `<style>
  body { margin: 0; }
  .horario { background: #fff; padding: 8px; }
  #manha { margin-bottom: 20px; }
  #tarde { margin-top: 16px; }
  .divisoria { margin-top: 40px; margin-bottom: 24px; ${extra} }
</style>
<div class="horario" id="manha">Segunda a sexta, 8h as 12h</div>
<div class="divisoria" id="div"></div>
<div class="horario" id="tarde">Segunda a sexta, 14h as 18h</div>`;

for (const [rotulo, extra] of [
  ['.divisoria vazia', ''],
  ['.divisoria com height: 1px', 'height: 1px;'],
  ['.divisoria com padding: 1px 0', 'padding: 1px 0;'],
]) {
  await pagina.setContent(comDivisoria(extra));
  const medida = await pagina.evaluate(() => {
    const b = (s) => document.querySelector(s).getBoundingClientRect();
    return { altura: b('#div').height, distancia: b('#tarde').top - b('#manha').bottom };
  });

  console.log(`vizinhos com 20px e 16px, ${rotulo}`);
  console.log(`  soma das margens declaradas: ${20 + 40 + 24 + 16}px`);
  console.log(`  altura da .divisoria: ${medida.altura}px`);
  console.log(`  distância entre os dois horários: ${medida.distancia}px\n`);
}
Chromium 151.0.7922.34

vizinhos com 20px e 16px, .divisoria vazia soma das margens declaradas: 100px altura da .divisoria: 0px distância entre os dois horários: 40px

vizinhos com 20px e 16px, .divisoria com height: 1px soma das margens declaradas: 100px altura da .divisoria: 1px distância entre os dois horários: 65px

vizinhos com 20px e 16px, .divisoria com padding: 1px 0 soma das margens declaradas: 100px altura da .divisoria: 2px distância entre os dois horários: 66px

Cem pixels declarados, quarenta na tela. E olhe o que um pixel faz: dar height: 1px à divisória sobe o total para 65px, porque agora existe corpo separando as duas margens dela — os 40px de cima param de encostar nos 24px de baixo. Com padding: 1px 0, o elemento passa a ter 2px e a distância vira 66px.

Esse é o caso que mais consome tempo de depuração, porque o culpado é um elemento invisível. Se você tem um <div> de layout que não desenha nada e o espaçamento da página está estranho, comece por ele.

Só o eixo vertical colapsa

Uma verificação que economiza confusão: margem esquerda e direita nunca colapsam. Coloquei margem nos dois eixos no mesmo cartão para comparar:

css
.painel { background: #eef; width: 400px; }
.cartao { background: #fff; margin-left: 20px; }

.cartao h3 {
  margin-left: 32px;
  margin-top: 32px;
  margin-bottom: 24px;
}
Chromium 151.0.7922.34 esquerda do .cartao dentro do .painel: 20px esquerda do titulo dentro do .cartao: 32px esquerda do titulo dentro do .painel: 52px altura do titulo: 22px altura do .cartao: 22px da base do titulo ate a base do .painel: 0px

Na horizontal, 20 + 32 = 52: as duas margens se somam, como qualquer pessoa esperaria. Na vertical, o cartão continua com a altura exata do título e a base do painel coincide com a base do título — os 24px de margin-bottom do título saíram inteiros para fora dos dois. Mesma propriedade, mesmos valores, dois comportamentos. Vale a pena reler o box model do CSS com isso em mente: a margem é a única das quatro camadas da caixa que se comporta assim.

O que bloqueia o colapso: padding, border, overflow, flex e grid

O caso 2 é o mais fácil de corrigir, porque qualquer coisa que separe a borda do pai do primeiro filho já resolve. Testei nove variantes do mesmo cartão — o original, sem nada, mais as oito linhas abaixo, uma por vez —, medindo onde o .painel foi parar e onde o título ficou dentro do .cartao:

css
/* o .cartao do caso 2, com uma destas linhas por vez */
.cartao { background: #fff; padding-top: 1px; }
.cartao { background: #fff; border-top: 1px solid #ccc; }
.cartao { background: #fff; overflow: hidden; }
.cartao { background: #fff; overflow: auto; }
.cartao { background: #fff; display: flex; flex-direction: column; }
.cartao { background: #fff; display: grid; }
.cartao { background: #fff; display: inline-block; }
.cartao { background: #fff; display: flow-root; }
Chromium 151.0.7922.34

regra aplicada no .cartao | topo do .painel | h3 dentro do .cartao (nada) | 32px | 0px padding-top: 1px | 0px | 33px border-top: 1px solid | 0px | 33px overflow: hidden | 0px | 32px overflow: auto | 0px | 32px display: flex | 0px | 32px display: grid | 0px | 32px display: inline-block | 0px | 32px display: flow-root | 0px | 32px

Todas as nove funcionam, e nenhuma é igual à outra:

correção o que ela também faz quando eu usaria
padding-top: 1px vira 33px, não 32 — o pixel entra na conta nunca; é o remendo que sempre reaparece no code review
border-top: 1px solid mesma soma indevida, mais uma linha visível só se você já quisesse a borda
overflow: hidden corta qualquer coisa que saia da caixa perigoso: mata sombra, tooltip e menu suspenso
overflow: auto pode criar barra de rolagem só quando o conteúdo rola de verdade
display: flex / grid muda o layout inteiro dos filhos quando você ia usar Flexbox ali de qualquer jeito
display: inline-block o elemento para de ocupar a linha toda quase nunca, num container
display: flow-root nada além de conter as margens sempre que a intenção for só essa

O overflow: hidden é a correção que mais aparece em resposta de fórum, e é a que mais volta como bug depois. Ela funciona porque cria um contexto de formatação de bloco, mas leva junto o corte de conteúdo — o preço está descrito em overflow no CSS.

display: flow-root conserta o vazamento, não o irmão

flow-root foi criado exatamente para isso: cria o contexto de formatação e não faz mais nada. É o overflow: hidden sem o efeito colateral, e é a resposta certa quando a sua intenção é apenas conter as margens dos filhos.

O que quase nenhuma resposta de fórum diz é até onde ele vai. Apliquei flow-root na .servicos da página da clínica e medi as duas coisas ao mesmo tempo: onde a seção nasce e qual a distância entre os cartões.

css
/* aplicado no .servicos, uma opção por vez */
.servicos { display: flow-root; }
.servicos { overflow: hidden; }
.servicos { display: flex; flex-direction: column; }
.servicos { display: flex; flex-direction: column; gap: 24px; }
Chromium 151.0.7922.34

regra no .servicos | topo do .servicos | entre os cartões (nada — o CSS original) | 40px | 40px display: flow-root | 0px | 40px overflow: hidden | 0px | 40px display: flex; flex-direction: column | 0px | 70px flex + gap: 24px, sem margem no filho | 0px | 24px

Leia a coluna da direita. Com flow-root, a seção volta para o topo da página — o caso 2 morreu — mas os cartões continuam a 40px um do outro: o caso 1 passou ileso. Quem devolve os 70px é o display: flex, porque item de container flex não colapsa margem com ninguém. Ali 30 + 40 volta a ser 70.

E na divisória vazia? Testei flow-root nela também, com os horários vizinhos carregando margin-bottom: 20px e margin-top: 16px:

css
/* aplicado na .divisoria do caso 4, uma opção por vez */
.divisoria { display: flow-root; }
.divisoria { overflow: hidden; }
.divisoria { min-height: 1px; }
Chromium 151.0.7922.34

sem nada altura 0px distancia 40px display: flow-root altura 0px distancia 64px overflow: hidden altura 0px distancia 64px min-height: 1px altura 1px distancia 65px

Sobe de 40px para 64px. Ou seja: flow-root impede a divisória de fundir as próprias margens (o caso 3), mas ela continua colapsando com os vizinhos (o caso 4) — por isso 64px, e não os 100px declarados. Um min-height: 1px chega ao mesmo lugar, mais um pixel de altura.

caso o que acontece o que corrige de fato
1 — irmãos adjacentes vale a maior das duas margens gap, margem num sentido só, ou pai flex/grid
2 — filho empurra o pai a margem sai por fora do pai display: flow-root no pai
3 — elemento vazio se funde as duas margens dele viram uma flow-root ou min-height no elemento vazio
4 — colapso atravessa quatro margens viram uma apagar o elemento vazio, ou pôr gap no container

A correção que eu uso primeiro: gap no lugar de margin

A última linha da medição é a que interessa: flex com gap: 24px e zero margem no filho dá exatamente 24px entre os cartões. Sempre. Não tem maior, não tem soma, não tem vazamento para o pai.

css
.servicos {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.servico {
  background: #fff;
  padding: 16px;
  /* nenhuma margem aqui */
}

O motivo é simples: gap é uma propriedade do container, não do filho. Quem manda no espaço entre os itens é quem os organiza. Isso resolve o problema na raiz em vez de bloquear o colapso, e funciona igual em Flexbox e em Grid.

O comentário /* nenhuma margem aqui */ não é enfeite. gap soma com a margem que sobrou no filho, não substitui a margem — e dentro de um container flex não existe colapso para segurar a conta:

js
// mesma `pagina` do Playwright aberta na dobra do caso 1
const comGap = (margem) => `<style>
  body { margin: 0; }
  .servicos { display: flex; flex-direction: column; gap: 24px; }
  .servico { background: #fff; padding: 16px; ${margem} }
</style>
<section class="servicos">
  <article class="servico" id="banho"><h3>Banho e tosa</h3></article>
  <article class="servico" id="vacina"><h3>Vacinação anual</h3></article>
</section>`;

for (const [rotulo, margem] of [
  ['filho sem margem nenhuma', ''],
  ['filho ainda com as margens antigas', 'margin-top: 40px; margin-bottom: 30px;'],
]) {
  await pagina.setContent(comGap(margem));
  const distancia = await pagina.evaluate(() => {
    const b = (s) => document.querySelector(s).getBoundingClientRect();
    return b('#vacina').top - b('#banho').bottom;
  });
  console.log(`${rotulo.padEnd(36)} ${distancia}px`);
}
Chromium 151.0.7922.34 filho sem margem nenhuma 24px filho ainda com as margens antigas 94px

Noventa e quatro é 24 + 40 + 30, tudo somado. Quem troca margin por gap e esquece de apagar a margem antiga sai de um espaçamento que encolhia sozinho para um que cresce sozinho. Trocar é apagar uma coisa e escrever a outra.

Quando gap não serve — texto corrido, artigo de blog, conteúdo vindo de um CMS —, a segunda melhor regra é a da margem em um sentido só: escolha margin-bottom para tudo e nunca declare margin-top. Sem duas margens se encostando, não existe colapso para debater. Se sobrar um espaço indesejado no fim do bloco, :last-child { margin-bottom: 0 } resolve.

Confirmando no navegador em dez segundos

Aqui está a parte que atrapalha na hora do diagnóstico: o painel de estilos do DevTools mostra a margem que você pediu, não a que sobrou. Um elemento que teve a margem colapsada continua exibindo margin-top: 40px em Computed, e o diagrama do box model desenha os 40px como se existissem.

Cole isto no console, apontando para os dois elementos que deveriam estar separados:

js
const a = document.querySelector('#banho');
const b = document.querySelector('#vacina');
const servicos = document.querySelector('.servicos');

console.log(`margin-bottom computado do #banho: ${getComputedStyle(a).marginBottom}`);
console.log(`margin-top computado do #vacina: ${getComputedStyle(b).marginTop}`);
console.log(`espaco real entre os dois: ${b.getBoundingClientRect().top - a.getBoundingClientRect().bottom}px`);
console.log(`margin-top computado do .servicos: ${getComputedStyle(servicos).marginTop}`);
console.log(`topo do .servicos na pagina: ${servicos.getBoundingClientRect().top}px`);
Chromium 151.0.7922.34 margin-bottom computado do #banho: 30px margin-top computado do #vacina: 40px espaco real entre os dois: 40px margin-top computado do .servicos: 0px topo do .servicos na pagina: 40px

As duas últimas linhas são a assinatura do caso 2: margin-top computado igual a 0px num elemento que mesmo assim começa 40px abaixo. Quando você vir isso, não procure mais o CSS do elemento — procure o CSS do primeiro filho dele.

O diagnóstico completo em três perguntas, na ordem:

  1. O espaço entre dois irmãos é menor que a soma? É o caso 1 — troque por gap ou use margem num sentido só.
  2. Um elemento sem margem começa deslocado? É o caso 2 — o culpado é o primeiro filho; ponha display: flow-root no pai ou mova a margem para o filho certo.
  3. Existe um elemento vazio no meio? É o caso 3 ou 4 — apague o elemento, ou dê corpo a ele.

Por onde seguir

O colapso de margem é o primeiro de uma família de comportamentos do CSS que só fazem sentido quando você entende que o navegador tem regras próprias de composição. O parente mais próximo é o display no CSS, que decide se um elemento participa do fluxo normal — e portanto se ele colapsa ou não. O mapa completo da linguagem, com a ordem em que cada assunto entra, está no guia de CSS; a sequência de aulas fica na trilha de CSS.

Antes de fechar a aba: abra o seu projeto atual, escolha um bloco que “está com o espaçamento estranho” e rode o snippet do console acima. Em dez segundos você sabe se é colapso ou se é outra coisa.

  • css
  • margin
  • colapso de margem
  • box model
  • erro

Perguntas frequentes

Colapso de margem é um bug do navegador?
Não. Está escrito na especificação desde o CSS 2, na seção sobre margens colapsantes, e todo navegador implementa igual. A intenção era tipográfica: uma sequência de parágrafos com margem em cima e embaixo fica com espaço uniforme em vez de dobrar entre um e outro.
Por que ninguém reclama disso em layout com Flexbox?
Porque item de container flex ou grid não colapsa margem com ninguém. Se o seu projeto inteiro é feito de containers flex, você pode passar anos sem topar com o problema — e aí ele aparece no primeiro bloco de texto em fluxo normal e parece feitiço.
Adicionar !important no margin-top resolve?
Não. Testei com margin-top de 40px marcado como !important encostado num margin-bottom de 30px, e a distância continuou 40px. O !important decide qual regra vence a disputa da cascata; o colapso acontece depois, na hora de montar o layout com o valor que já venceu.
Trocar margin por padding é uma correção válida?
Padding nunca colapsa, então funciona. O preço é que padding fica dentro da caixa: ele recebe o background, a borda passa por fora dele e o clique dentro daquele espaço ainda é um clique no elemento. Para respiro entre componentes, gap é melhor; para respiro dentro de um cartão, padding é exatamente o certo.
Como isso aparece no React ou no Tailwind?
Igual. O colapso é do motor de layout do navegador, não da biblioteca. A classe mt-10 do Tailwind vira margin-top e colapsa do mesmo jeito; um componente React que devolve uma div com margem também empurra o pai. O que muda é a dificuldade de achar o culpado, porque o CSS está espalhado em vários arquivos.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Chromium 151.0.7922.34 dirigido por Playwright, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. MDN — Mastering margin collapsing — developer.mozilla.org
  2. CSS 2.2 — Collapsing margins (8.3.1) — w3.org
  3. CSS Display Module Level 3 — flow-root — w3.org

Continue por aqui