Tudo sobre Tecnologia da Informação

Dicas de FlexBox Parte 1

O formato da água é algo que varia conforme o recipiente, a água se adapta à garrafa, jarra, copo, etc., da mesma forma, o layout do FlexBox se adapta à tela do dispositivo. Ou seja: O formato do FlexBox se adapta dependendo do container.

O Flexible Box Module trabalha com uma caixa com um tamanho base, mas não exatamente definido. Dentro dessa caixa podemos tem vários itens, e caso usemos coisas como display: block e display: inline-block, pode ser que os itens de dentro não se adaptem caso o container de fora seja redimensionado. Se usarmos o display: flex no container de fora e flex: auto nos itens de dentro, poderemos adaptar os itens de forma melhor pra dispositivos diferentes. O elemento pai é o container (o de fora, que recebe o display: flex) e os elementos filhos são os itens.

Vamos exemplificar com essas divs:


<div class="container">
    <div class="item">A</div>
    <div class="item">B</div>
    <div class="item">C</div>
    <div class="item">D</div>
    <div class="item">E</div>
    <div class="item">F</div>
    <div class="item">G</div>
</div>

Com esse CSS:


div.container {
    background-color: lightsalmon;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
}

Colocando display: flex no container, ele já tentará se adaptar:


div.container {
    background-color: lightsalmon;
    display: flex;
}

E ao colocar flex: auto nos itens, eles já ficarão mais adaptáveis, podendo redimensionar o navegador:


div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    flex: auto;
}

No caso acima, ele ocupa o máximo de espaço disponível, ao diminuir e aumentar o navegador, ele vai se adaptando.

A configuração padrão de direção é row (deitado, em linha, da esquerda pra direita). Podemos definir isso explicitamente com flex-direction: row, isso é o padrão do idioma português, inglês, espanhol e outros mais comuns. Para sentido contrário de leitura (como no idioma árabe), colocaremos flex-direction: row-reverse. Podemos também configurar verticalmente com flex-direction: column, lido de cima pra baixo (o padrão), pra reverter usamos flex-direction: column-reverse.

Ao definir ambos teremos a definição dos eixos (axis), e o eixo principal é o main-axis, começando da esquerda pra direita, e o eixo transversal é o cross-axis, de cima pra baixo, ambos com começo (start) e fim (end). Isso na configuração padrão (row), em outras configurações isso muda, como visto abaixo:

Eixos Flexbox

Deixe o CSS assim:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-direction: row;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    text-align: center;
    padding: 10px;
}

Experimente mudar o flex-direction para row-reserve, column e column-reverse.

A propriedade flex-wrap vai configurar o comportamento da cápsula (o container, que é o pai). Com a opção nowrap ele não quebra os itens ao diminuir o container a um tamanho menor que o limite, ele vai os encolher (o nowrap é o padrão). Lembrando que o tamanho mínimo do bloco de item é o tamanho do conteúdo, portanto, não tem como diminuir mais do que o bloco permite. Ao colocar flex-wrap: wrap ele vai quebrar o item pra baixo (ou melhor, no sentido do cross-axis). Se colocarmos flex-wrap: wrap-reverse, ele vai pra cima (ou melhor, pro sentido inverso ao cross-axis).

Deixe o HTML assim:


<div class="container">
    <div class="item">Exemplo de bloco A</div>
    <div class="item">Exemplo de bloco B</div>
    <div class="item">Exemplo de bloco C</div>
    <div class="item">Exemplo de bloco D</div>
    <div class="item">Exemplo de bloco E</div>
    <div class="item">Exemplo de bloco F</div>
    <div class="item">Exemplo de bloco G</div>
</div>

E o CSS assim:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    text-align: center;
    padding: 10px;
}

Altere o flex-wrap assim, e ele quebrará os itens:


flex-wrap: wrap;

E de forma reversa:


flex-wrap: wrap-reverse;

Faça assim:


flex-direction: row-reverse;
flex-wrap: wrap;

E assim:


flex-direction: row-reverse;
flex-wrap: wrap-reverse;

Temos também a opção flex-flow, que junta numa mesma declaração, o flex-direction e o flex-wrap, dispensando o uso das duas últimas. Dessa forma aqui:


flex-flow: row nowrap;

Inclusive, é recomendado usar o flex-flow, pra simplificar aqui.

Lembrando que o flex: auto só funciona nos filhos (itens). Ele é pra não ter espaço em branco no elemento pai. Por exemplo:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-flow: row nowrap;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    text-align: center;
    padding: 10px;
    flex: auto;
}

Pra ver um efeito interessante, coloque flex-flow: row wrap no código acima, deixando o restante sem alterações. Tente também com flex-flow: row wrap-reverse.

PS: Ao colocar algo como flex-flow: column wrap-reverse ele apenas vai empilhar, mas podemos fazer essa configuração, por exemplo:


div.container {
    background-color: lightsalmon;
    height: 150px;
    display: flex;
    flex-flow: column wrap;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    text-align: center;
    padding: 10px;
    flex: auto;
}

Como visto, o container (o pai) define o comportamento dos filhos, para alinhar os itens em relação ao main-axis (no row, seria da esquerda pra direita), do main-start ao main-end. Ao colocar justify-content: flex-start (que é o padrão), o primeiro item ficará grudado no main-start (no caso do row, à esquerda) e o espaço em branco fica no final. Ao colocar flex-end, ele ficará grudado no main-end (no caso do row, à direita), e o espaço em branco fica antes dos itens. Temos também a propriedade center pra centralizar os itens e o espaço fica dividido entre o main-start e o main-end.

Além disso, temos o space-between que coloca o primeiro item grudado no main-start, o último item grudado no main-end, e os outros terão espaço distribuídos entre eles. Para distribuir espaço entre todos os itens incluindo o primeiro e o último (de forma que não fiquem grudados no main-start e main-end) usamos o space-evenly. Já o space-around pega o espaço do container, divide pela quantidade de itens, e distribui o espaço igualmente pra cada um deles (atenção que entre os itens, tem o espaço do final de um item grudado com o espaço do começo de outro. Veja como ficam cada um deles:

Spaces

Lembrando que ao mudar o direction (como o column nowrap), o main-axis tem outra posição (no caso seria de cima pra baixo) e o espaço. Veja abaixo:

Spaces Column

A mesma regra segue com row-reverse e column-reverse.

Deixe o HTML novamente assim:


<div class="container">
    <div class="item">A</div>
    <div class="item">B</div>
    <div class="item">C</div>
    <div class="item">D</div>
    <div class="item">E</div>
    <div class="item">F</div>
    <div class="item">G</div>
</div>

E o CSS assim:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-flow: row nowrap;
    justify-content: flex-start;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    text-align: center;
    padding: 10px;
}

Depois mude a propriedade pro flex-end, assim:


justify-content: flex-end;

PS: Isso não é a mesma coisa que pôr row-reverse no flex-flow. O flex-end não reverte os itens.

Tente os valores center, space-between, space-evenly e space-around.

Também podemos fazer o alinhamento no cross-axis (no caso do row, é de cima pra baixo). Nesse caso usamos o align-items. Nesse caso, devemos aumentar o container de cima pra baixo (no caso do row). O valor padrão é o stretch, que estica os itens junto com o container, dessa forma aqui:


align-items: stretch;

As outras opções são flex-start, que gruda os itens no cross-start e deixa o espaço do outro lado. O flex-end gruda os itens no cross-end e deixa espaço do outro lado. O center centraliza os itens no meio.

Deixe o CSS assim, definindo o height do container e colocando o align-items:


div.container {
    background-color: lightsalmon;
    height: 300px;
    display: flex;
    flex-flow: row nowrap;
    justify-content: space-around;
    align-items: stretch;
}

Tente mudar as opções para flex-start, flex-end e center.

Vamos fazer um exercício com um item apenas, mas podemos fazer com vários itens também. Nós centralizaremos perfeitamente o item, independente de como redimensionamos a página. Vamos usar a base dos eixos principal (main-axis) e transversal (cross-axis), nos quais usamos para posicionamentos o justify-content e o align-items, respectivamente, com o center em ambos.

Para exemplificar vamos limpar o nosso HTML e CSS totalmente, e vamos usar tags semânticas dessa vez (que é o recomendado). Deixe o HTML assim:


<main>
    <section>
        <h1>Tela de Login</h1>
    </section>
</main>

Com esse CSS:


* {
    margin: 0px;
    padding: 0px;
    box-sizing: border-box;
}

body {
    background-color: lightsalmon;
}

main {
    background-color: lightgreen;
    height: 90vh; /* 90vh ocupa 90% da tela */
    width: 90vw; /* Aqui é vm mesmo */
}

section {
    background-color: lightseagreen;
    height: 200px;
    width: 200px;
}

Para centralizarmos perfeitamente, vamos considerar que o main é o container principal e o section é o item. No CSS do main, apenas adicione isso:


display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;

Isso já alinhará o item, mesmo que nós só tenhamos mexido no CSS do container (main).

PS: Evite usar o body como o container (pai).

Caso deseje colocar a área do container preenchendo 100% da tela, deixe o CSS do main assim:


height: 100vh;
width: 100vw;

Relembrando, ao colocar flex-flow: row wrap, ele vai jogar os itens pro sentido do cross-end do cross-axis (no caso do row, pra baixo) caso o container fique menor do que o espaço dos itens. Mas caso ele aumente pro sentido do cross-end (pra baixo, no caso do row) ele ficará um espaço em branco. Para podermos definir o que fazer nesse espaço, usamos a propriedade align-content, que alinha os elementos no eixo transversal quando eles estão empacotados. Os valores aceitos nele são stretch (padrão, que estica), flex-start (que gruda os elementos no cross-start), flex-end (que gruda os elementos no cross-end), center (que centraliza com espaços iguais entre o cross-start e cross-end), space-between (que gruda os primeiros no cross-start, os últimos no cross-end e os espaços são divididos igualmente entre os items), space-evenly (com espaços iguais entre o cross-start e o cross-end) e space-around (que divide os espaços igualmente, e entre os itens tem um espaço de um e do outro).

Vamos voltar pro primeiro exercício, deixando o HTML assim:


<div class="container">
    <div class="item">A</div>
    <div class="item">B</div>
    <div class="item">C</div>
    <div class="item">D</div>
    <div class="item">E</div>
    <div class="item">F</div>
    <div class="item">G</div>
</div>

Com esse CSS:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-flow: row wrap;
    height: 400px;
    align-items: stretch;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    padding: 40px;
    text-align: center;
}

Por padrão, ele vai esticar, então coloque no container a propriedade align-items: flex-start, aí eles não esticarão e ficarão grudados no cross-start (no caso, em cima). Mas ele continuará quebrando os itens pra baixo.

Deixe o CSS do container assim:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-flow: row wrap;
    height: 400px;
    align-content: flex-start; /* Aqui é content */
}

Coloque mais uns itens no HTML, indo até o M, pra formar três linhas ao quebrar. E deixe o align-content do container assim:


align-content: stretch;

Teste na mesma propriedade, todos as outras opções: flex-end, center, space-between, space-evenly e space-around.

PS: Lembre-se sempre que apenas o container é flex, o que está dentro dele não é flex.

Todo item em Flexbox (que está dentro de um container) tem como padrão o order como 0 (se tivermos 4 itens, todos eles terão a ordem 0). Caso queiramos mudar a ordem dos elementos, podemos modificar também. Isso é útil para, por exemplo, menus, que podem ter uma ordem diferente no PC e no celular.

No mesmo HTML com os itens de A a G, deixe o CSS assim:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-flow: row wrap;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    padding: 20px;
    text-align: center;
}

Em um dos itens, faça um estilo inline assim:


<div class="item" style="order: -3">D</div>

Ao fazer isso, o D acabará vindo primeiro.

Coloque isso em outro elemento:


<div class="item" style="order: 1">F</div>

Aí o F ficará por último, já que o D tem valor -3, o F tem valor 1 e todos os outros são 0, por padrão.

Deixe o HTML assim, com as IDs:


<div class="container">
    <div class="item" id="i1">A</div>
    <div class="item" id="i2">B</div>
    <div class="item" id="i3">C</div>
    <div class="item" id="i4">D</div>
    <div class="item" id="i5">E</div>
    <div class="item" id="i6">F</div>
    <div class="item" id="i7">G</div>
    <div class="item" id="i8">H</div>
    <div class="item" id="i9">I</div>
</div>

E coloque o CSS assim, com todos os itens em ordem aleatória no order:


div#i1 {
    order: 0;
}

div#i2 {
    order: -2;
}

div#i3 {
    order: 4;
}

div#i4 {
    order: 8;
}

div#i5 {
    order: 2;
}

div#i6 {
    order: 0;
}

div#i7 {
    order: -4;
}

div#i8 {
    order: 5;
}

div#i9 {
    order: -2;
}

No caso, o primeiro valor é o menor (no caso, -4), e o último é o maior (no caso, 8). Caso tenha mais de um valor igual (como o 0, que está em dois itens, ele vai colocar o primeiro com esse valor, e depois o segundo e assim por diante).

Temos a propriedade align-self que se aplica a cada um dos itens, os alinhando no sentido do cross-axis (pra baixo, no caso do row). O valor auto herdará a característica de alinhamento configurado no container (pai). O flex-start alinha perto do cross-start, e da mesma forma o flex-end alinha perto do cross-end. O center calcula o meio entre o cross-start e o cross-end. O stretch puxará o item para que ele ocupe todo o espaço do eixo transversal. Isso só será visível ao aumentar o container, dessa forma:

Align Self

Retire todos os orders do CSS, deixando só o bloco dos seletores da DIV, assim:


div.container {
    background-color: lightsalmon;
    display: flex;
    flex-flow: row wrap;
    align-items: flex-start;
    height: 400px;
}

div.item {
    background-color: lightskyblue;
    border: 1px solid black;
    padding: 20px;
    text-align: center;
}

div#i1 {
    
}

div#i2 {
    
}

/* Segue igual até o div#i9 */

Da forma acima, todos ficarão como flex-start porque é o que foi definido no container, e ao deixar a opção align-self como auto, ele ficará dessa mesma forma porque é o container pai que manda.

O align-self é configurado para cada item individualmente:


div#i1 {
    
}

div#i2 {
    align-self: auto; /* Pode ser inherit também, que herda */
}

div#i3 {
    align-self: flex-end;
}

div#i4 {
    
}

div#i5 {
    align-self: center;
}

div#i6 {
    align-self: flex-start;
}

div#i7 {
    align-self: stretch;
}

div#i8 {
    
}

div#i9 {
    
}

Agora tente mudar o align-items do container pra outro valor, por exemplo:


align-items: center;

Podemos também tirar a propriedade acima do container, aí ele assumirá o valor padrão (que é stretch).

Parte 2 da Matéria