View on GitHub

aulas-programacao-web

Materiais de Aula - Programação de Computadores com tecnologias Web

Mobile-first holy grail

📽 Veja esta vídeo-aula no Youtube

Um dos mais famosos problemas de Web Design até pouco tempo era a construção do chamado Holy Grail Layout, ou o “Leiaute Cálice Sagrado”. O termo foi popularizado em 2006 por um artigo muito influente no site A List Apart.

Consiste em criar de forma simples e funcional um leiaute com cabeçalho, rodapé (que deve estar sempre no final da página mesmo com pouco conteúdo) e três colunas, sendo uma de conteúdo principal (que deve aparecer antes na marcação para ter mais peso nos mecanismos de busca) e duas de conteúdo adicional como menus de navegação e propagandas (que devem ter a mesma altura do conteúdo, mesmo quando tenha menos conteúdo).

Isso era bastante difícil de se obter antes da especificação flex.

Nesse passo-a-passo vamos criá-lo com a mentalidade mobile-first, ou seja, primeiro criaremos um design para dispositivos pequenos, depois ajustaremos para dispositivos maiores.

Marcação

Vamos usar tags semânticas para cada elemento. Nossa estrutura básica será, então:

<body>
    <header>Cabeçalho</header>
    <div class="hg-conteudo">
        <main>Conteúdo principal</main>
        <nav>Menu</nav>
        <aside>Conteúdo secundário</aside>
    </div>
    <footer>Rodapé</footer>
</body>

Algumas áreas estarão em destaque para facilitar o seu entendimento, mas não são necessárias para a solução.

Estilização mobile

Vamos iniciar nossa estilização removendo as margens da página.

body {
    margin: 0;
}

Vamos transformar o body em um flex container, com o eixo principal em coluna, de forma que sejam empilhados o cabeçalho, o conteúdo e o rodapé. Definiremos que a altura mínima de body será 100% da viewport visível, usando 100vh.

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-flow: column;
}

Agora, podemos forçar o conteúdo a ocupar todo o espaço disponível.

.hg-conteudo {
    flex: 1;
}

Para nosso design atingir os objetivos para dispositivos mobile, só falta trazer o menu visualmente para antes do conteúdo. Fazemos isso tornando nosso conteúdo um novo container vertical e usando a propriedade order aplicada no flex item nav com o valor -1, indicando para ele voltar uma posição na ordem dos itens.

.hg-conteudo {
    flex: 1;
    display: flex;
    flex-flow: column;
}

.hg-conteudo > nav {
    order: -1;
}

Nosso leiaute já quase está pronto para mobile, porém ainda fica bastante inadequado para telas maiores:

Media query

Vamos criar uma media query para estilizar telas maiores. Digamos que a opção seja por tornar o design do conteúdo horizontal em viewports com pelo menos 768px. Podemos fazer da seguinte forma:

@media (min-width: 768px) {
    .hg-conteudo {
        flex-flow: row;
    }    
}

Vamos fazer o conteúdo principal ocupar todo o espaço disponível:

@media (min-width: 768px) {
    /* ... */

    .hg-conteudo > main {
        flex: 1;
    }
}

E vamos configurar as barras laterais para ocuparem um tamanho base de 200px, de forma inflexível.

@media (min-width: 768px) {
    /* ... */

    .hg-conteudo > aside,
    .hg-conteudo > nav {
        flex: 0 0 200px;
    }
}

Ajustes finais

Falta tratar os casos extremos de viewports muito largas. A maneira mais comum é definir um tamanho máximo para o corpo da página (body) e centralizá-lo usando a técnica das bordas horizontais automáticas (auto).

body {
    margin: 0 auto;
    max-width: 1440px;
    /* ... */
}

Para finalizar, falta garantir que o conteúdo principal seja flexível em ambas as situações, já que ainda não está em pequenas viewports. Podemos somente mover a regra de dentro da media query para fora dela.

.hg-conteudo > main {
    flex: 1;
}

E pronto! 😊

Veja exemplos funcionais sem conteúdo e com conteúdo.

Código da solução final

Disponível aqui.

Marcação (index.html)

<!DOCTYPE html>
<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Holy Grail Layout</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>Cabeçalho</header>
    <div class="hg-conteudo">
        <main>Conteúdo principal</main>
        <nav>Menu</nav>
        <aside>Conteúdo secundário</aside>
    </div>
    <footer>Rodapé</footer>
</body>
</html>

Estilo (style.css)

body {
    margin: 0 auto;
    max-width: 1440px;
    min-height: 100vh;
    display: flex;
    flex-flow: column;
}

.hg-conteudo {
    flex: 1;
    display: flex;
    flex-flow: column;
}

.hg-conteudo > nav {
    order: -1;
}

.hg-conteudo > main {
    flex: 1;
}

@media (min-width: 768px) {
    .hg-conteudo {
        flex-flow: row;
    }

    .hg-conteudo > aside,
    .hg-conteudo > nav {
        flex: 0 0 200px;
    }
}