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;
}
}