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](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/rodape-mf-300x21.png) 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](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/tres-caixas-300x30.png) 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](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/footer-align-items-300x21.png) 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](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/rodape-mf-300x21.png) 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é](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/cabecalho-corpo-rodape-300x142.png) 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é](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/cabecalho-corpo-rodape-espaco-sobrando-300x142.png) 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](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/pacotes-desktop-300x140.png) 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](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/pacotes-mobile-137x300.png) Versão mobile da página de três serviços
![Versão tablet da página de três serviços](assets/o-drama-do-posicionamento-de-elementos-na-tela-e-o-flexbox/pacotes-tablet-241x300.png) 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.