Skip to content
This page has been auto-translated and may contain errors.View in English

Posicionamento e z-index

docs.scrimba.com

Você quer um pequeno emblema "Promoção" sentado no canto superior direito de uma foto de produto. Você quer um cabeçalho que fica no lugar enquanto a página rola embaixo dele. Você quer um diálogo que flutua acima de tudo mais na página. O espaçamento CSS ordinário não pode fazer nenhum disso, porque ele apenas organiza elementos um após o outro. Posicionamento é o conjunto de ferramentas que permite que um elemento saia desse arranjo ordinário e sente-se exatamente onde você o aponta, até mesmo em cima de algo mais.

A propriedade position

Todo elemento começa com posicionamento estático, que é uma forma simples de dizer "sente-se no seu lugar normal no fluxo da página". Esse é o padrão, e na maioria das vezes é o que você quer. Posicionamento começa a importar quando você quer que um elemento se mova para longe desse lugar normal.

O primeiro passo é position: relative. Ele move um elemento de onde normalmente ficaria, usando quatro propriedades de deslocamento: top, right, bottom e left. A coisa-chave é que o elemento mantém seu espaço original. Nada mais se move para preencher a lacuna.

css
.badge {
  position: relative;
  top: 10px;                 /* empurrado 10px para baixo de seu lugar normal */
  left: 20px;                /* empurrado 20px para a direita de seu lugar normal */
}

Esses deslocamentos top e left não fazem absolutamente nada em um elemento static. Eles só começam a funcionar quando você dá ao elemento um position diferente de static.

Tudo na página começa com position: static, seu lugar no fluxo normal de cima para baixo que você conheceu em display e fluxo do documento. Elementos estáticos ignoram completamente as propriedades de deslocamento, então definir top ou left neles não faz nada. Isso pega as pessoas desprevenidas: elas adicionam top: 20px, não veem mudança, e assumem que o valor está errado quando o problema real é um position ausente.

A opção gentil é posicionamento relativo. Ele desloca um elemento de seu lugar normal pelos deslocamentos que você dá, deixando seu espaço original reservado no fluxo. Os vizinhos não se movem para fechar a lacuna, então um elemento posicionado relativamente pode acabar se sobrepondo ao que está ao lado dele.

css
.tooltip {
  position: relative;
  top: -4px;                 /* levantado 4px para cima de onde ficaria */
  left: 8px;                 /* empurrado 8px para a direita */
  /* o espaço reservado fica no lugar; apenas a pintura se move */
}

Deslocamentos como top e left só se aplicam quando um elemento está posicionado. Por conta própria, relative é uma pequena ferramenta de ajuste. Seu trabalho mais importante é estabelecer um ponto de referência para filhos posicionados absolutamente, que é a próxima seção.

A propriedade position tem cinco valores, e o modelo inteiro é mais fácil de entender se você dividi-lo por uma pergunta: o elemento ainda ocupa espaço no fluxo normal? static (o valor inicial) e relative sim; absolute e fixed não; sticky é um híbrido que alterna entre os dois comportamentos conforme você rola.

relative é o afastamento mais leve de static. O elemento é diagramado no fluxo normal exatamente como se fosse estático, seu espaço é reservado naquele local original, e então é visualmente deslocado daquela posição por top / right / bottom / left. Como o espaço reservado nunca se move, deslocar um elemento relativo apenas muda onde ele é pintado, não onde o layout acha que ele está, então pode se sobrepor ao conteúdo adjacente sem mover nada ao redor.

css
.list-item {
  position: relative;
  top: 6px;                  /* pintado 6px mais baixo; seu espaço no fluxo fica no lugar */
}

Dois pontos de precisão. Primeiro, os deslocamentos se resolvem contra a posição normal do elemento em si, não contra nenhum ancestral: top: 10px significa "10px abaixo de onde você estaria de outra forma", não "10px do topo do contêiner". Segundo, se você definir deslocamentos opostos (top e bottom, ou left e right) em um elemento relativo, o lado inicial vence em um modo de escrita da esquerda para direita, de cima para baixo, então top substitui bottom e left substitui right. A razão muito mais comum para recorrer a relative não é o ajuste em tudo; é estabelecer um bloco de contenção para descendentes absolute, que a próxima seção desenvolve.

JunoA propriedade position Tudo começa como static, significando que fica em seu lugar normal. Mude para position: relative e você pode ajustá-lo com top, right, bottom e left, enquanto seu espaço original fica reservado. Essas propriedades de deslocamento não fazem nada até o elemento estar posicionado, então se top parece ser ignorado, verifique se você definiu um position primeiro.
JunoA propriedade positionstatic é o padrão e ignora deslocamentos completamente, é por isso que top: 20px em um elemento simples não faz nada. relative ajusta o elemento de seu lugar normal mantendo seu espaço reservado, então vizinhos nunca se deslocam para preencher a lacuna. O ajuste é útil, mas a verdadeira razão pela qual você recorre a relative é ancorar filhos posicionados absolutamente, chegando a seguir.
JunoA propriedade position Classifique os cinco valores pela questão de se eles mantêm seu espaço no fluxo: static e relative mantêm, absolute e fixed não mantêm, sticky alterna entre os dois. relative deslocamentos se resolvem contra a posição normal do elemento em si, não seu contêiner, e deslocamentos opostos deixam o lado inicial vencer. Você usará relative muito mais como uma âncora para filhos absolutos do que como uma ferramenta de ajuste.

Posicionamento absoluto

position: absolute é um movimento maior. Ele remove o elemento completamente do fluxo normal, então ele não ocupa mais nenhum espaço, e os elementos ao redor dele se fecham como se ele nunca tivesse existido. Então você o coloca usando os mesmos deslocamentos top, right, bottom e left.

A questão é: deslocado de quê? Um elemento absoluto se posiciona contra seu ancestral posicionado mais próximo, significando o pai mais próximo que tem um position próprio (qualquer coisa que não seja static). É por isso que o padrão emblema-em-um-cartão sempre vem em um par: você torna o cartão relative para que se torne o ponto de referência, depois torna o emblema absolute para que possa ficar no canto do cartão.

css
.card {
  position: relative;        /* se torna o ponto de referência */
}

.card .badge {
  position: absolute;
  top: 8px;                  /* 8px do topo do cartão */
  right: 8px;                /* 8px da direita do cartão */
}

Sem position: relative no cartão, o emblema procuraria mais acima na página por um ancestral posicionado e pousaria em algum lugar que você não pretendia.

Posicionamento absoluto remove um elemento do fluxo normal completamente. Ele não ocupa nenhum espaço, seus antigos vizinhos se desabam juntos como se ele não existisse, e você então o coloca com as propriedades de deslocamento. Os deslocamentos são medidos contra o bloco de contenção do elemento: a borda de preenchimento de seu ancestral mais próximo que é posicionado.

Essa regra de "ancestral posicionado mais próximo" é o coração do padrão que você usará constantemente. Defina o pai como position: relative (que por si só não muda nada visualmente), e ele se torna o quadro de referência para qualquer filho absoluto dentro dele.

css
.dropdown {
  position: relative;        /* o quadro de referência, sem mudança visual */
}

.dropdown .menu {
  position: absolute;
  top: 100%;                 /* diretamente abaixo do gatilho */
  left: 0;                   /* alinhado à sua borda esquerda */
  width: 200px;
}

Se nenhum ancestral for posicionado, o elemento absoluto retorna ao bloco de contenção inicial, aproximadamente a janela de visualização, e normalmente acaba no canto superior esquerdo da página. Um elemento absoluto fora de lugar é quase sempre um position: relative ausente no pai que você pretendia ancorá-lo.

Um elemento posicionado absolutamente é retirado do fluxo normal: ele não gera uma caixa no fluxo, não contribui para a altura de seu pai, e seus irmãos se diagramam como se ele não existisse. Ele é então posicionado em relação ao seu bloco de contenção, e acertar as regras do bloco de contenção é o que separa "funciona" de "funciona e eu sei por quê".

Para um elemento absolute, o bloco de contenção é a caixa de preenchimento de seu ancestral mais próximo cujo position não é static. Se vários ancestrais se qualificarem, o mais próximo vence. Se nenhum se qualificar, o bloco de contenção é o bloco de contenção inicial, um retângulo do tamanho da janela de visualização ancorado ao documento, é por isso que um elemento absoluto não ancorado se move para o canto superior esquerdo da página.

css
.field {
  position: relative;        /* estabelece o bloco de contenção */
  padding: 12px;
}

.field .clear-button {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);   /* centro vertical contra .field */
  /* deslocamentos medem a partir da caixa de preenchimento de .field, não sua caixa de conteúdo */
}

Uma regra que surpreende as pessoas e importa para a imagem avançada do bloco de contenção: um transform, filter ou will-change em um ancestral também estabelece um bloco de contenção para descendentes absolutos, até mesmo sem um position definido. Então um filho absoluto pode de repente reparentar seus deslocamentos em um avó transformado que você não pensou como uma âncora. Quando um elemento absoluto se posiciona contra o elemento "errado", verifique se há um transform perdido na árvore, não apenas um position ausente.

JunoPosicionamento absolutoposition: absolute puxa um elemento para fora do fluxo, então ele não ocupa espaço e vizinhos se fecham atrás dele. Ele então se posiciona contra seu pai mais próximo que tem um position definido. É por isso que o truque emblema-em-um-cartão é um par: relative no cartão, absolute no emblema, para que o emblema pouse no canto do cartão e não em algum lugar aleatório.
JunoPosicionamento absoluto Um elemento absoluto sai completamente do fluxo e se posiciona contra seu bloco de contenção, o ancestral mais próximo que é posicionado. O padrão cotidiano é position: relative no pai, que não muda nada visualmente mas se torna o quadro de referência, depois absolute no filho. Quando um elemento absoluto pousa no canto superior esquerdo da página, significa que nenhum ancestral foi posicionado e ele retornou à janela de visualização.
JunoPosicionamento absoluto O bloco de contenção para um elemento absoluto é a caixa de preenchimento de seu ancestral não-static mais próximo, ou a janela de visualização se nenhum existir. A armadilha é que transform, filter e will-change também criam um bloco de contenção sem nenhum position visível, então um filho pode ancorar a um avó transformado que você esqueceu. Quando deslocamentos pousam contra o elemento errado, escaneie a árvore por um transform perdido, não apenas um position ausente.

Fixed e sticky

position: fixed fixa um elemento na tela em si. Ele sai do fluxo como absolute faz, mas em vez de ancorar a um pai, ele ancora na janela de visualização, a janela visível. Uma vez fixo, ele fica no mesmo lugar na tela mesmo enquanto você rola a página. É assim que uma barra de navegação ou um botão "voltar ao topo" fica visível o tempo todo.

css
.site-header {
  position: fixed;
  top: 0;                    /* fixado no topo da janela */
  left: 0;
  right: 0;                  /* esticado em toda a largura */
}

position: sticky é o meio-termo amigável. Um elemento sticky rola junto com a página normalmente, até atingir um limite que você define, e depois fica no lugar. Um título de seção que rola com seu conteúdo e depois se prende ao topo da tela é o exemplo clássico.

css
.section-title {
  position: sticky;
  top: 0;                    /* fica fixo quando chega à borda superior */
}

Posicionamento fixo funciona como absoluto, exceto que o bloco de contenção é sempre a janela de visualização em vez de um ancestral. Um elemento fixo é removido do fluxo e então fixado a um ponto fixo na tela, então ele não se move quando a página rola. Isso o torna a ferramenta para chrome persistente: uma barra superior, um banner de cookie, um botão de ação flutuante.

css
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 64px;
  /* conteúdo abaixo precisa de padding-top: 64px então não fica oculto */
}

Esse comentário aponta para a principal cilada. Como um cabeçalho fixo sai do fluxo, ele se sobrepõe ao conteúdo abaixo dele, então você geralmente adiciona margem ou preenchimento correspondente ao corpo da página para empurrar o conteúdo para longe.

Posicionamento sticky é um híbrido: o elemento se comporta como relative enquanto está em vista, depois alterna para comportamento tipo fixed uma vez que atinge o limite que você define com top, bottom, left ou right. Um elemento sticky sem um desses deslocamentos de limite nunca fica fixo, porque não tem nenhuma linha na qual ficar.

css
.section-title {
  position: sticky;
  top: 16px;                 /* se mantém 16px do topo uma vez que chega lá */
}

fixed é absolute com uma diferença que importa: seu bloco de contenção é a janela de visualização, então é fixado na tela e não rola com o documento. Isso o torna certo para UI persistente, com duas ressalvas. Ele se sobrepõe ao conteúdo em fluxo (reserve espaço com preenchimento no corpo), e a mesma regra transform-estabelece-um-bloco-de-contenção do absoluto se aplica aqui também, então um transform ancestral prenderá um elemento fixed dentro daquele ancestral e ele rolará com ele. Um elemento "fixo" que misteriosamente rola quase sempre está sentado sob um ancestral transformado.

Posicionamento sticky é o mais condicional dos cinco. Um elemento sticky é diagramado no fluxo normal, mantendo seu espaço, e fica lá até seu ancestral de rolagem mais próximo rolá-lo para o limite que você declarou; a partir desse ponto ele é mantido no limite até seu bloco de contenção rolar para além, momento em que é liberado. Dois requisitos são os que as pessoas perdem:

css
.toc {
  position: sticky;
  top: 24px;                 /* o limite; sem um deslocamento nunca fica fixo */
  /* fica fixo apenas enquanto o pai de .toc ainda está visível */
}

Primeiro, sticky precisa de um deslocamento de limite (top, bottom, left ou right); sem nenhum, não há linha para ficar e se comporta como um elemento relativo simples. Segundo, pode apenas ficar fixo dentro dos limites de seu pai, então se o pai é curto, o elemento se liberta assim que o pai sai de vista. E uma falha silenciosa frequente: um ancestral com overflow: hidden, scroll ou auto se torna o contêiner de rolagem do elemento sticky, então um item sticky pode calmamente não fazer nada porque está confinado a um contêiner que não é o que você esperava rolar.

JunoFixed e stickyposition: fixed fixa um elemento na tela, então ele fica no lugar enquanto você rola, é assim que um cabeçalho ou um botão voltar-ao-topo fica por perto. position: sticky é o meio-termo: ele rola com a página, depois fica fixo uma vez que atinge um limite como top: 0. Fixed também se sobrepõe ao que está abaixo dele, então deixe um pouco de espaço para o conteúdo que ele cobre.
JunoFixed e stickyfixed é absolute ancorado à janela de visualização, então fica na tela enquanto a página rola, ao custo de se sobrepor ao conteúdo em fluxo a menos que você preencha em volta. sticky se comporta como relative até atingir seu deslocamento de limite, depois se mantém no lugar. Um elemento sticky sem top ou bottom definido nunca fica fixo, porque não há linha para ele ficar.
JunoFixed e stickyfixed fixa à janela de visualização, mas um transform ancestral o prende dentro daquele ancestral e ele rolará, então um elemento "fixo" rolando geralmente tem um pai transformado. sticky precisa de duas coisas que as pessoas esquecem: um deslocamento de limite e um pai suficientemente alto, desde que ele se liberta o momento que seu pai sai de vista. E observe um ancestral com overflow definido, que se torna o contêiner de rolagem e pode silenciosamente impedir sticky de funcionar.

Empilhamento com z-index

Quando elementos se sobrepõem, algo tem que decidir qual fica em cima. É isso que z-index controla. Um z-index maior desenha na frente de um menor, então um dropdown com z-index: 10 cobre um cartão com z-index: 1.

Há uma regra que pega todos de surpresa: z-index só funciona em elementos posicionados. Se um elemento ainda for static, seu z-index é ignorado. Então você quase sempre define um position e um z-index juntos.

css
.modal {
  position: fixed;
  z-index: 100;              /* fica acima do conteúdo normal da página */
}

.modal-overlay {
  position: fixed;
  z-index: 99;               /* a sobreposição escura, um passo atrás do modal */
}

Você raramente precisa de números enormes aqui. Uma escala pequena e organizada como 1, 10, 100 é mais fácil de raciocinar do que uma página cheia de 9999s lutando uns contra os outros.

Quando dois elementos se sobrepõem, z-index define sua ordem de frente para trás: números maiores são pintados em cima de menores. A cilada é que z-index só afeta elementos posicionados. Dê a um elemento static um z-index e nada acontece, então z-index e position quase sempre viajam juntos.

css
.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
}

.tooltip {
  position: absolute;
  z-index: 50;               /* acima de cartões, abaixo do modal */
}

O hábito que vale a pena formar cedo é uma escala pequena e deliberada em vez de números crescentes. Quando você se vê escrevendo z-index: 9999 para vencer algo, o verdadeiro conserto é geralmente um plano para seus valores de empilhamento, não um número maior. O próximo nível explica por que um número maior às vezes perde mesmo assim.

z-index ordena elementos posicionados sobrepostos ao longo do eixo z, números maiores na frente, e é ignorado em elementos static. A parte que transforma um bug de cinco minutos em uma hora é o contexto de empilhamento: um grupo autossuficiente dentro do qual valores de z-index são comparados, e que se empilha como uma unidade única contra seus irmãos.

Um contexto de empilhamento forma sempre que um elemento é posicionado com um z-index diferente de auto, mas também a partir de opacity abaixo de 1, qualquer transform, filter, will-change, isolation: isolate, e alguns outros. Crucialmente, z-index é apenas comparado dentro do mesmo contexto de empilhamento. Uma vez que um elemento senta dentro de um contexto, seu z-index não pode levantá-lo acima de nada fora daquele contexto, não importa quão grande seja o número.

css
.card {
  opacity: 0.98;             /* isso sozinho cria um contexto de empilhamento */
}

.card .badge {
  position: absolute;
  z-index: 9999;             /* preso dentro do contexto de .card */
}

.sibling-panel {
  position: relative;
  z-index: 2;                /* ainda assim pinta acima de .badge */
}

Aqui .badge em 9999 perde para .sibling-panel em 2, porque .badge está confinado ao contexto que a opacity de .card criou, e esse contexto inteiro se classifica abaixo de .sibling-panel. Quando um z-index alto se recusa a vencer, a resposta quase nunca é um número mais alto; é um ancestral que silenciosamente abriu um contexto de empilhamento (frequentemente uma opacity ou um transform), e o conserto é elevar ou isolar aquele ancestral.

Um ponto de fechamento sobre julgamento: posicionamento e z-index são para sobreposições, emblemas e chrome fixo, as coisas que precisam sair do fluxo ou ficar em cima. Eles são a ferramenta errada para organizar a estrutura principal de uma página. Para linhas, colunas e grades que devem responder ao seu conteúdo e à janela de visualização, recorra a flexbox ou o fluxo orientado pelo modelo de caixa no modelo de caixa, e guarde posicionamento para a camada que flutua acima deles.

JunoEmpilhamento com z-index Quando elementos se sobrepõem, z-index decide qual fica na frente: um número maior vence. Só funciona em elementos posicionados, então um z-index em um elemento static simples é ignorado, e você define position e z-index juntos. Mantenha os números pequenos e organizados, como 1, 10, 100, em vez de uma pilha de 9999s.
JunoEmpilhamento com z-indexz-index define ordem de frente para trás para elementos posicionados, e não faz nada em estáticos, então viaja junto com position. Recorra a uma escala pequena deliberada em vez de números crescentes; se você está escrevendo 9999 para vencer, você precisa de um plano, não de um valor maior. E às vezes um valor maior perde mesmo assim, que o aprofundamento em contextos de empilhamento explica.
JunoEmpilhamento com z-indexz-index é comparado apenas dentro de um contexto de empilhamento, e coisas como opacity abaixo de 1, transform e filter silenciosamente criam um. É por isso que um filho em z-index: 9999 ainda pode perder para um elemento fora em 2: está preso no contexto de seu ancestral. Quando um número alto não vai vencer, procure o ancestral que abriu um contexto e levante-o, e guarde posicionamento para sobreposições em vez de layout de página.