Skip to content
This page has been auto-translated and may contain errors.View in English

O modelo de caixa

docs.scrimba.com

Assim que você começa a espaçar as coisas com CSS, você esbarra no mesmo quebra-cabeça que todos esbarram: você define uma largura, adiciona alguns preenchimentos, e o elemento fica mais largo do que você pediu. Dois parágrafos ficam mais próximos um do outro do que os números indicam que deveriam. Nada disso é um erro. É o modelo de caixa, o conjunto de regras que decide quanto espaço cada elemento ocupa, e uma vez que você consegue visualizá-lo, a maioria de suas surpresas de espaçamento deixa de ser surpresa.

Cada elemento é uma caixa

CSS trata cada elemento na página como um retângulo, até mesmo uma única palavra ou um link. Esse retângulo é construído a partir de quatro camadas empilhadas de dentro para fora, e isso é o modelo de caixa: o conteúdo no meio, depois preenchimento ao redor dele, depois uma borda, depois margem do lado de fora.

Imagine uma foto emoldurada em uma parede. A foto é o conteúdo. O passepartout branco ao redor da foto é o preenchimento, espaço respirável dentro do marco. O marco de madeira em si é a borda. E a parede vazia entre este marco e o próximo é a margem. Cada elemento que você estiliza é embalado da mesma forma, de dentro para fora.

css
.card {
  padding: 16px;              /* espaço dentro, ao redor do conteúdo */
  border: 2px solid #333;     /* o marco */
  margin: 24px;               /* espaço fora, afastando vizinhos */
}

Cada elemento que o navegador renderiza é uma caixa retangular, e essa caixa tem quatro partes medidas para fora do centro: a caixa de conteúdo (onde seu texto ou imagem fica), o preenchimento, a borda e a margem. Você já viu essa ideia em como CSS funciona; o modelo de caixa é a parte que governa tamanho e espaçamento.

A ordem importa porque cada camada envolve a anterior. O preenchimento fica entre o conteúdo e a borda, então uma cor de fundo preenche o conteúdo e o preenchimento, mas para na borda externa. Margem fica fora da borda e é sempre transparente, é por isso que você não pode dar uma cor à margem.

css
.card {
  padding: 16px;              /* dentro da borda, compartilha o fundo */
  border: 2px solid #333;
  margin: 24px;               /* fora da borda, sempre transparente */
  background: #f4f4f4;        /* preenche conteúdo + preenchimento, para na borda */
}

Cada elemento renderizado gera pelo menos uma caixa retangular, e o modelo de caixa é a especificação para suas quatro regiões aninhadas. De dentro para fora: a caixa de conteúdo (a área que contém o texto, imagem ou caixas filhas do elemento), a caixa de preenchimento (conteúdo mais preenchimento, o espaço transparente que ainda carrega o fundo do elemento), a caixa de borda (caixa de preenchimento mais a borda, a borda visível) e a caixa de margem (caixa de borda mais margem, a região transparente mais externa que separa o elemento de seus vizinhos).

Duas consequências resultam desse aninhamento que enganam as pessoas depois. Primeiro, fundos pintam a caixa de preenchimento, não a caixa de margem, então o preenchimento lhe dá espaço interior clicável e colorível enquanto a margem lhe dá separação vazia que você não pode estilizar. Segundo, a largura e altura que você declara não descrevem a caixa inteira por padrão; elas descrevem apenas uma região dela, e qual região depende de box-sizing, que as próximas seções tornam precisa.

css
.card {
  padding: 16px;
  border: 2px solid #333;
  margin: 24px;
  background: #f4f4f4;        /* pinta conteúdo + preenchimento, nunca margem */
}
JunoCada elemento é uma caixa Cada elemento é uma caixa com quatro camadas de dentro para fora: conteúdo, depois preenchimento, depois borda, depois margem. A foto de foto emoldurada pegou comigo: foto, passepartout, marco, espaço até o próximo marco. Consiga essa imagem em sua cabeça e o resto deste capítulo decorre disso.
JunoCada elemento é uma caixa Conteúdo, preenchimento, borda, margem, envolvendo para fora nessa ordem. O fundo preenche o conteúdo e o preenchimento e para na borda, e a margem é sempre transparente, é por isso que margin não aceita cor. Mantenha o aninhamento reto e as regras de espaçamento param de parecer arbitrárias.
JunoCada elemento é uma caixa Quatro regiões aninhadas: caixa de conteúdo, caixa de preenchimento, caixa de borda, caixa de margem. O fundo pinta até a caixa de borda e nunca a margem, então o preenchimento é interior colorível e a margem é separação vazia. E sua width declarada sempre dimensiona apenas uma dessas regiões, que é toda a razão pela qual a próxima seção sobre box-sizing existe.

padding, border e margin

As três camadas de espaçamento fazem um trabalho cada uma. Preenchimento é espaço do lado de dentro, entre o conteúdo e a borda. Margem é espaço do lado de fora, afastando outros elementos. A borda é a linha desenhada entre eles.

Você pode definir um valor para todos os quatro lados de uma vez, ou direcionar um único lado:

css
.card {
  padding: 20px;             /* todos os quatro lados ganham 20px */
  margin-bottom: 32px;       /* apenas a borda inferior */
}

A forma resumida padding: 20px é uma maneira rápida de dizer topo, direita, fundo e esquerda todos juntos. Quando você precisa que um lado seja diferente do resto, use a propriedade específica como margin-bottom.

Preenchimento é espaço dentro da borda, margem é espaço fora dela, e a borda é a borda entre eles. A regra a manter é que margem afasta vizinhos, preenchimento cresce o interior do próprio elemento. Se um fundo parece apertado contra seu texto, esse é um trabalho de preenchimento. Se dois elementos ficam muito próximos, esse é um trabalho de margem.

Cada um leva uma forma resumida que preenche os lados no sentido dos ponteiros do relógio a partir do topo. Um valor se aplica a todos os quatro lados, dois valores definem vertical depois horizontal, quatro valores vão topo, direita, fundo, esquerda:

css
.hero {
  padding: 40px 24px;        /* 40px topo e fundo, 24px esquerda e direita */
  margin: 0 auto;            /* sem margem vertical, auto esquerda e direita para centrar */
}

.card {
  padding-left: 16px;        /* por lado quando apenas uma borda difere */
}

Preenchimento, borda e margem são três controles de espaçamento independentes com comportamentos diferentes, não três nomes para a mesma lacuna. Preenchimento é a região transparente dentro da borda que ainda carrega o fundo e ainda responde a cliques. Margem é a região transparente fora da borda que separa o elemento de seus irmãos e não carrega o fundo. A borda é a borda desenhada que também ocupa espaço de layout igual à sua largura.

Os três aceitam a forma resumida no sentido dos ponteiros do relógio (um, dois, três ou quatro valores expandindo para topo / direita / fundo / esquerda), e todos os três têm longhands por lado. Dois comportamentos valem a pena manter precisamente. Margens podem ser negativas, o que puxa um elemento em direção a ou sobre um vizinho e é uma ferramenta de layout legítima, enquanto preenchimento não pode ir abaixo de zero. E margin: 0 auto centra um bloco horizontalmente apenas quando o elemento tem uma largura definida, porque auto resolve dividindo o espaço horizontal restante, e não há espaço restante para dividir se a caixa já preenche seu recipiente.

css
.hero {
  padding: 40px 24px;        /* pares no sentido dos ponteiros do relógio: vertical, horizontal */
  margin: 0 auto;            /* centra apenas com uma largura definida em .hero */
  border-bottom: 1px solid #ddd;  /* borda longhand, uma borda, leva 1px de layout */
}

.card {
  margin-top: -8px;          /* margem negativa puxa o cartão para cima */
}
Junopadding, border e margin Preenchimento é espaço dentro da borda, margem é espaço fora dela, e a borda é a linha entre. Use padding quando o texto parece apertado dentro de uma caixa, e margin quando duas caixas ficam muito próximas. Um valor como padding: 20px cobre todos os quatro lados, ou nomeie um lado como margin-bottom quando apenas um precisa mudar.
Junopadding, border e margin Preenchimento cresce o interior do próprio elemento, margem afasta seus vizinhos, e saber qual você quer economiza muito palpite. A forma resumida vai no sentido dos ponteiros do relógio a partir do topo: um valor para todos os lados, dois para vertical depois horizontal. Quando um fundo abraça seu texto muito perto, é preenchimento que você quer, não margem.
Junopadding, border e margin Preenchimento carrega o fundo e cliques, margem é separação transparente, e a borda leva espaço de layout real igual à sua largura. Margens podem ficar negativas para puxar elementos juntos, preenchimento não pode. E margin: 0 auto apenas centra um bloco que tem uma largura definida, porque auto não tem espaço restante para dividir caso contrário.

box-sizing: content-box vs border-box

Aqui está a surpresa que pega quase todo mundo. Você define um cartão para width: 300px, adiciona um pouco de preenchimento, e o cartão acaba ficando mais largo que 300 pixels na página. Você não fez nada errado. Por padrão, width apenas mede o conteúdo no meio, e qualquer preenchimento e borda são adicionados em cima.

Há uma correção de uma linha que faz a largura se comportar da forma que você esperava. Defina box-sizing: border-box, e agora a largura que você digita é a largura completa da caixa, preenchimento e borda inclusos:

css
.card {
  box-sizing: border-box;    /* width agora inclui preenchimento e borda */
  width: 300px;
  padding: 20px;             /* o cartão fica com 300px de largura, não 340px */
}

Por padrão, todo elemento usa box-sizing: content-box, o que significa que a width que você define se aplica apenas à caixa de conteúdo. Preenchimento e borda são então adicionados fora dele, então a caixa que você realmente vê na página é mais larga que o número que você digitou. Este é o clássico gotcha de dimensionamento por trás de cem layouts estourados.

Faça as contas uma vez e fica claro. Com content-box, uma largura de 300px mais 20px de preenchimento em cada lado mais uma borda de 2px em cada lado renderiza em 300 + 40 + 4 = 344px. Mude para border-box e width agora significa a caixa de borda: o preenchimento e a borda são subtraídos de dentro, então o elemento fica exatamente 300px e o conteúdo encolhe para caber.

css
.card {
  box-sizing: content-box;   /* o padrão */
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
  /* largura renderizada real: 300 + 20 + 20 + 2 + 2 = 344px */
}

Como isso é quase sempre o que você quer, o movimento padrão é definir globalmente com o seletor universal para que cada elemento opte:

css
* {
  box-sizing: border-box;    /* uma regra, dimensionamento previsível em todo lugar */
}

box-sizing decide qual região sua width e height medem, e tem dois valores. O valor inicial é content-box: width define a caixa de conteúdo, e preenchimento e borda são dispostos fora dessa largura, então a caixa de borda, o footprint real na página, é width + padding-left + padding-right + border-left + border-right. A alternativa é border-box: width define a caixa de borda diretamente, e o navegador subtrai preenchimento e borda de dentro, encolhendo a caixa de conteúdo para o que sobra.

A aritmética vale a pena afirmar exatamente, porque é a razão inteira pela qual a propriedade existe. Dado width: 300px; padding: 20px; border: 2px:

css
/* content-box (padrão): preenchimento e borda se adicionam em cima */
.card {
  box-sizing: content-box;
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
  /* caixa de borda = 300 + 40 + 4 = 344px; caixa de conteúdo = 300px */
}

/* border-box: preenchimento e borda consumem a largura declarada */
.card {
  box-sizing: border-box;
  width: 300px;
  padding: 20px;
  border: 2px solid #333;
  /* caixa de borda = 300px; caixa de conteúdo = 300 - 40 - 4 = 256px */
}

Recomende border-box: faz o dimensionamento componível. A razão é prática: com border-box, um conjunto de colunas declaradas em width: 25% permanecem em um quarto do recipiente não importa qual preenchimento você adicione, porque o preenchimento não muda mais o tamanho externo. Essa previsibilidade é o motivo pelo qual o reset quase universal o aplica a tudo, geralmente incluindo pseudo-elementos, para que nada escorregue de volta para content-box:

css
*, *::before, *::after {
  box-sizing: border-box;
}

Uma nota de precisão: isso apenas governa width e height. Margens estão sempre fora da caixa de borda independentemente de box-sizing, então nunca contam para o tamanho declarado, o que conecta diretamente com o colapso de margem abaixo. Se você precisa de unidades para dimensionar essas caixas de forma confiável, unidades de dimensionamento cobre como percentuais, rem e unidades de janela de visualização são resolvidas.

Junobox-sizing: content-box vs border-box Por padrão, a width que você define apenas mede o conteúdo no meio, então preenchimento e borda fazem a caixa ficar maior que o número que você digitou. Definir box-sizing: border-box corrige isso: a largura se torna a caixa inteira, preenchimento e borda inclusos. Este aqui me pegou por idades, então vale a pena lembrar cedo.
Junobox-sizing: content-box vs border-box O content-box padrão significa que width dimensiona apenas o conteúdo, então preenchimento e borda empurram a caixa real mais larga que você pediu. Um cartão de 300px com 20px de preenchimento e uma borda de 2px realmente renderiza em 344px. Mude para border-box e a largura significa a caixa inteira, é por isso que defini-la em * globalmente é a linha padrão inicial da maioria das folhas de estilo.
Junobox-sizing: content-box vs border-boxcontent-box adiciona preenchimento e borda fora de sua largura, então uma caixa de 300px se torna 344px; border-box os subtrai de dentro e mantém em 300px. Procure por border-box porque larguras percentuais permanecem componíveis quando preenchimento não muda mais o tamanho externo. Aplique a * mais os pseudo-elementos para que nada silenciosamente volte ao padrão.

Colapso de margem

Uma última surpresa, e é sobre margens se empilhando. Coloque dois parágrafos um sobre o outro, dê ao primeiro uma margem inferior de 24px e ao segundo uma margem superior de 16px, e você pode esperar 40px de lacuna entre eles. Você realmente recebe 24px.

Isso é chamado colapso de margem. Quando duas margens verticais se encontram, o navegador não as soma. Ele mantém a maior das duas e joga a menor fora. Então a margem maior vence, e a lacuna é 24px, não 40px. Isso só acontece com margens superior e inferior, nunca com preenchimento.

css
.intro {
  margin-bottom: 24px;
}
.details {
  margin-top: 16px;
  /* a lacuna entre eles é 24px, a margem maior, não 40px */
}

Quando a margem inferior de um bloco encontra a margem superior do próximo, os dois não se somam. Eles colapsam em uma única margem igual à maior das duas. Dê a um parágrafo margin-bottom: 24px e ao próximo margin-top: 16px, e o espaço entre eles é 24px, não 40px. Isso é colapso de margem, e só afeta margens verticais.

A razão de saber é que muda como você raciocina sobre espaçamento. Se uma lacuna é menor que os dois números sugerem, você provavelmente está observando um colapso, e adicionar à margem menor não faz nada até passar a maior. A maneira limpa de controlar o ritmo vertical é definir margem em apenas uma direção, por exemplo um margin-bottom em cada bloco e sem margin-top, então o colapso não tem nada para resolver.

css
.article > * {
  margin-top: 0;
  margin-bottom: 24px;       /* uma direção, então lacunas são previsíveis */
}

Colapso de margem é a regra que margens verticais adjacentes em fluxo normal se mesclam em uma, cujo tamanho é a maior das duas (ou a mais positiva, com negativos tratados somando a maior positiva e a mais negativa). Fluxo normal é o padrão de empilhamento topo-a-fundo de caixas de bloco antes que qualquer flex, grid ou float mude isso. Dê a .intro uma margem inferior de 24px e a .details uma margem superior de 16px e a lacuna é 24px, porque o navegador resolve ambas em uma única margem colapsada.

A parte precisa é saber o que faz e o que não faz colapso, porque as escapes são como você para uma surpresa:

  • Afeta apenas margens verticais. Margens horizontais nunca colapsam; dois elementos lado a lado sempre somam suas margens enfrentadas.
  • Qualquer coisa entre as duas margens para o colapso: uma borda, qualquer preenchimento, ou conteúdo no pai entre a margem de um filho e a sua própria.
  • Margens não colapsam através de um limite de contexto de formatação. Um contêiner flex ou grid estabelece um para seus filhos, então itens dentro dele nunca colapsam suas margens, que é uma razão pela qual layouts modernos parecem mais previsíveis que os da era de flutuação.
  • Um pai e seu primeiro ou último filho podem colapsar através um do outro quando nada os separa, então a margem superior de um filho pode unexpectedly aparecer fora do pai. Adicionar padding-top ou uma borda ao pai para isso.
css
/* O colapso acontece: irmãos adjacentes em fluxo normal */
.intro { margin-bottom: 24px; }
.details { margin-top: 16px; }   /* lacuna se resolve para 24px */

/* Colapso parado: uma borda entre as margens */
.section {
  border-top: 1px solid #eee;    /* agora ambas as margens se aplicam, sem fusão */
}
JunoColapso de margem Quando uma margem inferior encontra uma margem superior, o navegador mantém a maior e descarta a menor, em vez de somá-las. Então 24px abaixo encontrando 16px acima dá uma lacuna de 24px, não 40px. Isso só acontece com margens superior e inferior, e surpreende todo mundo na primeira vez.
JunoColapso de margem Margens verticais entre blocos colapsam para a maior das duas, nunca a soma, então uma lacuna pode ser menor que os números sugerem. Quando isso acontece, crescer a margem menor não faz nada até ultrapassar a maior. Definir margem em apenas uma direção, como margin-bottom em cada bloco, contorna a coisa toda.
JunoColapso de margem Margens verticais adjacentes em fluxo normal se mesclam para o maior valor, e margens horizontais nunca fazem. Uma borda ou preenchimento entre elas para o colapso, e contêineres flex e grid param completamente porque fazem um novo contexto de formatação. Observe o caso de pai-e-primeiro-filho, onde a margem superior de um filho escapa do pai até você adicionar padding-top ou uma borda para segurá-lo.