📽 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.
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.
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:
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;
}
}
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.
Disponível aqui.
<!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>
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;
}
}