Dominar o layout da web sem dominar CSS é tão viável quanto aprender a nadar em terra firme. Mas ao contrário da natação - que, uma vez dominada, é uma habilidade que permanece com você por toda a vida - dominar o CSS é um processo que nunca termina, pois o próprio CSS está em constante evolução.
O desafio é agravado pelas diferenças na implementação e suporte de CSS em diferentes navegadores (e até mesmo em diferentes versões do mesmo navegador), bem como diferentes taxas de adoção de recomendações de CSS. Por mais de uma década, web designers e desenvolvedores têm lutado com explosões esporádicas e inconsistentes de Recursos CSS3 sendo suportados em cada nova versão do navegador.
Mas seja como for, dominar CSS é uma necessidade absoluta para qualquer sólido web designer ou desenvolvedor . Este artigo irá guiá-lo através de alguns princípios fundamentais de layout CSS, das técnicas clássicas do CSS2 às abordagens de layout mais recentes no CSS3.
NOTA: Todos os exemplos de código neste artigo usam HTML5 elementos e Sass sintaxe. O código de trabalho completo pode ser clonado de https://github.com/laureanoarcanio/css-layout-examples .
Uma das melhores maneiras de aprender uma tecnologia é ter um caso de uso específico que você está tentando oferecer suporte ou um problema específico que você está tentando resolver. Para isso, vamos nos concentrar em um caso de uso com um conjunto específico de requisitos.
Nosso caso de uso consiste em um layout de aplicativo da Web com algum comportamento dinâmico. Ele terá elementos fixos na página, como cabeçalho, rodapé, menu de navegação e sub-navegação, bem como uma seção de conteúdo rolável. Aqui estão os requisitos de layout específicos:
Para começar, aqui está a marcação HTML5 que usaremos com nosso exemplo de implementação usando CSS clássico:
z-index
Em CSS2, podemos obter os elementos fixos na página (cabeçalho, rodapé, etc.) empregando um modelo de layout posicionado que usa posicionamento fixo.
Além disso, usaremos o z-index
Propriedade CSS para garantir que nossos elementos fixos permaneçam 'sobre' o outro conteúdo da página. O z-index
propriedade especifica a ordem de empilhamento de um elemento, onde um elemento com maior ordem de empilhamento está sempre “no topo de” um elemento com uma ordem de empilhamento inferior. Observe que z-index
propriedade só funciona com posicionado elementos Para nosso exemplo, usaremos arbitrariamente um width
valor de 20 (que é maior que o padrão) para garantir que nossos elementos fixos permaneçam visualmente na vanguarda.
Além disso, definiremos o #header, #footer { position: fixed; width: 100%; z-index: 20; } #header { top: 0; height: 5em; } #footer { bottom: 0; height: 3em; }
para 100% que instrui o navegador a usar todo o espaço disponível horizontalmente para o elemento.
#nav
OK, então esse é o cabeçalho e o rodapé. Mas e quanto ao #subnav
e #nav
?
Para o #subnav
e top
, usaremos uma técnica um pouco mais sofisticada conhecida como Expansão CSS , que pode ser usado ao posicionar elementos como fixo (ou seja, em uma posição fixa na página) ou como absoluto (ou seja, em uma posição especificada em relação ao seu mais próximo posicionado ancestral ou ao bloco que o contém).
A expansão vertical é alcançada definindo tanto o bottom
e left
propriedades de um elemento para um valor fixo, então o elemento se expandirá verticalmente para usar o espaço vertical restante de acordo. Basicamente, o que você está fazendo é amarrar a parte superior do elemento a uma distância específica da parte superior da página e a parte inferior do elemento a uma distância específica da parte inferior da página, para que o elemento se expanda para preencher todo o espaço vertical entre esses dois pontos.
Da mesma forma, a expansão horizontal é alcançada definindo tanto o right
e #nav, #subnav { position: fixed; top: 6em; /* leave 1em margin below header */ bottom: 4em; /* leave 1em margin above footer */ z-index: 20; } #nav { left: 0; width: 5em; } #subnav { left: 6em; /* leave 1em margin to right of nav */ width: 13em; }
propriedades de um elemento para um valor fixo, então o elemento se expandirá horizontalmente para usar o espaço horizontal restante de acordo.
Para nosso caso de uso, precisamos usar a expansão vertical.
margin
A área de conteúdo rolável principal pode simplesmente contar com o posicionamento padrão (estático), por meio do qual os elementos são renderizados na ordem em que aparecem no fluxo do documento. Como tudo o mais em nossa página está em uma posição fixa, esse elemento é o único que está no fluxo do documento. Como resultado, tudo o que precisamos fazer para posicioná-lo corretamente é especificar seu #main { margin: 6em 0 4em 20em; }
propriedade para evitar qualquer sobreposição com o cabeçalho, rodapé e nav / subnav fixos:
5em
E com isso, atendemos aos requisitos básicos de layout de nosso caso de uso usando CSS2, mas ainda precisamos atender aos requisitos adicionais de funcionalidade dinâmica.
Os requisitos declararam que nosso menu de navegação mostraria por padrão apenas ícones, mas também poderia ser expandido para exibir texto (e poderia ser recolhido para mostrar novamente apenas ícones).
Vamos começar simplesmente adicionando #nav { left: 0; width: 5em; &.expanded { /* Sass notation */ width: 10em; } }
à largura do menu de navegação quando ele é expandido. Faremos isso criando uma classe CSS “expandida” que podemos adicionar ou remover dinamicamente do elemento do menu de navegação:
$('.layout-classic #nav').on('click', 'li.nav-toggle', function() { $('#nav’').toggleClass('expanded'); });
Aqui está um exemplo do código JavaScript (neste exemplo, usamos jQuery) que pode ser usado para alternar dinamicamente o menu de navegação entre os modos expandido e recolhido, com base no clique do usuário no ícone de alternância de navegação:
#subnav { left: 6em; width: 13em; &.expanded { left: 11em; /* move it on over */ } } #main { margin: 6em 0 4em 20; z-index: 10; &.expanded { margin-left: 25em; /* move it on over */ } }
E com isso, nosso menu de navegação agora pode ser expandido ou reduzido dinamicamente. Ótimo.
Bem, até certo ponto ótimo, mas não exatamente. Embora o menu de navegação agora possa expandir e contrair, ele não funciona bem com o resto da página. O menu de navegação expandido agora se sobrepõe ao subnav, o que definitivamente não é o comportamento desejado.
Isso revela uma das principais limitações do CSS2; ou seja, há caminho muito que precisa ser codificado com valores de posição fixa. Como resultado, para os outros elementos da página que precisam ser reposicionados para acomodar o menu de navegação expandido, precisamos definir adicional Classes CSS “expandidas” com ainda mais valores de posição fixa.
$('.layout-classic #nav').on('click', 'li.nav-toggle', function() { $('#nav, #subnav, #main').toggleClass('expanded'); });
Em seguida, precisamos estender nosso código JavaScript para adicionar o ajuste dinâmico desses outros elementos também quando o botão de navegação é clicado pelo usuário:
display: none
OK, isso funciona melhor.
Agora vamos abordar o requisito de ter algumas páginas que ocultam o submenu de navegação. Especificamente, queremos que o submenu de navegação fique oculto quando o usuário clicar no ícone “usuários” na área de navegação principal.
Portanto, primeiro, criaremos uma nova classe “oculta” que se aplica .hidden { display: none; }
:
#subnav
E, novamente, usaremos JavaScript (jQuery) para aplicar a classe CSS “oculta” ao $('#nav.fa-user').on('click', function() { $('#subnav').toggleClass('hidden'); });
elemento quando o usuário clica no ícone do usuário:
#subnav
Com essa adição, o #subnav
elemento fica devidamente escondido quando o usuário clica no ícone 'usuários', mas o espaço que ocupou permanece sem uso , em vez de os outros elementos se expandirem para usar o espaço desocupado pelo #subnav
elemento.
Para obter o comportamento desejado quando ocultamos o main
elemento, vamos empregar um dos menos conhecidos, mas altamente úteis, seletores de CSS conhecido como o seletor irmão adjacente .
o seletor irmão adjacente permite que você especifique dois elementos, selecionando apenas as instâncias do segundo elemento que seguem imediatamente o primeiro elemento especificado.
Por exemplo, o seguinte selecionará apenas os elementos com o ID subnav
este imediatamente siga um elemento com o ID #subnav + #main { margin-left: 20em; }
:
#main
O snippet CSS acima define a margem esquerda de 20em
para #subnav
se e apenas se ele segue imediatamente um #nav
exibido.
No entanto, se expanded
é expandido (o que faz com que a classe #main
seja adicionada a #main
também, com base em nosso código anterior), movemos a margem esquerda de #subnav + #main.expanded { margin-left: 25em; }
a 25em.
#subnav
E, se #main
está oculto, movemos a margem esquerda de #nav
todo o caminho até 6em para estar bem próximo a #subnav.hidden + #main { margin-left: 6em; }
:
#subnav
(Observação: uma desvantagem de usar o seletor irmão adjacente é que ele nos força a sempre ter #subnav
presente no DOM, independentemente de ele estar sendo exibido ou não.)
Finalmente, se #nav
está oculto e #main
é expandido, definimos a margem esquerda de 11em
em #subnav.hidden + #main.expanded { margin-left: 11em; }
:
calc()
Isso nos permite conectar as coisas sem nenhum código JavaScript pesado, mas também podemos ver como esse código pode se tornar complicado se adicionarmos mais elementos à página. Vemos mais uma vez que, com CSS2, grande quantidade de codificação de valores de posição é necessário para fazer as coisas funcionarem corretamente.
CSS3 oferece funcionalidade significativamente aprimorada e técnicas de layout que o tornam muito mais fácil de usar e muito menos dependente de valores codificados permanentemente. CSS3 é inerentemente projetado para suportar um comportamento mais dinâmico e é, nesse sentido, mais “programável”. Vamos examinar alguns desses novos recursos relacionados ao nosso caso de uso.
calc()
FunçãoO novo CSS3 calc()
função pode ser usado para calcular valores de propriedade CSS dinamicamente (observe, porém, que suporte varia entre navegadores). A expressão fornecida para +
função pode ser qualquer expressão simples combinando os operadores aritméticos básicos (-
, *
, /
, calc()
) usando regras de precedência de operador padrão.
Uso do #nav, #subnav { position: fixed; height: calc(100% - 10em); /* replaces */ z-index: 20; }
A função pode ajudar a evitar muitos dos hardcoding de valores exigidos pelo CSS2. Em nosso caso, isso nos permite alcançar a expansão CSS de forma mais dinâmica. Por exemplo:
height
Com o acima calc()
especificação usando top:6em
função, obtemos o mesmo resultado que obtivemos em CSS2 com bottom:4em
e top
, mas de uma forma muito mais flexível e adaptável, e sem a necessidade de codificar bottom
e display
valores de posição.
Flexbox é um novo layout introduzido em CSS3 ( o suporte varia entre os navegadores ) O layout flexbox torna mais simples organizar os elementos em uma página de uma forma que se comporta de forma previsível em diferentes tamanhos de tela, resoluções e dispositivos. Portanto, é particularmente útil no contexto de web design responsivo .
Os principais recursos incluem:
O Flexbox apresenta seu próprio conjunto exclusivo de termos e conceitos. Alguns deles incluem:
flex
propriedade definida como inline-flex
ou flex-directio
que serve como o elemento de contêiner para itens flexíveis.flex-wrap
n que designa o eixo principal ao longo da qual os itens flexíveis são dispostos. o eixo transversal é então o eixo perpendicular ao eixo principal.main size
propriedade.cross size
e .layout-flexbox { display: flex; flex-direction: column; }
, que especificam os tamanhos do eixo principal e do eixo cruzado do flex container, respectivamente.OK, então com essa breve introdução, aqui está a marcação alternativa que podemos usar se estivermos usando um layout flexbox:
content-area
Para nosso caso de uso de exemplo, nosso layout principal (cabeçalho, conteúdo, rodapé) é vertical, então vamos configurar nosso flexbox para usar um layout de coluna:
#nav
Embora nosso layout principal seja vertical, os elementos em nossa área de conteúdo (nav, subnav, main) são dispostos horizontalmente. Cada flex container pode definir apenas uma direção (ou seja, seu layout deve ser horizontal ou vertical). Portanto, quando o layout exige mais do que isso (um caso comum é o layout de um aplicativo), podemos aninhar vários contêineres um dentro do outro, cada um com um layout direcional diferente.
É por isso que adicionamos um contêiner extra (que chamei de #subnav
) embalagem #main
, flex
e flex: ;
. Dessa forma, o layout geral pode ser vertical, enquanto o conteúdo da área de conteúdo pode ser disposto horizontalmente.
Agora, para posicionar nossos flex items vamos usar a propriedade flex-grow
isso é uma abreviação de flex-shrink
. Essas três propriedades flex são as que determinam como nossos flex items distribuem qualquer espaço livre restante entre eles na direção do fluxo, da seguinte maneira:
Configuração #header { flex: 0 0 5em; } #footer { flex: 0 0 3em; }
e flex-grow
ambos a zero significa que o tamanho do item é fixo e não aumentará ou diminuirá para acomodar a existência de mais ou menos espaço livre disponível. Isso é o que fazemos para nosso cabeçalho e rodapé, pois eles têm um tamanho fixo:
flex-shrink
Para que um item ocupe todo o espaço livre disponível, defina seu flex-basis
e auto
valores para 1 e defina seus content-area
valor para display: flex
. Isso é o que fazemos para a área de conteúdo, pois queremos que ela ocupe todo o espaço livre disponível.
E como afirmamos antes, queremos os itens dentro de flex-direction: row;
para dispor na direção da linha, então adicionaremos #nav
; e #subnav
. Isso torna o content-area um novo flex container para content-area
, .content-area { display: flex; flex-direction: row; flex: 1 1 auto; /* take up all available space */ margin: 1em 0; min-height: 0; /* fixes FF issue with minimum height */ }
e `#main.
Então, aqui está o que acabamos com o CSS para #nav
:
#subnav
Na área de conteúdo, ambos flex
e #nav { flex: 0 0 5em; margin-right: 1em; overflow-y: auto; } #subnav { flex: 0 0 13em; overflow-y: auto; margin-right: 1em; }
têm tamanhos fixos, portanto, definimos apenas overflow-y: hidden
propriedade em conformidade:
#main
(Observe que adicionei #main { flex: 1 1 auto; overflow-y: auto; }
a essas especificações CSS para superar o conteúdo que excede e transborda a altura do contêiner. Na verdade, o Chrome não precisa disso, mas o FireFox, sim.)
layout-flexbox
ocupará o resto do espaço livre:
layout-classic
Tudo isso parece bom, então agora vamos adicionar nosso comportamento dinâmico a isso e ver como funciona.
O JavaScript é idêntico ao que tínhamos antes (exceto aqui, a classe de contêiner de elemento CSS que estamos especificando é $('.layout-flexbox #nav’).on('click', 'li.nav-toggle', function() { $('#nav').toggleClass('expanded'); });
enquanto antes era expanded
):
#nav { flex: 0 0 5em; /* collapsed size */ margin-right: 1em; overflow-y: auto; &.expanded { flex: 0 0 10em; /* expanded size */ } }
Adicionamos o display
classe para CSS da seguinte maneira:
fr
E voila!
Observe que, desta vez, não precisamos deixar outros itens saberem sobre a mudança de largura, uma vez que o layout do flexbox cuida de tudo isso para nós.
A única coisa que resta então é esconder o sub nav. E adivinha? Isso “simplesmente funciona” também, sem quaisquer alterações adicionais, usando o mesmo código JavaScript de antes. O Flexbox conhece o espaço livre e faz nosso layout funcionar automaticamente sem nenhum código extra. Muito legal.
O Flexbox também oferece algumas maneiras interessantes de centralizar os elementos verticais e horizontais. Percebemos aqui o quão importante é para uma linguagem de apresentação incluir a noção de espaço livre e como nosso código pode se tornar escalável usando esses tipos de técnicas. Por outro lado, os conceitos e notações aqui podem demorar um pouco mais para dominar do que o CSS clássico.
Se o Layout do Flexbox está na vanguarda do CSS3, então pode-se dizer que o Layout de grade está na vanguarda. A especificação W3C ainda está em um estado de rascunho e ainda tem suporte de navegador bastante limitado . (É ativado no Chrome por meio do sinalizador 'recursos experimentais da plataforma da Web' em chrome: // flags )
Dito isso, eu pessoalmente não considero este projeto revolucionário. Em vez disso, como o Princípios de design HTML5 Estado: “Quando uma prática já está difundida entre os autores, considere adotá-la em vez de proibi-la ou inventar algo novo.”
Conseqüentemente, as grades baseadas em marcação têm sido usadas há muito tempo, então agora o Layout de Grade CSS está realmente seguindo o mesmo paradigma, oferecendo todos os seus benefícios e muito mais na camada de apresentação sem requisitos de marcação.
A ideia geral é ter um layout de grade predefinido, fixo ou flexível onde podemos posicionar nossos elementos. Assim como o flexbox, ele também trabalha com o princípio do espaço livre e permite definir “direções” verticais e horizontais no mesmo elemento, o que traz vantagens no tamanho e flexibilidade do código.
O layout de grade apresenta 2 tipos de grades; nomeadamente, explícito e implícito . Para simplificar, vamos nos concentrar em grades explícitas nisso.
Como o flexbox, o layout de grade apresenta seu próprio conjunto exclusivo de termos e conceitos. Alguns deles incluem:
.layout-grid { display: grid; grid-template-columns: auto 0 auto 1em 1fr; grid-template-rows: 5em 1em 1fr 1em 3em; }
propriedade definida como “grade” ou “grade embutida” na qual os elementos contidos são dispostos posicionando e alinhando a uma grade predefinida (modo explícito). A grade é um conjunto de intersecção de linhas de grade horizontais e verticais que dividem o espaço do contêiner da grade em células de grade. Existem dois conjuntos de linhas de grade; um para definir as colunas e um ortogonal a ele para definir as linhas.display: grid;
unidade, que representa uma fração do espaço livre no contêiner da grade.
Esta é a marcação alternativa que podemos usar se estivermos usando um layout de grade:
grid-template-columns
Observe que com este layout, fazemos não precisamos de um wrapper extra para a área de conteúdo, como fizemos para o flexbox, uma vez que este tipo de layout nos permite definir a designação do espaço do elemento em ambas as direções no mesmo contêiner de grade.
Vamos mergulhar no CSS agora:
grid-template-rows
Definimos grid-template-columns: auto 0 auto 1em 1fr;
em nosso contêiner. O auto
e #nav
cada uma das propriedades é especificada como uma lista de espaços entre as faixas da grade. Em outras palavras, esses valores não são a posição das linhas da grade; em vez disso, eles representam o quantidade de espaço entre duas faixas.
Observe que as unidades de medida podem ser especificadas como:
Portanto, com #subnav
nós teremos:
auto
largura (#subnav
espaço)1em
está no nível do elemento, pois pode estar presente ou não, evitamos assim ter calha dupla)1fr
largura (#main
espaço)auto
#nav
para #subnav
(ocupará todo o espaço restante)Aqui, usamos intensamente o grid-template-rows: 5em 1em 1fr 1em 3em;
valor para a trilha, o que nos permite ter colunas dinâmicas onde a posição e o tamanho das linhas são definidos pelo seu conteúdo máximo. (Portanto, precisaremos especificar os tamanhos dos elementos #header
e #footer
, o que faremos em breve.)
Da mesma forma, para as linhas de linha, temos 1em
que define nosso #header { grid-column: 1 / 6; grid-row: 1 / 2; } #footer { grid-column: 1 / 6; grid-row: 5 / 6; } #main { grid-column: 5 / 6; grid-row: 3 / 4; overflow-y: auto; }
e grid-column
a ser corrigido e todos os elementos entre para usar o espaço livre restante ao usar grid-row
calhas.
Agora vamos ver como colocamos os elementos reais a serem posicionados em nossa grade definida:
grid-column-start
Isso especifica que queremos que nosso cabeçalho fique entre as linhas de grade 1 e 6 (largura total) e entre as linhas de grade 1 e 2 para nossas linhas. O mesmo para o rodapé, mas entre as duas últimas linhas (em vez das duas primeiras). E a área principal é configurada apropriadamente para o espaço que deve ocupar.
Observe que grid-column-end
e grid-row-start
propriedades são uma forma abreviada de especificar grid-row-end
/ #nav
e #subnav
/ #nav
, respectivamente.
OK, então de volta ao #subnav
e #nav { width: 5em; grid-column: 1 / 2; grid-row: 3 / 4; &.expanded { width: 10em; } } #subnav { grid-column: 3 / 4; grid-row: 3 / 4; width: 13em; /* track has gutter of 0, so add margin here */ margin-left: 1em; }
. Como colocamos anteriormente #nav
e #subnav
na trilha com valores automáticos, precisamos especificar a largura desses elementos (o mesmo para o modo expandido, apenas alteramos sua largura e o Layout de grade cuida do resto).
Portanto, agora podemos alternar nosso
|_+_|e também ocultar / remover o
|_+_|e tudo funciona perfeitamente! O layout de grade também nos permite usar aliases para nossas linhas, então, eventualmente, mudar as grades não vai quebrar o código, pois é mapeado para um nome e não uma linha de grade. Definitivamente, estou ansioso para que isso seja mais amplamente suportado por mais navegadores.
Mesmo com técnicas clássicas de CSS, há muito mais que pode ser realizado do que muitos desenvolvedores da web percebem ou aproveitam. Dito isso, muito disso pode ser entediante e pode envolver a codificação de valores repetidamente em uma folha de estilo.
O CSS3 começou a fornecer técnicas de layout muito mais sofisticadas e flexíveis que são significativamente mais fáceis de programar e evitam muito do tédio das especificações CSS anteriores.
Dominar essas técnicas e paradigmas - tanto para CSS2 quanto para CSS3 - é essencial para aproveitar tudo o que o CSS tem a oferecer a fim de otimizar a experiência do usuário e a qualidade do seu código. Este artigo realmente representa a ponta do iceberg de tudo o que há para aprender e tudo o que pode ser realizado com o poder e a flexibilidade do CSS. Têm-no!
Relacionado: * Explorando SMACSS: Arquitetura Escalável e Modular para CSS *