O drama do posicionamento de elementos na tela e o Flexbox

O CSS é uma ferramenta que revolucionou o webdesign. No início da Web, as páginas eram todas desenhadas usando tabelas no código HTML, o que era doloroso de escrever e manter. Com a popularização do CSS, houve uma separação entre conteúdo e apresentação: o conteúdo fica no HTML, a apresentação no CSS. Assim, ambas as partes ficaram mais fáceis de manter, além de inúmeros outros benefícios que comentamos ao longo do nosso curso de desenvolvimento Web.

Com CSS, podemos escrever o HTML pensando apenas na informação que queremos apresentar, sem nos preocuparmos com seu visual final, isso graças ao poder que o CSS nos dá para modificar o visual de qualquer elemento da página.

No entanto, alguns layouts ainda são bastante desafiadores de serem feitos apenas com CSS. Considere o exemplo abaixo:

Rodapé complicado de fazer com CSS

Rodapé complicado de fazer com CSS

Parece simples, não? No entanto, como fazer os ícones das redes sociais ficarem à direita e centralizados verticalmente com o logotipo à esquerda? Algumas possibilidades são usar a propriedade position, a propriedade float ou a propriedade display mas, em todos os casos, teremos que usar algum número mágico. Considere que nosso HTML está como abaixo:

<footer>
    <div class="container">
        <h1>Mirror Fashion</h1>
        <ul class="social">
            <li><a href="#">Facebook</a></li>
            <li><a href="#">Twitter</a></li>
            <li><a href="#">Google+</a></li>
        </ul>
    </div>
</footer>

A div com a classe container ajuda a centralizar o conteúdo no interior do rodapé. Veja como poderíamos posicionar os ícones de rede social, da lista com a classe social, usando a propriedade position:

.container {
    position: relative;
}

.social {
    position: absolute;
    top: 11px; /* 11px!? Por quê!? */
    right: 0;
}

Repare que colocamos a declaração top: 11px para deixar os ícones centralizados verticalmente com o logotipo. Mas de onde vêm esses 11px? Podemos chegar nesse valor fazendo algumas contas, medindo a diferença de altura entre o logotipo na esquerda (54px) e os ícones (32px). Podemos, inclusive, deixar isso claro no código CSS, o que é considerado uma boa prática:

.social {
    position: absolute;
    top: 11px; /* 11px = (logo (54px) - icones (32px)) / 2 */
    right: 0;
}

No entanto, esse número continua sendo meio mágico, não? E o que acontece se resolvermos trocar o logotipo ou os ícones por imagens de tamanhos diferentes? Teremos que mexer no CSS, não? Isso é sinal de que nosso layout não está muito flexível, aumentando o seu custo de manutenção.

É para resolver esses e outros casos que está surgindo um novo conjunto de regras para o CSS conhecido como módulo de regras de layout Flexbox. O principal objetivo dessa nova especificação é facilitar o controle de espaçamentos e tamanhos de elementos da página passando mais controle dos elementos filhos para o elemento pai. Assim, as declarações passam a valer independente de quantos e quais elementos filhos um determinado elemento container tem.

Vejamos um exemplo simples de como essa nova especificação pode nos ajudar. Considere o layout abaixo:

Três caixas lado a lado num container

Três caixas lado a lado num container

Para fazer as três caixas ficarem lado a lado dentro do container, podemos, simplificando um pouco, fazer

.caixa {
    width: 33%;
    float: left;
}

No entanto, a interação do width com as propriedades padding e border pode complicar nossas vidas na hora de fazer as três caixas sempre caberem na mesma linha da página. Além disso, o que acontece se decidirmos que teremos que colocar quatro caixas em vez de três apenas? Teremos que, novamente, mexer no CSS. Com a nova especificação flexbox fica muito mais simples! Basta indicar que o container é um container flex:

.container {
    display: flex;
}

E, agora, indicar que as caixas dentro desse container devem ocupar igualmente o espaço (daí o valor 1 para todas elas):

.caixa {
    flex: 1;
}

E pronto! Veja que, em nenhum momento, precisamos fazer alguma conta ou nos preocuparmos com as dimensões de cada uma das caixas. O próprio navegador vai cuidar disso para nós!

Neste exemplo, tínhamos um número fixo de caixas: três. No entanto, nem sempre é assim. Em alguns cenários, podemos ter um número de caixas dentro de um container variável. Poderíamos ter cinco, seis caixas. Neste caso, fica bem complicado fazer todas essas caixas permanecerem lado a lado dentro do container usando a propriedade width, não? Pois com flexbox não poderia ser mais simples:

.container {
    display: flex;
}
.caixa {
    flex: 1;
}

Exatamente o mesmo CSS de antes! Repare que a declaração flex: 1 faz com que o navegador distribua o espaço do container entre as caixas independente do número de caixas!

Agora, e se quisermos que a distribuição de espaço não seja exatamente igual entre as caixas? Poderíamos querer que a primeira caixa fosse maior que as outras, por exemplo. Sem problemas!

.caixa {
    flex: 1;
}
.caixa:first-child {
    flex: 2;
}

Desta forma, dizemos ao navegador que a primeira caixa deve receber duas vezes mais espaço que as outras, novamente sem importar quantas sejam as outras.

Voltando ao nosso problema original: queríamos deixar os ícones das redes sociais à direita e centralizados verticalmente com o logotipo no nosso rodapé. Num container flex, além de poder controlar o tamanho dos filhos de um container, podemos controlar a distribuição de espaço entre eles e o alinhamento deles com relação ao container.

Neste caso do rodapé, queremos que os elementos fiquem centralizados verticalmente no rodapé. Então basta declararmos

.container {
    display: flex;
    align-items: center;
}

Com isso já chegamos no seguinte resultado:

Footer com display: flex e align-items: center ativados

Footer com display: flex e align-items: center ativados

Agora, para fazer os ícones das redes sociais ficarem à direita, podemos mudar a forma como o container flex distribui o espaço que sobra entre seus filhos. Como não declaramos a propriedade flex nem no logotipo nem nos ícones, eles permanecem com o tamanho original. Assim, sobra bastante espaço livre no rodapé. Esse espaço sobrando, por padrão, fica após os elementos, mas podemos mudar essa distribuição com a propriedade justify-content de um container flex. No nosso caso, queremos que o espaço fique todo entre os dois elementos, então basta declararmos isso

.container {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

E pronto! Com apenas três declarações, chegamos ao resultado desejado sem usar números mágicos no CSS.

Rodapé complicado de fazer com CSS

Com a especificação Flexbox ficou fácil!

Os dois exemplos dados lidam com dimensões e espaços no eixo horizontal da página, mas também é possível usar um container flex para controlar os espaços e tamanhos no eixo vertical da página. Considere o seguinte layout, bastante usado por diversos sites:

Layout simples, com cabeçalho, corpo e rodapé

Layout simples, com cabeçalho, corpo e rodapé

O código HTML dessa página seria algo como

<header>
    <h1>Cabeçalho</h1>
</header>
<main>
    <h1>Corpo</h1>
    <p>Algum texto aqui</p>
</main>
<footer>
    <h1>Rodapé</h1>
</footer>

Neste layout, o rodapé deve sempre aparecer no final da tela, independente do tamanho do texto. Uma forma de fazer isso é transformar nossa página inteira num container flex:

body {
    display: flex;
}

Além disso, é necessário deixar nossa página ocupando sempre a tela toda:

html, body {
    height: 100%;
}

Fazendo isso, já temos quase o que queremos. Falta apenas dizer para o body que os filhos dele devem ser dispostos um em cima do outro, e não um ao lado do outro, como é o padrão com um container flex:

body {
    flex-direction: column;
}

Com essas declarações, voltamos praticamente ao ponto inicial:

Layout simples (cabeçalho, corpo e rodapé) com espaço sobrando após o rodapé

Layout simples (cabeçalho, corpo e rodapé) com espaço sobrando após o rodapé

No entanto, agora basta declararmos que o corpo do site deve ocupar todo o espaço sobrando no container flex:

main {
    flex: 1;
}

E pronto! Como não declaramos a propriedade flex nem para o cabeçalho nem para o rodapé, eles permanecem com o tamanho original. Resta então ao corpo da página crescer para ocupar o espaço completo da página.

Um outro problema que a especificação Flexbox ataca é a ordem dos elementos em uma página. Antes dela, a ordem em que os elementos aparecem numa página HTML é basicamente determinada pela ordem em que eles aparecem no código fonte da página. Isso pode ser um fator limitante, principalmente considerando layouts responsivos em que, muitas vezes, queremos mostrar elementos da página em ordens diferentes no desktop e nos dispositivos móveis. Considere o exemplo abaixo:

Página de vendas de um serviço com três opções de pacotes e preços

Página de vendas de um serviço com três opções de pacotes e preços

No desktop, é interessante mostrar o pacote mais relevante no meio da tela. Entretanto, nos tablets e celulares, os três pacotes lado a lado não caberiam muito bem. Uma alternativa, então, é deixar o pacote mais interessante acima dos outros, como nas figuras abaixo.

Versão mobile da página de três serviços

Versão mobile da página de três serviços

Versão tablet da página de três serviços

Versão tablet da página de três serviços

O código HTML para essa parte da página, simplificado, poderia ser algo como

<ol class="pacotes">
    <li class="pacote pacote-principal">...</li>
    <li class="pacote pacote-menor">...</li>
    <li class="pacote pacote-maior">...</li>
</ol>

Deixamos o pacote mais relevante em primeiro no HTML. Dessa forma, para os layouts mobile e tablet, não há muito segredo. Mas e o desktop?

Transformando a lista de pacotes num container flex, podemos usar a propriedade order nos elementos filhos para reordená-los como quisermos dentro do container:

.pacote-principal {
    order: 2;
}
.pacote-menor {
    order: 1;
}
.pacote-maior {
    order: 3;
}

Essa mesma técnica também pode ser bastante interessante para tornar nosso site mais acessível para leitores de tela. Podemos, por exemplo, colocar um menu de navegação no início do HTML para os leitores de tela, mesmo se ele aparecer depois no layout!

Por esses exemplos, é possível notar que a especificação Flexbox é bastante poderosa. No entanto, é importante ressaltar que ela não é feita para resolver todos os problemas de layout. Em muitos casos, usar as propriedades mais antigas position e float já basta e resulta num layout que funciona em mais navegadores.

Vale notar que a especificação Flexbox já é recomendação candidata pela W3C, ou seja, os navegadores já devem começar a implementá-la. O suporte dos navegadores já é razoável: tirando o Internet Explorer, que passou a suportar a especificação apenas na versão 10, e o Opera, que passou a suportá-la apenas na versão 12.1, todos os outros já a suportam há bastante tempo. Alguns ainda implementam uma sintaxe antiga da especificação, em que se usava display: box em vez de display: flex.

Os exemplos usados neste post estão online. Veja o código fonte no Github e teste no seu navegador! Conheça mais nos nossos cursos de front-end.

17 Comentários

  1. Cezar Luiz 24/07/2013 at 10:10 #

    Opa Luiz, post bem explicado, parabéns! Gostaria de saber se é possível utilizar já essas propriedades e também com ajuda de polyfills. É possível?

    Obrigado.

  2. Flávio Almeida 24/07/2013 at 11:37 #

    Luiz, gostei muito do post. Ele demonstra que essas preocupações tão corriqueiras não passam desapercebidas por aqueles que ditam o futuro do CSS.

  3. Luiz Corte Real 24/07/2013 at 18:30 #

    Oi, Cezar! Obrigado!

    É possível usá-las, sim! Os únicos navegadores que podem dar problema são o Internet Explorer (o Flexbox só funciona a partir da versão 10) e o Firefox, no qual não há suporte completo da especificação (não suporta as propriedades flex-flow e flex-wrap).

    Há um polyfill para ele (https://github.com/doctyper/flexie), mas está desatualizado (emula uma versão mais antiga da especificação).

  4. Renato Cerullo 25/07/2013 at 08:03 #

    Parabéns Luiz, ótimo post.

    Bom saber que o CSS e os navegadores estão sendo modificados para facilitarem essas situações.

  5. Bruno 25/07/2013 at 17:38 #

    Obrigado Luiz pelo post.
    Muito bem explicado.

  6. FELIPE 25/07/2013 at 23:37 #

    Ótima explicação Luiz!…com isso o terror dos float e clears podem ter um fim 🙂

  7. Steimntz Machado de Figueiredo 26/07/2013 at 02:18 #

    Ótimo artigo, parabéns, sabe se já existe algo para remediar a falta de compatibilidade (scripts tipo o html5shiv), para que já seja aceitável o uso em alguns casos?

  8. Luiz Corte Real 26/07/2013 at 09:57 #

    Oi, Steimntz. Obrigado!

    Existe um script que faz o Flexbox funcionar até mesmo no IE6 (https://github.com/doctyper/flexie). O problema é que ele ainda não suporta a última versão da especificação, que foi a que eu mostrei aqui no post. Ele só suporta uma versão antiga, em que ainda se usava display: box em vez de display: flex.

  9. Thiago César 26/07/2013 at 10:09 #

    Muito bom, só que o maior problema das novas tags do css3, é que muitas ainda os navegadores não dão suporte.

  10. Irae 04/08/2013 at 13:37 #

    O Firefox é o único browser com flex-box e sem flex-wrap. Seria uma ótima ajuda se todo mundo que entendeu esse post e a importância disso pro design votasse no bug aqui:

    https://bugzilla.mozilla.org/show_bug.cgi?id=702508

    Pra votar num bug da mozilla o procedimento é se colocar no “CC” do bug, você vai receber e-mails periódicos sobre aquele bug. Vale à pena. Faça sua parte pra melhorar a nossa vida =)

  11. Danilo Buzar 24/10/2013 at 15:03 #

    Parabéns pelo ótimo artigo!! Sem dúvida uma grande evolução no CSS!

  12. Alice Mello 22/01/2015 at 12:49 #

    Obrigada pelo ótimo post Luiz. Realmente o Flexbox veio para agilizar e facilitar o desenvolvimento, conferindo maior precisão, menos erros e ajustes.

    Vivaaaa!! Finalmente podemos alinhar conteúdo verticalmente sem cálculos!!

  13. Alice Mello 22/01/2015 at 13:06 #

    Uma dúvida: estou trabalhando em um site em que um elemento “X” deve ficar posicionado no html no topo (próximo ao h1) por questões de SEO. Mas no layout ele deve ser visualizado no footer.

    Posso utilizar a propriedade “order” pra este elemento “X”, com o mesmo número do footer, para que eles possam ambos se sobrepor (o elemento “X” visualizado dentro do footer)?

    Por exemplo:
    header -> order:1
    content -> order: 2
    footer -> order: 3
    elemento “X” -> order: 3

  14. Ícaro Pinto 05/08/2015 at 17:33 #

    Parabéns pelo post, foi muito bem explicado e com ótimos exemplos. Adeus sticky footer! hehe =}

  15. Adilson Calixto 29/08/2016 at 08:36 #

    Amigão show de bola seu post. Eu, particularmente, desconhecia essa nova regra CSS e os benefícios que ela nos proporcionam.

  16. Marcelo de Souza Duarte 23/10/2017 at 22:28 #

    Você é um gênio kkkkk tirou todas as minhas dúvidas excelente trabalho!!!

Deixe uma resposta