Use a propriedade interpolate-size
ou a função calc-size()
para permitir transições e animações suaves de comprimentos a palavras-chave de dimensionamento intrínseco e volta.
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 permite 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 será adicionada ao código. Como resultado, as animações de e para width: auto
funcionam bem (em navegadores compatíveis):
Limitar o alcance da ativação restringindo o seletor
Se você quiser limitar a ativação de allow-keywords
a apenas uma subárvore do documento, ajuste o seletor de :root
para apenas 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 animar e redimensionar 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 pressupõem 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 feito 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 geralmente aparece com 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 ficar tentado a escrever calc(max-content - min-content)
, que parece válido, mas na realidade 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. calc-size()
é definido explicitamente para representar um tamanho intrínseco, não um <length>
. Graças a isso, esses algoritmos conseguem 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 aprimoramento progressivo interessante, pois os navegadores que não o suportam voltarão 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 esse tipo de interpolação é usado é animar um widget de divulgação ou um acordeão exclusivo quando ele for 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 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: