Desenvolvimento Web com HTML, CSS e JavaScript > Display: grid

Display: grid

Mais uma propriedade de posicionamento de elementos! A propriedade display: flex já nos proporcionou grandes vantagens em relação às maneiras que costumávamos manipular posicionamento de elementos, só que encontramos uma certa complicação quando precisamos posicionar elementos de forma bidimensional. Flex é uma ótima ferramenta para quando temos elementos que precisam ser distribuídos de maneira igual e com uma direção bem definida.

<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>Exemplo</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <main class="flex-container">
        <div class="foto"></div>
        <div class="foto"></div>
        <div class="foto"></div>
        <div class="foto"></div>
        <div class="foto"></div>
        <div class="foto"></div>
        <div class="foto"></div>
        <div class="foto"></div>
    </main>
</body>
</html>
.flex-container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-evenly;
}

.foto {
    width: 200px;
    height: 200px;

    border-radius: 5px;

    box-shadow: 3px 3px 3px #000000aa;

    background-color: #ff002b;

    margin-bottom: 1rem;
}

No exemplo mostrado acima o display: flex funciona perfeitamente porque a distribuição é unidirecional, mesmo que exista uma segunda linha por conta do flex-wrap. Mas agora se vamos fazer algo como:

display: flex por conta não consegue lidar o posicionamento bidimensional. Precisamos modificar a estrutura para que os elementos ocupem o espaço de uma linha:

<main>
    <div class="flex-container"> <!-- Primeira linha que contém "apenas" o bloco verde e o container que guarda os elementos vermelhos-->
        <div class="foto foto-destaque-verde"></div> <!-- Bloco verde e primeiro elemento da linha -->
        <div class="flex-container foto-container"> <!-- Container que vai guardar o restante dos eleentos vermelhos e segundo elemento da linha -->
            <div class="foto"></div>
            <div class="foto"></div>
            <div class="foto"></div>
            <div class="foto"></div>
        </div>
    </div>
</main>
.flex-container {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-evenly;
}

.foto-container {
    width: 66%;
}

.foto {
    width: 200px;
    height: 200px;

    border-radius: 5px;

    box-shadow: 3px 3px 3px #000000aa;

    background-color: #ff002b;

    margin-bottom: 1rem;
}

.foto-destaque-verde {
    background-color: #24cc2d;

    height: 400px;
}

Vamos entender o que aconteceu. Como o flex só consegue trabalhar com uma direção, então vamos criar a primeira linha contendo apenas dois elementos: o bloco verde e uma caixa vazia. Nessa caixa vazia, vamos criar nossa segunda linha (deixar a caixa como um flex container) com os 4 blocos vermelhos dentro e vamos limitar o tamanho dessa caixa para que o espaço seja melhor dividido. Já conseguimos perceber com esse exemplo simples que o código já começou a ficar muito complicado. Imagine fazer layouts mais complexos usando essa técnica do diplay: flex. Por conta dessa dificuldade de fazer layouts com uma complexidade bidimensional que surgiu o display: grid.

A idéia do grid é exatamente de não precisar mudar a estrutura do HTML e que toda a parte de posicionamento de layout fique apenas no CSS.

Quando colocamos a propriedade display: grid em um elemento ele se transforma em um grid container da mesma forma que um elemento se torna flex container quando recebe a propriedade display: flex. Depois que definimos um grid container precisamos definir sua estrutura. Por padrão um grid container coloca cada item em uma linha e todos eles ficam em uma coluna:

Para ver melhor as marcações de grid, use a ferramenta de desenvolvimento de seu navegador.

Nós sabemos que nós precisamos de 3 colunas e 2 linhas para conseguir o efeito que queremos. Então vamos usar as propriedades de grid container que vão definir o número de colunas e o número de linhas respectivamente: grid-template-columns: e grid-template-rows. A propriedade de grid permite que nós usemos outra unidade de medida: fr (que significa fraction/ fração).

grid-template-columns

Podemos colocar infinitos valores dentro dessa propriedade, mas cada valor dentro dessa propriedade vai representar uma coluna. Queremos 3 colunas que ocupem igualmente o espaço da página. Vamos usar a nova unidade de medida fr.

.grid-container {
    display: grid;

    grid-template-columns: 1fr 1fr 1fr;
}

No código acima, quando escrevemos 1fr 1fr 1fr estamos dizendo que queremos 3 colunas que ocupem 1 fração do espaço disponível, ou seja, cada coluna vai ter 1/3 do espaço disponível lateralmente.

grid-template-rows

A propriedade que cria as linhas funciona da mesma maneira que a propriedade que cria colunas. Portanto se queremos duas linhas que ocupam o mesmo espaço:

.grid-container {
    display: grid;

    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
}

Só que mesmo depois de ter colocado as duas linhas ainda assim os elementos não ficaram dispostos corretamente. O elemento verde ainda ocupa apenas uma linha e não duas, então precisamos indicar para o navegador:

.foto-destaque-verde {
    background-color: #24cc2d;

    grid-row: span 2;

    height: 400px;
}

A propriedade grid-row é usada apenas nos elementos filhos de um grid container e ela recebe dois valores: linha de início e linha de término. No nosso exemplo usamos o valor span que diz que queremos mesclar linhas e o 2 a quantidade de linhas que vamos mesclar. Quando usamos o span o próximo valor vai ser a quantidade de linhas ou colunas que vamos mesclar, portanto não indicamos qual linha é a linha de término. Quando não colocamos o valor de término o navegador coloca automaticamente span 1 que quer dizer "ocupe apenas 1 linha/coluna".

Agora sim nosso bloco verde está na posição correta em relação aos elementos vermelhos. Usado grid nós não precisamos mudar a estrutura HTML para conseguir o efeito que queríamos no começo e o código ficou muito mais simples de entender também.

Vamos dificultar um pouco nosso exemplo. Vamos trocar a posição do bloco verde para a direita. Vamos usar a propriedade grid-columns para dizer onde que queremos que nosso bloco verde comece e termine, mas apenas colocar essa propriedade no bloco verde não vai ser o suficiênte, precisamos colocar essa propriedade também nos blocos vermelhos e isso já começa a aumentar significativamente a complexidade e dificuldade de manutenção.

Para melhorar essa capacidade de movimentação de elementos dentro do grid, foi criado uma propriedade chamada grid-template-areas, que nomeia cada espaço do grid criado. Vamos pegar nosso layout normal e tentar nomear baseado no que já temos:

.grid-container {
    display: grid;

    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    grid-template-areas:
        "verde vermelho vermelho"
        "verde vermelho vermelho";
}

Então no exemplo acima na primeira linha (o primeiro conjunto de aspas): primeiro espaço: bloco verde, segundo espaço: bloco vermelho, terceiro espaço: bloco vermelho; segunda linha (o segundo conjunto de aspas): primeiro espaço: bloco verde, segundo espaço: bloco vermelho, terceiro espaço: bloco vermelho. Agora só precisamos dizer quem é o bloco vermelho e quem é o bloco verde:

.grid-container {
    display: grid;

    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    grid-template-areas:
        "verde vermelho vermelho"
        "verde vermelho vermelho";
}

.foto-destaque-verde {
    background-color: #24cc2d;

    /*
    grid-column: 4;
    grid-row: span 2;
    Esse código não é mais necessário por conta do grid-template-area
    */
    grid-area: verde;

    height: 400px;
}

Antes de colocar nome nos outros blocos, vamos nomear apenas o bloco verde e depois testar. Podemos observar que os elementos continuaram na mesma posição. Agora vamos mudar a posição do bloco verde:

.grid-container {
    display: grid;

    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    grid-template-areas:
        "vermelho vermelho verde"
        "vermelho vermelho verde";
}

.foto-destaque-verde {
    background-color: #24cc2d;

    /*
    grid-column: 4;
    grid-row: span 2;
    Esse código não é mais necessário por conta do grid-template-area
    */
    grid-area: verde;

    height: 400px;
}

Mesmo sem nomear os blocos vermelhos com a propriedade grid-area chegamos no nosso objetivo. Neste caso só queremos mudar a posição do bloco verde e queremos que o restante se adapte conforme necessário, então podemos trocar os valores de vermelho para apenas um ponto "." .

.grid-container {
    display: grid;

    grid-template-columns: 1fr 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    grid-template-areas:
        ". . verde"
        ". . verde";
}

.foto-destaque-verde {
    background-color: #24cc2d;

    /*
    grid-column: 4;
    grid-row: span 2;
    Esse código não é mais necessário por conta do grid-template-area
    */
    grid-area: verde;

    height: 400px;
}

Não podemos deixar espaços vazios que o browser não vai entender, precisamos deixar alguma coisa para indicar o espaço do grid.