O “posicionamento estático” refere-se ao fluxo padrão da página que estivemos trabalhando até o momento. Os esquemas de layout Modelo de Caixa CSS, Floats, e Flexbox operam nesse fluxo "estático", mas esse não é o único esquema de posicionamento disponível no CSS.

Diagram: comparison of static, relative, absolute, and fixed positioning schemes

Os outros três tipos de posicionamento são “relativo”, “absoluto”, e “fixo”. Cada um deles permite que você posicione manualmente elementos utilizando coordenadas específicas, ao contrário das opções semânticas do flexbox e floats. Ao invés de dizer “Coloque essa caixa no centro do seu contêiner”, o posicionamento avançado permite dizer coisas como “Coloque aquela caixa 20 píxels acima e 50 pixels à direita da origem de seu elemento pai.”

A grande maioria dos elementos em uma página web deveriam ser dispostos de acordo com o fluxo estático da página. Estes outros esquemas de posicionamento entram em jogo quando você quer fazer coisas mais avançadas como ajustar a posição de um elemento particular ou animar um componente da interface do usuário sem bagunçar os elementos ao redor.

Este capítulo é dividido em duas partes. Começaremos examinando posicionamento relativo, absoluto e fixo isoladamente, e então aplicaremos tudo que aprendemos em um bonito menu dropdown.

Setup

Comece criando um novo projeto no Atom chamado advanced-positioning e um novo arquivo chamado schemes.html com o seguinte cógigo:

<!DOCTYPE html>
<html>
  <head>
    <meta charset='UTF-8'/>
    <title>Pposicionamento é Fácil!</title>
    <link href='styles.css' rel='stylesheet'/>
  </head>
  <body>

    <div class='container'>
      <div class='example relative'>
        <div class='item'><img src='images/static.svg' /></div>
        <div class='item item-relative'><img src='images/relative.svg' /></div>
        <div class='item'><img src='images/static.svg' /></div>
      </div>
    </div>
    
    <div class='container'>
      <div class='example absolute'>
        <div class='item'><img src='images/static.svg' /></div>
        <div class='item item-absolute'><img src='images/absolute.svg' /></div>
        <div class='item'><img src='images/static.svg' /></div>
      </div>
    </div>

    <div class='container'>
      <div class='example fixed'>
        <div class='item'><img src='images/static.svg' /></div>
        <div class='item item-fixed'><img src='images/fixed.svg' /></div>
        <div class='item'><img src='images/static.svg' /></div>
      </div>
    </div>

  </body>
</html>

Nós temos três exemplos para trabalhar, todos com exatamente a mesma estrutura HTML. Alterar o comportamento de posicionamento dentro de cada um tem efeitos dramaticamente diferentes.

Screenshot: files in the example project

Essa página depende de algumas imagens para deixar nosso exemplo um pouco mais claro. Mantenha a pasta pai das images no seu projeto ao descompactar os arquivos para o seu projeto, como mostrado acima. Assegure-se de criar um styles.css e preenchê-lo com a base necessária de estilos, assim como:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  height: 1200px;
}

.container {
  display: flex;
  justify-content: center;
}

.example {
  display: flex;
  justify-content: space-around;
  
  width: 800px;
  margin: 50px 0;
  background-color: #D6E9FE;
}

.item img {
  display: block;
}

Nada novo aqui, apenas algumas técnicas já conhecidas de flexbox para criar uma grade de itens. A única coisa estranha é a propriedade height explícita no elemento <body>, que nos permitirá rolar a página para demonstrar diferentes comportamentos de posicionamento.

Web page with static, relative, absolute, and fixed boxes in different colors

Elementos Posicionais

A propriedade position do CSS permite alterar o esquema de posicionamento de um elemento particular. Seu valor padrão, como você pode imaginar, é static. Quando a propriedade position de um elemento não tem o valor static, é chamado de "elemento posicionado". Elementos posicionados são o tema desse capítulo inteiro.

Diagram: relative, absolute, and fixed elements denoted as positioned elements

É possível misturar e combinar diferentes esquemas de posicionamento. Novamente, a maior parte da sua página web deve ser estaticamente posicionada, mas é comum encontrar elementos relativamente e absolutamente posicionados dentro de outros elementos que são parte do fluxo normal da página.

Posicionamento Relativo

O “posicionamento relativo” move os elementos ao redor de forma relativa à onde apareceriam normalmente no fluxo estático da página. Isso é útil quando precisamos empurrar caixas ao redor do fluxo padrão quando ele está um pouco fora do lugar.

Diagram: relatively positioned box offset from the upper left corner of its static position

Vamos transformar o elemento .item-relative no schemes.html em um elemento posicionado relativamente. Adicione a seguinte regra ao styles.css:

.item-relative {
  position: relative;
  top: 30px;
  left: 30px;
}

A linha position: relative; faz dele um elemento posicionado, e as propriedades top e left permitem definir o quanto ele fica deslocado de sua posição estática. É quase como definir uma coordenada (x, y) para o elemento.

Web page with a relatively positioned element

Posicionamento relativo funciona de forma similar às margens, com uma diferença bem importante: nenhum dos elementos ao redor ou os elementos pais são afetados pelos valores de top e left. Todo o resto vai ser renderizado como se o .item-relative estivesse na sua posição original. Imagine que o deslocamento está sendo aplicado depois que o navegador terminou de carregar a página.

As medidas das propriedades top e left da são aplicadas a partir da direção direita e esquerda da caixa original, respectivamente. Podemos também fazer o deslocamento relativo com relação aos lados das propriedades bottom e right.

Diagram: top, left, bottom, and right offsets of a relatively positioned element

Por exemplo, o código a seguir vai arrastar a caixa na direção oposta:

.item-relative {
  position: relative;
  bottom: 30px;
  right: 30px;
}

Essas propriedades também aceitam valores negativos, o que significa que existem duas formas de especificar o mesmo deslocamento. Podemos utilizar top: -30px; no lugar de bottom: 30px; na declaração anterior.

Posicionamento absoluto

“Posicionamento absoluto” é quase igual ao posicionamento relativo, exceto que o deslocamento relativo é aplicado em relação a janela do navegador, ao invés da posição original do elemento. Já que não tem mais nenhuma relação com o fluxo estático da página, considere isso como a forma mais manual de posicionar um elemento.

Diagram: absolutely positioned element offset from the top-left of the browser window

Vamos dar uma olhada em como fica adicionando a seguinte regra na nossa folha:

.item-absolute {
  position: absolute;
  top: 10px;
  left: 10px;
}

Nossa estrutura HTML é exatamente igual ao exemplo anterior, no entanto isso fará com que imagem roxa fique posicionada no canto superior-esquerdo da janela do navegador. Você também pode tentar definir um valor bottom ou right para ter uma ideia melhor do que está acontecendo.

Web page with an absolutely positioned element

O outro efeito interessante do posicionamento absoluto é que ele remove completamente um elemento do fluxo normal da página. Isso é fácil de ver com os elementos alinhados à esquerda, então vamos mudar temporariamente a propriedade justify-content na nossa regra .example:

.example {
  display: flex;
  justify-content: flex-start;  /* Mude isso */
  /* ... */
}

No nosso exemplo de posicionamento relativo (a primeira linha), ainda tem um espaço onde o elemento posicionado costumava ficar, mas com o posicionamento absoluto, esse espaço some! É como se o .item-absolute nunca tivesse existido para o seu elemento pai e os elementos ao redor dele. Mude novamente a propriedade justify-content para space-around antes de continuar.

Web page highlighting the empty space left by an absolutely positioned element

Este comportamento não é tão útil na maioria das vezes por que isso que significa que tudo na nossa página precisa estar com um posicionamento absoluto—sendo assim, teríamos sobreposições de elementos estáticos sobre elementos absolutos. Então, por que a propriedade absolute existe?

Posicionamento (relativamente) absoluto

Posicionamento absoluto é muito prático quando ele é relativo a algum outro elemento que está no fluxo estático da página. Felizmente, existe uma forma de mudar as coordenadas do sistema de um elemento de posicionamento absoluto.

Diagram: absolute element positioned relative to a parent positioned element

Coordenadas para elementos absolutos são sempre relativas ao posicionamento do elemento contêiner mais próximo, que é um elemento posicionado. Só volta a ser relativo ao navegador quando nenhum dos seus predecessores são posicionados. Então, se mudarmos o elemento pai de .item-absolute para posicionamento relativo, ele deve aparecer no canto superior-esquerdo daquele elemento ao invés da janela do navegador.

.absolute {
  position: relative;
}

A div .absolute está posta no fluxo normal da página, e nós podemos movê-la manualmente ao redor do .item-absolute da forma como quisermos. Isso é ótimo, porque se quisermos alterar o fluxo normal do contêiner, vamos dizer para um layout mobile, qualquer elemento com posicionamento absoluto irá se mover automativamente.

Web page with an absolutely positioned element inside another element that is relatively positioned

Perceba que nós não especificamos nenhuma coordenada de deslocamento para a classe .absolute. Estamos utilizando posicionamento relativo com um único propósito de deixar nosso elemento absoluto encaixar de novo no fluxo normal da página. É assim que combinamos posicionamento absoluto com estático.

Posicionamento fixo

“Posicionamento fixo” tem muito em comum com posicionamento absoluto: é bem manual, o elemento é tirado do fluxo normal da página, e o sistema de coordenadas é relativo a toda a janela do navegador. E diferença é que elementos fixos não rolam junto com o resto da página.

Diagram: fixed element positioned relative to the browser window, but with scrolling disabled

Vamos lá, vamos mudar nosso terceiro exemplo para utilizar posicionamento fixo:

.item-fixed {
  position: fixed;
  bottom: 0;
  right: 0;
}

Isso irá colocar a imagem vermelha no canto inferior-direito da janela. Tente rolar a página, e você irá descobrir que ele não se move com o resto dos elementos da página, enquanto a imagem roxa com o posicionamento absoluto sim.

Isso irá permitir que você crie barras de navegação que sempre estarão apresentadas na janela como aqueles baners chatíssimos que nunca somem.

Posicionamento de elementos para animações

Isso está um pouco fora do escopo, já que esse tutorial é sobre HTML e CSS, não JavaScript. No entanto, animações são um dos primeiros usos para posicionamento relativo e absoluto, então vamos dar uma olhadinha nisso para animar um dos nosso elementos.

Esses esquemas avanaçados de posicionamento permitem que o JavaScript movimente os elementos pela página enquanto evitamos qualquer tipo de interação com os elementos ao redor. Por exemplo, vamos copiar e colar o código abaixo no nosso schemes.html depois do terceiro elemento .container. O elemento <script> deve ser a última coisa dentro do <body>.

<script>
  var left = 0;

  function frame() {
    var element = document.querySelector('.item-relative');
    left += 2;
    element.style.left = left + 'px';
    if (left >= 300) {
      clearInterval(id)
    }
  }

  var id = setInterval(frame, 10)
</script>

Esse código em JavaScript cria uma animação simples que fica atualizando a propriedade left do .item-relative. Quando você atualizar a página, deve ver a imagem em azul flutuando para o canto direito do contêiner.

Web page showing simple animation of a relatively positioned element

Esse é um exemplo bem rudimentar, mas com sorte verá como é aplicável para animações bonitinhas. Se você tentasse alcançar o mesmo efeito manipulando as propriedades de margem ou o preenchimento, você poderia mover sem querer as caixas posicionadas estaticamente e/ou também o elemento .exemple.

Elementos posicionados para menus

Certo, essas são todas as técnicas. Vamos fazer alguma coisa avançado com elas! O resto desse capítulo é para aplicar nossas novas habilidades criando um menu bem lindão de navegação com uma interação de rolagem para os links. Vamos contruir essa página desde o começo.

Web page with a dropdown menu

Posicionamento fixos vão permitir que o menu fique na parte superior da página, e o posicionamento relativo vai nos dar uma âncora para o posicionamento absoluto da rolagem (dropdown). Também falaremos sobre as melhores práticas para menu de navegação e ver alguns usos práticos para pseudo-classes, falamos sobre iso em Seletores CSS.

Para começar vamos precisar de uma página nova, chamada menu.html que tem um cabeçalho e um menu na parte superior:

<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta charset='UTF-8'/>
    <title>Fantárdigo!</title>
    <link href='menu.css' rel='stylesheet'/>
  </head>
  <body>

    <div class='header'>
      <div class='logo'><img src='images/awesome-logo.svg'/></div>
      <ul class='menu'>
        <li class='dropdown'><span>Recursos ▾</span></li>
        <li><a href='#'>Blog</a></li>
        <li><a href='#'>Assinar</a></li>
        <li><a href='#'>Sobre</a></li>
      </ul>
    </div>

  </body>
</html>

Menus de navegação devem ser quase sempre marcados como uma lista <ul> ao invés de um monte de elementos <div>. Essas semânticas tornam a navegação na sua página muito mais acessível para mecanismos de busca. Perceba também como estamos preparando nosso menu dropdown adicionando um atributo class no primeiro ítem <li> da lista. O <span> irá permitir diferenciar o rótulo revelado no submenu.

Próximo, precisamos de uma nova folha de estilo chamada menu.css que fará nosso .header parecer um pouco mais como um cabeçalho de verdade, entre outras coisas:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  height: 1200px;
  font-size: 18px;
  font-family: sans-serif;
  color: #5D6063;
}

a:link,
a:visited {
  color: #5D6063;
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

.header {
  position: fixed;
  display: flex;
  justify-content: space-between;
  
  width: 100%;
  padding: 50px;
  background: #D6E9FE;
}

Isso tudo deve ser familiar pra você, mas note o posicionamento fixed do .header, que irá manter nosso menu de navegação sobre qualquer conteúdo que tivermos na página.

Web page with a menu made out of block <li> elements (no positioning)

Itens de menu em linha

Apesar da marcação como uma lista não ordenada, o menu de navegação da maioria das páginas web não se parece como uma lista. Podemos arrumar isso transformando a lista de itens em caixas em linha ao invés de caixas em bloco, através da propriedade display. Adicione o código abaixo no menu.css:

.menu {
  margin-top: 15px;
}

.menu > li {
  display: inline;
  margin-right: 50px;
}

.menu > li:last-of-type {
  margin-right: 0;
}

Precisamos utilizar seletores filhos aqui ao invés de seletores descendentes porque nós só queremos selecionar o elemento <li> que está dentro do .menu. Isso se faz importante uma vez que adicionamos nosso submenu, que tem seu próprio elemento <li> que não queremos estilizar com essa regra. Este trecho também adiciona margens em todos os nossos itens, mas é remove do último <li> ao utilizar a pseudo-classe :last-of-type. Essa é uma técnica bem comum para criar margens entre os itens.

Web page with a menu made out of inline <li> elements (no positioning)

Nosso submenu está se parecendo como o nosso menu, exceto por todas as coisas que estão aninhadas dentro da lista de item. Altere o elemento .menu para casar com o que está abaixo, garantindo que a lista .features-menu esteja encapsulada no primeiro <li> do elemento .menu.

<ul class='menu'>
  <li class='dropdown'><span>Recursos &#9662;</span>
    <ul class='features-menu'>           <!-- Início do submenu -->
      <li><a href='#'>Harder</a></li>
      <li><a href='#'>Better</a></li>
      <li><a href='#'>Faster</a></li>
      <li><a href='#'>Stronger</a></li>
    </ul>                                <!-- Fim do submenu -->
  </li>
  <li><a href='#'>Blog</a></li>          <!-- Isso continua o mesmo -->
  <li><a href='#'>Assinar</a></li>
  <li><a href='#'>Sobre</a></li>
</ul>

Isso irá providenciar muitas informações para mecanismos de pesquisa. Permite que o Google veja que todos esses novos itens estão associados com um rótulo Recursos e que ele s formam uma seção isolada da nossa página web. Você deve sempre marcar menus de navegação complexos como esse com esse tipo de estrutura.

Quanto a CSS, vamos lidar com a interatividade da parte da rolagem depois. Agora vamos só fazer o submenu se parecer do jeito que queremos. Adicione um estilo simples para que possamos ver a caixa que estamos tentando posicionar:

.features-menu {
  display: flex;
  flex-direction: column;
  background: #B2D6FF;
  border-radius: 5px;
  padding-top: 60px;
}

.features-menu li {
  list-style: none;
  border-bottom: 1px solid #FFF;

  padding: 0 40px 10px 20px;
  margin: 10px;
}

.features-menu li:last-of-type {
  border-bottom: none;
}

O submenu em si já está corretamente estilizado, mas está sendo apresentado no lugar errado, e está modificando todo o resto do nosso menu. Isso deveria ser esperado porque ele ainda está posicionado estaticamente, o que significa que ainda está se comportando como seu pai e os elementos ao redor dele.

Web page with an unstyled submenu (HTML-only)

Para criar o layout desejado, precisamos chamar nossas novas habilidades de posicionamento com CSS.

Submenus (relativamente) absolutos

Nós queremos que nossos outros ítens do menu principal sejam apresentados como estavam antes de adicionarmos o submenu, como se o submenu nem estivesse ali. Pera aí…isso é exatamente o comportamento de elementos com posicionamento absoluto. Vamos ver como isso vai ser. Adicione as linhas abaixo a regra .feature-menu:

.features-menu {
  display: flex;
  flex-direction: column;
  background: #B2D6FF;
  border-radius: 5px;
  padding-top: 60px;

  position: absolute;      /* Adicione isso */
  top: -25px;
  left: -30px;
}

Perfeito! Agora o submenu não é mais parte do fluxo estático da página, assim os ítens do menu principal voltaram ao normal. No entanto, o submenu está aparecendo por baixo do rótulo Recursos—não no canto da janela do navegador. Que coincidência…nós acabamos de aprender como fazer isso!

O submenu está declarado como um item <li class='dropdown'>. Se transformarmos isso em um elemento posicionado isso deve mudar o sistema de coordenadas utilizada pelo posicionamento absoluto do .features-menu:

.dropdown {
  position: relative;
}

Beleza, próximo problema. Nosso submenu está no lado direito, mas agora ele está cobrindo o rótudo dos Recursos.

Web page with a absolutely positioned submenu inside a relative element

Z-Index

Não tínhamos lidado com problemas de “profundidade” antes. Até agora, todo nossos elementos HTML eram renderizados acima ou abaixo um dos outros, é a forma mais intuitiva. Mas, agora que estamos trabalhando com configurações mais avançadas, depende do navegador determinar quais elementos aparecem um sobre o outro sem escondê-los.

A propriedade z-index vai permitir o controle da profundidade dos elementos na página. Se você imaginar a sua tela (do computador, smartphone, etc.) como um espaço 3D, valores negativos do z-index vão mais para o fundo da página, e valores positivos vem mais para a superfície da página.

Diagram: positive z-index coming out of the page and negative z-index going into the page

Em outras palavras, os elementos de .features-menu precisam ter valores menores de z-index que o rótulo Recursos. O valor padrão do z-index é 0, então vamos deixar ambos com valores maiores que isso. Felizmente nós encapsulamos o rótulo Recursos em um <span>, permitindo a estilização dele via seletore filhos, da seguinte forma:

.dropdown > span {
  z-index: 2;
  position: relative;  /* Isso é importante! */
  cursor: pointer;
}

.features-menu {
  /* ... */
  z-index: 1;
}

O rótulo Recursos deve aparecer agora em cima do submenu. Preste atenção na linha position: relative;. Isso é preciso, por que somente elementos posicionados são afetados pela propriedade z-index. Isso é fácil de esquecer, então crie uma nota mental para a próxima vez que você tiver problemas de profundidade e as regras CSS não tiverem nenhum efeito.

Web page showing submenu after adding a positive z-index

Utilizamos uma propriedade chamada cursor para fazer parecer como se fosse um link quando o usuário passar com o ponteiro do mouse por cima do rótulo. Você pode ler um pouco mais sobre essa propriedade na Mozilla Developer Network.

Pseudo-Classes para menus rolantes

Tudo certo! Nosso submenu está pronto! Nossa tarefa final é escondê-lo até que o usuário passe o mouse sobre ele. Lembra da pseudo-classe :hover do capítulo Seletores CSS? Nós podemo utilizá-la para adicionar uma interação de rolagem (dropdown) no submenu.

Primeiro, precisamos mudar nossa regra .features-menu para aparecer somente quando o usuário pousar o mouse sobre ele adicionando o seletor descendente :hover. Modifique o seletor .features-menu para ficar como abaixo:

.dropdown:hover .features-menu {    /* Isso costumava ser o `.features-menu` */
  display: flex;                    /* Deixe todo o resto como está */
  flex-direction: column;
  background: #B2D6FF;
  /* ... */
}

Depois, precisamos inicialmente esconder o submenu utilizando a propriedade display. Adicione essa nova regra à folha menu.css:

.features-menu {                    /* Adicione isso como uma nova regra */
  display: none;
}

Configurando o display como none vai fazer o elemento desaperecer completamente. Ao sobreescrever aquele valor com flex na regra :hover, estaremos efetivamente dizendo ao navegador para mostrar o .features-menu de novo. Essa é uma maneira inteligente de combinar seletores descendentes e pseudo-classes que permita esconder ou mostrar um elemento de forma condicional.

Resumo

Neste capítulo, demos uma olhada em quatro novos esquemas de layout CSS:

  • Relativo
  • Absoluto
  • Relativamente absoluto
  • Fixo

Utilizamos o posicionamento relativo para ajustar a posição de um elemento sem afetar as caixas ao redor. O posicionamento absoluto tirou o elemento do fluxo estático da página e o colocou em uma posição relativa a janela do navegador, enquanto o relativamente absoluto nos permite colocar ele novamente no fluxo estático da página. E finalmente, o posicionamento fixo nos permite ficar um elemento em uma posição e não rolar ela junto com o resto da página.

Diagram: comparison of relative, absolute, relatively absolute, and fixed positioning schemes

Utilizamos essas novas técnicas de posicionamento para criar um menu de navegação bem sofisticado. Se você achou complicado, é por que ele é! Mas não se preocupe, você não precisa se sentir pressionado para memorizar a HTML e a CSS por trás do nosso menu. Seu objetivo deve ser ter a habilidade de usar esse exemplo como referência daqui três meses e entender tudo o que é tudo isso de position: relative; e position: absolute; e o que essas declarações estão fazendo.

Esse menu também é um exemplo de como uma marcação HTML inicial pode tornar nossa vida mais fácil. Primeiro, criamos a estrutura semântica que queríamos. Depois, escrevemos algumas regras CSS para colocar as caixas exatamente onde queríamos. Sempre que você se deparar com um modelo que parece complicado e você não souber como começar, essa é uma boa forma de abordar o problema.

Ainda temos um problema com o nosso menu: ele não foi construído pensando em dispositivos móveis. Smartphones e tablets não tem uma forma de pousar o ponteiro sobre uma parte da página, e nosso layout não se ajusta quando a janela é menor do que 960 pixels. Essa página precisa de um pouco da mágica do JavaScript (ou uma CSS realmente avançada), então vamos deixar isso para outro tutorial. Mas, vamos poder resolver o último problema com um design responsivo no próximo capítulo.