Posicionamento da âncora

Ao colocar uma dica ou um menu suspenso, geralmente você quer posicioná-lo em relação a outro elemento na página. Embora existam maneiras de usar o posicionamento absoluto para conseguir esse efeito, requisitos mais complexos historicamente recorrem ao posicionamento de itens usando JavaScript.

O posicionamento de âncora CSS oferece uma maneira de posicionar declarativamente um elemento em relação a outro.

Elementos de tethering

Para transformar um elemento em âncora, atribua a ele um valor anchor-name de qualquer string que comece com dois traços. Esse é o identificador que o elemento posicionado vai usar para encontrar a âncora. É útil dar a ele um nome descritivo. Você pode até mesmo dar a um elemento vários nomes de âncora, se ele for usado como uma âncora de maneiras diferentes.

É preciso definir algumas propriedades no elemento posicionado para que ele possa ser fixado. Primeiro, você precisa extrair o elemento do fluxo do documento para que ele flutue, definindo position: absolute ou position: fixed.

Em seguida, defina a âncora a que você quer vincular, definindo position-anchor como o nome da âncora que você definiu nela.

Por fim, você precisará definir como posicionar a âncora. Você vai saber mais sobre position-area mais adiante neste módulo.

#anchor {
   anchor-name: --my-anchor;
}

#positionedElement {
     position: absolute;
     position-anchor: --my-anchor;
     position-area: end;
}

Conexões implícitas

Os popovers são ainda mais simples de vincular. Quando você abre um popover usando um botão com um popovertarget ou definindo um source com showPopover({source}), o popover já tem uma âncora implícita definida. Como um popover já está flutuando com position: fixed por padrão, para posicionar um popover, basta definir a posição.

#anchor{}

#positionedElement {
  position-area: end;
  margin: unset;
}

Definir o escopo das possíveis âncoras

Você pode implementar o posicionamento de âncora como parte de um componente para usar um padrão, como um menu suspenso, em vários lugares. Se você estiver usando o mesmo anchor-name várias vezes, como garantir que cada elemento posicionado encontre a âncora correta?

As soluções em JavaScript envolvem adicionar IDs exclusivos a cada âncora e fazer referência a ela no elemento posicionado. Isso se torna complicado, e o CSS tem uma solução mais simples com anchor-scope.

A propriedade anchor-scope define quais nomes de âncora serão correspondidos apenas entre um elemento e seus descendentes. Ele aceita uma lista de um ou mais nomes de âncora ou a palavra-chave all para limitar o escopo de todos os nomes de âncora definidos.

Um anchor-scope é adicionado a um ancestral do elemento posicionado e do elemento de ancoragem que não contém outros elementos de ancoragem com o mesmo nome. Muitas vezes, isso está na raiz do componente reutilizável.

O exemplo a seguir mostra a diferença que anchor-scope faz quando aplicado a elementos repetidos com o mesmo anchor-name. No exemplo, todos os elementos <img> e os banners de imagem fazem referência ao nome de âncora --image. Quando anchor-scope é aplicado aos elementos <li>, position-anchor: --image corresponde apenas ao elemento <img> no mesmo elemento <li> do banner. Caso contrário, ele corresponde ao último <img> renderizado.

Posicionamento

Agora que você vinculou o elemento à âncora, é hora de posicioná-lo. O posicionamento de âncora oferece dois métodos de posicionamento: position-area e a função anchor().

position-area

A propriedade position-area permite posicionar um elemento ao redor da âncora especificando uma ou duas palavras-chave. Isso abrange muitos casos de uso comuns e geralmente é um bom ponto de partida.

Como o position-area funciona

position-area funciona criando um novo bloco de contenção para o elemento posicionado em uma área gerada pelas bordas da âncora e o bloco de contenção original do elemento posicionado.

Embora haja muitas palavras-chave disponíveis para position-area, elas podem ser divididas em algumas categorias para facilitar a compreensão. O site Anchor-tool.com é uma ótima ferramenta para explorar a sintaxe.

Palavras-chave físicas

Você pode usar as palavras-chave físicas top, left, bottom, right e center. Por exemplo, position-area: top right vai colocar o elemento posicionado acima e à direita da âncora. Essas palavras-chave também têm equivalentes de eixo físico, y-start, x-start, y-end e x-end.

Palavras-chave lógicas

Você também pode usar palavras-chave lógicas, block-start, block-end, inline-start e inline-end. Por exemplo, position-area: block-end inline-start vai colocar o elemento posicionado abaixo e à esquerda da âncora em idiomas como o inglês ou depois da âncora no eixo de bloco e antes da âncora no eixo inline no modo de escrita do documento. center também pode ser usado com uma palavra-chave lógica.

Você também pode omitir o eixo se estiver especificando palavras-chave lógicas, com o eixo de bloco primeiro e o inline depois. position-area: start end é igual a position-area: block-start inline-end ou até mesmo position-area: inline-end block-start.

Abrangendo várias áreas de grade

Até agora, você deve ter percebido que essas opções só permitem colocar o elemento posicionado em um único espaço da grade. Adicionar o prefixo span a propriedades físicas ou lógicas adiciona o espaço adjacente da grade central. position-area: span-top right será posicionado à direita da âncora e de baixo para cima do bloco de contêiner original do elemento posicionado.

Uma posição-área comum para um menu suspenso é position-area: block-end span-inline-end.

A palavra-chave span-all abrange três linhas ou colunas.

Palavra-chave única

Se você definir apenas uma palavra-chave, o outro eixo será definido automaticamente. Isso funciona como esperado, mas pode ser útil entender como funciona.

Se a palavra-chave fornecida for clara sobre o eixo, o outro eixo será calculado como span-all. Isso significa que position-area: bottom é equivalente a position-area: bottom span-all, e o elemento posicionado ficará abaixo da âncora e terá toda a largura do bloco de contêiner disponível.

Por outro lado, se a palavra-chave não indicar claramente um eixo, ela será repetida. position-area: start é equivalente a start start e é colocado na parte superior esquerda da âncora em idiomas da esquerda para a direita.

A função anchor()

Para casos de uso mais avançados, talvez o position-area não atenda aos seus requisitos. A função anchor() permite definir propriedades de encarte individuais com base na posição de outro elemento. Isso resulta em um comprimento de CSS, o que significa que você pode usá-lo em cálculos e com outras funções de CSS. Além disso, você pode vincular lados diferentes a âncoras diferentes.

A função anchor() usa um nome e um lado de âncora. Se o elemento tiver uma âncora padrão, definida com position-anchor ou implicitamente, por exemplo, com um popover, você poderá omitir o nome da âncora.

.positionedElement {
  block-start: anchor(--my-anchor start);
  /*  OR  */
  position-anchor: --my-anchor;
  block-start: anchor(start);
}

Valores substitutos

Se não for possível encontrar uma âncora para uma função anchor(), toda a declaração será inválida. Isso pode acontecer se a âncora for renderizada depois do elemento posicionado ou se não houver um elemento com anchor-name correspondente. Para resolver isso, defina uma duração ou porcentagem alternativa.

.positionedElement {
   block-start: anchor(--my-anchor, 100px)
}

No exemplo anterior, o valor à esquerda do elemento posicionado está ancorado em --focused-anchor, mas esse anchor-name só existe quando o primeiro botão é passado ou focado. Como uma função anchor() é resolvida para um comprimento, você pode usar outra âncora como substituição. Se não fornecermos um substituto, o elemento posicionado não será posicionado.

Palavras-chave de âncora lateral

O valor do lado da âncora escolhe qual das bordas da âncora será posicionada. Assim como position-area, o valor do lado da âncora é compatível com vários tipos diferentes de sintaxe.

Tipo Valores Descrição
Físico top, left, bottom, right

As palavras-chave físicas correspondem a um lado específico da âncora, mas só podem ser usadas no mesmo eixo do encarte do elemento posicionado que você está definindo.

Por exemplo, top: anchor(bottom) posiciona a parte de cima do elemento na parte de baixo da âncora, mas left: anchor(top) não funciona.

Lado inside, outside

A palavra-chave inside corresponde ao mesmo lado da propriedade de encarte, e a palavra-chave outside corresponde ao lado oposto no mesmo eixo.

Por exemplo, inset-block-start: anchor(inside) se refere ao lado block-start da âncora, e inset-inline-end: (outside) se refere ao lado inline-start da âncora.

Lógica start, end, self-start, self-end

As palavras-chave lógicas se referem aos lados da âncora com base no modo de escrita do elemento posicionado com self-start e self-end ou com o modo de escrita do bloco de contêiner do elemento posicionado com start e end.

Porcentagem 0% - 100%

Um valor de porcentagem posiciona o elemento ao longo do eixo do início ao fim da âncora no eixo especificado. 0% está no lado start da âncora, e 100% é o lado final da âncora. center é equivalente a 50%. Se você estiver usando uma porcentagem em um encarte do lado final, como bottom, isso não será invertido. 0% ainda será o lado start da âncora.

Este exemplo mostra como um valor de porcentagem sempre vai do início ao fim no eixo especificado:

Como usar o anchor()

Como anchor() é um comprimento, ele é muito flexível. É possível manipular o valor com funções CSS como max() e calc().

Uma limitação é que só é possível usar funções anchor() em propriedades de encarte.

O exemplo anterior adiciona um plano de fundo atrás do painel de detalhes aberto que é animado de maneira suave quando um painel diferente é aberto e se estende para incluir um painel de detalhes em que o cursor está parado. Para isso, ele usa min() para escolher o menor comprimento entre duas âncoras.

#indicator{
/*  Use the smaller of the 2 values:  */
  inset-block-start: min(
/*   1. The start side of the default anchor, which is the open `<details>` element  */
    anchor(start),
/*   2. The start side of the hovered `<details>` element.    */
    anchor(--hovered start,
/*     If no `<details>` element is hovered, this falls back to infinity px, so that the other value is smaller, and therefore used.   */
       var(calc(1px * infinity)))
  );
}

O exemplo também usa calc() para adicionar espaço inline ao redor do painel aberto.

Usar o tamanho da âncora

Você também pode usar a função anchor-size() para usar as dimensões da âncora no tamanho, na posição ou na margem do elemento posicionado.

anchor-size() usa um nome de âncora ou a âncora padrão. Por padrão, ele usa o tamanho da âncora no eixo em que está sendo usado. Assim, width: anchor-size() retorna a largura da âncora. Você também pode usar o outro eixo especificando o comprimento desejado com as palavras-chave físicas width e height ou as palavras-chave lógicas block, inline, self-block e self-inline.

Como lidar com o estouro

Você criou um componente de menu suspenso e usou o posicionamento de âncora para colocar o menu suspenso onde queria. Mas, ao mover o menu para o outro lado da tela ou usá-lo em um menu de usuário, o nome do usuário fica muito longo. De repente, o menu suspenso sai da tela. E agora?

O posicionamento de âncora do CSS tem um sistema integrado que permite criar rapidamente um conjunto robusto de substituições quando o elemento posicionado fica fora do bloco de contêiner.

Opções alternativas

A regra position-try-fallbacks usa uma lista de opções de substituição. Quando a posição padrão transborda, cada opção é testada em ordem até que haja uma posição que não transborde.

É possível usar qualquer valor position-area como uma opção substituta. Neste exemplo, em modos de escrita da esquerda para a direita, como o inglês, o elemento posicionado vai tentar ficar na parte de baixo da âncora, abrangendo as colunas central e direita. Se isso transbordar, ele tentará ser posicionado na parte de baixo da âncora, abrangendo as colunas esquerda e central. Se isso também transbordar, a posição vai voltar para a posição padrão, mesmo que isso transborde.

.positioned-element {
  position-area: block-end span-inline-end;
  position-try-fallbacks: block-end span-inline-start;
}

Há também várias palavras-chave flip- que processam casos de substituição comuns. flip-block e flip-inline tentam inverter o elemento nos eixos de bloco e inline. Eles também podem ser combinados com flip-block flip-inline para inverter os dois eixos. O valor flip-start vira o elemento posicionado em uma linha diagonal do início até os cantos finais da âncora.

Você também pode criar uma opção de substituição personalizada com @position-try. Assim, é possível definir as margens, o alinhamento e até mesmo mudar a âncora.

@position-try --menu-below {
  position-area: bottom span-right;
  margin-top: 1em;
}

#positioned-element {
  position-try: --menu-below;
}

flip-block e flip-inline podem ser adicionados às opções de substituição @position-try para criar uma variante.

#positioned-element {
  position-try: --menu-below, flip-inline --menu-below;
}

No exemplo anterior, o navegador segue estas etapas, parando assim que encontra uma solução que não transborda.

  1. O elemento é colocado com position-area: end, na parte inferior direita da âncora.
  2. Se isso transbordar, o elemento será colocado com a opção de substituição personalizada chamada --bottom-span-right, que o coloca com position-area: bottom span-right e uma margem extra abaixo.
  3. Se isso transbordar, o elemento será colocado com flip-inline --bottom-span-right, que combina a opção de substituição personalizada com flip-inline, que é essencialmente position-area: bottom span-left.
  4. Se isso transbordar, o elemento será colocado usando a opção de substituição personalizada --use-alternate, que o coloca abaixo de uma âncora completamente diferente.
  5. Se isso transbordar, o elemento vai voltar ao posicionamento original, com position-area: end, mesmo que isso cause transbordamento.

Ordem de fallback

Por padrão, quando a posição inicial transborda, o navegador tenta cada opção em position-try-fallbacks até encontrar uma posição que não transborde. É possível substituir esse comportamento com position-try-order para testar cada opção de substituição e usar aquela que tem mais espaço em um eixo especificado.

É possível especificar o eixo com as palavras-chave lógicas most-block-size e most-inline-size ou com as palavras-chave físicas most-height e most-width.

position-try-order e position-try-fallbacks podem ser combinados com a abreviação position-try, com a ordem vindo primeiro.

Rolagem

Quando um usuário rola a tela, ele espera que a página se mova de maneira fluida. Para isso, os navegadores têm limites de uso do posicionamento de âncora durante a rolagem.

É possível vincular um elemento posicionado a âncoras em diferentes contêineres de rolagem, mas o elemento só se move em resposta à rolagem de uma das âncoras. Esse será o elemento âncora padrão, que é a âncora implícita de um popover ou o valor de position-anchor.

O elemento posicionado permanece visível mesmo quando a âncora é rolada para fora da visualização. Para ocultar o elemento posicionado quando a âncora estiver oculta, defina position-visibility: anchors-visible. Isso se aplica não apenas quando a âncora é rolada demais, mas também se ela estiver oculta de outras maneiras, por exemplo, com visibility: hidden.

Teste seu conhecimento

Quais são os valores válidos para o lado em anchor()?

inside
Correto.
25%
Correto.
25px
Incorreto. Embora um comprimento como 25px possa ser usado como o valor substituto, apenas porcentagens podem ser usadas para o lado.
block-start
Incorreto
start
Correto.

Quais são os valores válidos para position-area?

top
Correto.
block-end inline-end
Correto.
block-start block-end
Incorreto. Só é possível definir uma coluna ou linha em cada eixo.

Quais propriedades são compatíveis com a função anchor()?

top
Correto.
margin-left
Incorreto.
inset-block-start
Correto.
transform
Incorreto.

O que acontece se houver várias âncoras com o mesmo anchor-name?

O elemento posicionado é duplicado e vinculado a cada correspondência.
Incorreto.
O elemento posicionado é vinculado ao primeiro no documento.
Incorreto.
O elemento posicionado é vinculado ao último no documento.
Correto.
O elemento posicionado é fixado na âncora mais próxima.
Incorreto.