Programação Por Equipe DevBras

Capa do artigo: Centralizar um bloco na horizontal e na vertical sem número mágico

Centralizar um bloco na horizontal e na vertical sem número mágico

Como centralizar com Flexbox ou Grid um bloco de qualquer altura, sem margem negativa calculada nem script de resize, e sem cortar o conteúdo em telas pequenas.

O cartão de login estava perfeito no meio da tela, até a tradução para o alemão deixar o título em duas linhas. Aí ele desceu e ficou torto, porque alguém tinha medido a altura com jQuery uma vez e aplicado uma margem negativa de metade dela. A resposta curta: quem centraliza é o contêiner, não o filho. Com display: grid; place-items: center; ou com display: flex; justify-content: center; align-items: center; no pai, e uma altura mínima nele, o bloco fica no centro nos dois eixos com qualquer altura que tiver. Nenhum número precisa ser medido e nenhum script precisa rodar no resize.

Por que o número mágico quebra

A técnica antiga era posicionar o bloco com top: 50% e left: 50% e depois puxá-lo de volta com margem negativa igual à metade da largura e da altura. Funciona enquanto o tamanho do bloco é exatamente o que você mediu. Qualquer mudança quebra o centro: texto traduzido, fonte maior nas configurações de acessibilidade, mensagem de erro que aparece no formulário, imagem que carrega depois.

A versão com JavaScript tenta remediar medindo offsetHeight e recalculando no evento de resize. Agora você tem código para manter, uma piscada de layout enquanto o script não roda, e ainda assim nenhuma reação quando o conteúdo muda sem a janela mudar.

O CSS moderno resolve isso porque Flexbox e Grid sabem distribuir espaço entre o contêiner e os filhos. O navegador calcula o centro toda vez que o layout é refeito, com o tamanho real naquele momento.

Como centralizar com Grid ou Flexbox

  1. Identifique a área em que o bloco deve ficar no meio: a tela inteira, um card, uma seção.
  2. Dê a essa área uma altura mínima. Sem altura disponível, não existe centro vertical, porque o pai encolhe para o tamanho do filho.
  3. Aplique Grid com place-items: center, ou Flexbox com os dois alinhamentos.
  4. No filho, limite só a largura. Deixe a altura livre para crescer com o conteúdo.
  5. Teste com conteúdo maior que a tela para garantir que nada é cortado.
<main class="tela-login">
  <form class="cartao">
    <h1>Entrar</h1>
    <label>E-mail <input type="email" name="email"></label>
    <label>Senha <input type="password" name="senha"></label>
    <button type="submit">Entrar</button>
  </form>
</main>
.tela-login {
  display: grid;
  place-items: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: 1rem;
  box-sizing: border-box;
}

.cartao {
  width: 100%;
  max-width: 24rem;
}

place-items: center é um atalho para align-items e justify-items com o mesmo valor. O min-height aparece duas vezes de propósito: navegadores que não conhecem a unidade dvh ficam com 100vh e ignoram a segunda linha. A unidade dvh acompanha a altura visível real no celular, onde a barra de endereço aparece e some. O padding evita que o cartão encoste nas bordas em telas pequenas.

A versão com Flexbox dá o mesmo resultado:

.tela-login {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100dvh;
}

Use Grid quando o contêiner tem um único filho a centralizar. Use Flexbox quando já há outros itens em linha ou coluna e o centro é parte de uma distribuição maior.

Por que min-height e não height

Com height: 100vh, o contêiner tem exatamente a altura da tela. Se o cartão ficar mais alto, por exemplo num celular deitado ou com a fonte aumentada, ele transborda para cima e para baixo do centro. A parte de cima sai da área rolável e a pessoa não consegue ver o título nem o primeiro campo. Com min-height, o contêiner cresce junto com o conteúdo. Quando cabe, fica no centro. Quando não cabe, começa do topo e a página rola normalmente.

Centro dentro de um componente

O mesmo princípio vale em escala menor. Para centralizar um ícone num botão quadrado, ou uma mensagem de "nenhum item" dentro de um painel, aplique Grid ou Flexbox no pai daquele elemento, não na página. O pai precisa ter algum tamanho definido, seja por altura mínima, seja pela linha de um grid externo.

Só na horizontal, para um bloco de largura limitada, a forma mais simples continua sendo margem automática nas laterais: max-width: 40rem; margin-inline: auto;. Ela não depende de nada no pai.

Em modais, um contêiner fixo cobrindo a tela com position: fixed; inset: 0; display: grid; place-items: center; centraliza a caixa sem cálculo. Se você usa o elemento dialog com showModal(), o próprio navegador já o centraliza com os estilos padrão.

Centralizar o bloco não é centralizar o texto

text-align: center alinha as linhas de texto dentro da caixa. É outra propriedade, para outro problema. Misturar as duas por reflexo resulta em parágrafos inteiros centralizados, com cada linha começando num ponto diferente, o que cansa a leitura. Centralize o bloco. Dentro dele, título curto pode ficar centralizado, e o texto corrido e os campos de formulário ficam alinhados à esquerda.

Erros comuns

  • Margem negativa calculada a partir da altura medida. Qualquer mudança de conteúdo, idioma ou fonte tira o bloco do centro.
  • Script recalculando posição no resize. Código extra, piscada de layout e nenhuma reação quando o conteúdo muda sem a janela mudar.
  • Usar height fixa no contêiner. Conteúdo maior que a tela é cortado em cima e fica inacessível.
  • Esquecer a altura do pai. O pai encolhe para o tamanho do filho e o centro vertical simplesmente não acontece.
  • Usar text-align: center para centralizar um bloco. O texto vai para o meio, o bloco não, e parágrafos longos ficam difíceis de ler.
  • Centralizar com position: absolute e transform em conteúdo que pode crescer. Não exige número mágico, mas o bloco sai do fluxo e é cortado quando passa da altura disponível.

Checklist

  • A centralização está no contêiner, com Grid ou Flexbox.
  • O contêiner usa min-height, não height.
  • O filho limita só a largura, com max-width.
  • Não há offsetHeight, margem negativa calculada nem listener de resize para centralizar.
  • Com a fonte do sistema em 200%, o bloco continua inteiro e rolável.
  • Num celular deitado, o topo do bloco continua visível.
  • Texto corrido dentro do bloco está alinhado à esquerda.

Aumente a fonte nas configurações de acessibilidade do seu celular e abra a tela que você centralizou. O bloco continua inteiro e no meio, ou o título sumiu para cima da tela? E esse centro ajuda a pessoa a achar a ação, ou só deixa espaço vazio dos lados?

Avalie este artigo

Referência: http://stackoverflow.com/questions/13903556/centering-a-div-vertically-horizontally-using-jquery