Use a propriedade interpolate-size ou a função calc-size() para ativar transições e animações suaves de comprimentos para palavras-chave de dimensionamento intrínseco e vice-versa.
Publicado em 17 de setembro de 2024
Introdução
Um recurso do CSS solicitado com frequência é a capacidade de animar para height: auto. Uma pequena variação dessa solicitação é fazer a transição da propriedade width em vez de height ou para qualquer um dos outros tamanhos intrínsecos representados por palavras-chave como min-content, max-content e fit-content.
Por exemplo, na demonstração a seguir, seria bom se os rótulos fossem animados suavemente para a largura natural ao passar o cursor sobre os ícones.
O CSS usado é o seguinte:
nav a {
width: 80px;
overflow-x: clip;
transition: width 0.35s ease; /* 👈 Transition the width */
&:hover,
&:focus-visible {
width: max-content; /* 👈 Doesn't work with transitions */
}
}
Mesmo que um transition seja declarado para fazer a transição da propriedade width e width: auto seja declarado em :hover, nenhuma transição suave acontece. Em vez disso, a mudança é abrupta.
Animar para e de palavras-chave de dimensionamento intrínseco com interpolate-size
A propriedade CSS interpolate-size permite controlar se as animações e transições de palavras-chave de dimensionamento intrínseco do CSS devem ser permitidas ou não.
O valor padrão é numeric-only, que não ativa a interpolação. Ao definir a propriedade como allow-keywords, você ativa as interpolações de comprimentos para palavras-chave de dimensionamento intrínseco do CSS nos casos em que o navegador pode animar essas palavras-chave.
De acordo com a especificação:
numeric-only: um<intrinsic-size-keyword>não pode ser interpolado.allow-keywords: dois valores podem ser interpolados se um deles for<intrinsic-size-keyword>e o outro<length-percentage>. […]
Como a propriedade interpolate-size é herdada, ela pode ser declarada em :root para permitir a transição de e para as palavras-chave de dimensionamento intrínseco de todo o documento. Este é o método recomendado.
/* Opt-in the whole page to interpolate sizes to/from keywords */
:root {
interpolate-size: allow-keywords; /* 👈 */
}
Na demonstração a seguir, essa regra é adicionada ao código. Como resultado, as animações de width: auto funcionam bem (em navegadores com suporte):
Limitar o alcance da ativação restringindo o seletor
Se você quiser limitar a ativação de allow-keywords apenas a um subárvore do documento, ajuste o seletor de :root para o elemento que você quer segmentar. Por exemplo, se o <header> da sua página não for compatível com esse tipo de transição, você poderá limitar a ativação apenas ao elemento <main> e aos elementos filhos dele da seguinte maneira:
main { /* 👈 Scope the opt-in to only <main> and its descendants */
interpolate-size: allow-keywords;
}
Por que não é permitido usar a animação para dimensionar palavras-chave por padrão?
Um feedback comum sobre esse mecanismo de ativação é que os navegadores devem permitir apenas transições e animações de palavras-chave de dimensionamento intrínseco para comprimentos por padrão.
A opção de ativar esse comportamento foi pesquisada durante o desenvolvimento do recurso. O grupo de trabalho descobriu que ativar essa opção por padrão não é compatível com versões anteriores porque muitas folhas de estilo presumem que as palavras-chave de dimensionamento intrínseco (como auto ou min-content) não podem ser animadas. Confira os detalhes neste comentário sobre o problema relevante do Grupo de Trabalho do CSS.
Portanto, a propriedade é opcional. Graças à característica de herança, a ativação de um documento inteiro é apenas uma declaração interpolate-size: allow-sizes em :root, conforme detalhado anteriormente.
Animar para e de palavras-chave de dimensionamento intrínseco com calc-size()
Outra maneira de ativar a interpolação de e para palavras-chave de dimensionamento intrínseco é usar a função calc-size(). Ele permite que a matemática seja realizada em tamanhos intrínsecos de maneira segura e bem definida.
A função aceita dois argumentos, em ordem:
- Uma base de tamanho de cálculo, que pode ser um
<intrinsic-size-keyword>, mas também umcalc-size()aninhado. - Um cálculo de tamanho de cálculo, que permite realizar cálculos usando a base de tamanho de cálculo. Para se referir à base de tamanho de cálculo, use a palavra-chave
size.
Veja alguns exemplos:
width: calc-size(auto, size); // = the auto width, unaltered
width: calc-size(min-content, size); // = the min-content width, unaltered
Adicionando calc-size() à demonstração original, o código fica assim:
nav a {
width: 80px;
overflow-x: clip;
transition: width 0.35s ease;
&:hover,
&:focus-visible {
width: calc-size(max-content, size); /* 👈 */
}
}
Visualmente, o resultado é exatamente o mesmo que ao usar interpolate-size. Portanto, nesse caso específico, use interpolate-size.
A capacidade de fazer cálculos é a principal vantagem do calc-size(), algo que não pode ser feito com interpolate-size:
width: calc-size(auto, size - 10px); // = The auto width minus 10 pixels
width: calc-size(min-content, size + 1rem); // = The min-content width plus 1rem
width: calc-size(max-content, size * .5); // = Half the max-content width
Por exemplo, se você quiser que todos os parágrafos de uma página sejam dimensionados para o múltiplo mais próximo de 50px, use o seguinte:
p {
width: calc-size(fit-content, round(up, size, 50px));
height: calc-size(auto, round(up, size, 50px));
}
O calc-size() também permite interpolar entre dois calc-size()s quando as duas bases de tamanho de cálculo são idênticas. Isso também não pode ser alcançado com interpolate-size.
#element {
width: min-content; /* 👈 */
transition: width 0.35s ease;
&:hover {
width: calc-size(min-content, size + 10px); /* 👈 */
}
}
Por que não permitir <intrinsic-size-keyword> em calc()?
Uma pergunta que aparece com frequência em relação ao calc-size() é por que o Grupo de Trabalho do CSS não ajustou a função calc() para oferecer suporte a palavras-chave de dimensionamento intrínseco.
Uma das razões para isso é que você não pode misturar e combinar palavras-chave de dimensionamento intrínseco ao fazer cálculos. Por exemplo, você pode ter vontade de escrever calc(max-content - min-content), que parece válido, mas não é. calc-size() garante a correção porque, ao contrário de calc(), aceita apenas um <intrinsic-size-keyword> como primeiro argumento.
Outro motivo é a consciência de contexto. Alguns algoritmos de layout têm um comportamento especial para palavras-chave de dimensionamento intrínseco específicas. O calc-size() é definido explicitamente para representar um tamanho intrínseco, não um <length>. Graças a isso, esses algoritmos podem tratar calc-size(<intrinsic-size-keyword>, …) como <intrinsic-size-keyword>, mantendo o comportamento especial para essa palavra-chave.
Qual abordagem usar?
Na maioria dos casos, declare interpolate-size: allow-keywords em :root. É a maneira mais fácil de ativar a animação para e de palavras-chave de dimensionamento intrínseco, já que é basicamente uma linha.
/* Opt-in the whole page to animating to/from intrinsic sizing keywords */
:root {
interpolate-size: allow-keywords; /* 👈 */
}
Esse código é um bom aprimoramento progressivo, já que os navegadores que não oferecem suporte a ele vão voltar a não usar transições.
Quando você precisa de um controle mais refinado sobre as coisas, como fazer cálculos, ou quer usar um comportamento que só o calc-size() pode fazer, use calc-size().
#specific-element {
width: 50px;
&:hover {
width: calc-size(fit-content, size + 1em); /* 👈 Only calc-size() can do this */
}
}
No entanto, o uso de calc-size() no código exige que você inclua substitutos para navegadores que não oferecem suporte a calc-size(). Por exemplo, adicionar declarações de tamanho extras ou retornar à detecção de recursos usando @supports.
width: fit-content;
width: calc-size(fit-content, size + 1em);
/* 👆 Browsers with no calc-size() support will ignore this second declaration,
and therefore fall back to the one on the line before it. */
Mais demonstrações
Confira mais algumas demonstrações que usam interpolate-size: allow-keywords a seu favor.
Notificações
A demonstração a seguir é uma bifurcação da demonstração @starting-style. O código foi ajustado para permitir a adição de itens com alturas variadas.
Para isso, toda a página ativa a interpolação de tamanho da palavra-chave, e o height em cada elemento .item é definido como auto. Caso contrário, o código é exatamente o mesmo de antes da bifurcação.
:root {
interpolate-size: allow-keywords; /* 👈 */
}
.item {
height: auto; /* 👈 */
@starting-style {
height: 0px;
}
}
Animar o elemento <details>
Um caso de uso típico em que você gostaria de usar esse tipo de interpolação é para animar um widget de exibição ou um acordeão exclusivo à medida que ele é aberto. No HTML, use o elemento <details> para isso.
Com o interpolate-size: allow-keywords, você pode fazer muito:
@supports (interpolate-size: allow-keywords) {
:root {
interpolate-size: allow-keywords;
}
details {
transition: height 0.5s ease;
height: 2.5rem;
&[open] {
height: auto;
overflow: clip; /* Clip off contents while animating */
}
}
}
No entanto, a animação só é executada quando o widget de abertura é aberto. Para atender a isso, o Chrome está trabalhando no pseudo ::details-content, que será lançado no Chrome ainda este ano (e será abordado em uma postagem futura). Combinando interpolate-size: allow-keywords e ::details-content, é possível ter uma animação em ambas as direções: