Animar para tamanhos intrínsecos

Recursos da Web: calc-size() interpolate-size

A animação de elementos para tamanhos dinâmicos, como block-size: auto ou inline-size: max-content, sempre exigiu JavaScript ou hacks frágeis de "max-height". A propriedade interpolate-size e a função calc-size() permitem que o navegador faça a interpolação nativa entre comprimentos fixos e palavras-chave de dimensionamento intrínseco.

Etapas de implementação

  1. Ativar a interpolação de palavras-chave: aplique interpolate-size: allow-keywords a um elemento pai (geralmente :root) para ativar transições de propriedades que usam palavras-chave intrínsecas.
  2. Defina a transição: defina um transition para a propriedade de dimensionamento (por exemplo, block-size, inline-size) no elemento de destino.
  3. Use palavras-chave intrínsecas: mude a propriedade de dimensionamento para uma palavra-chave intrínseca compatível (auto, min-content, max-content, fit-content ou, para flex-basis, content) durante uma interação (por exemplo, :hover ou uma classe de estado).
  4. Fazer cálculos (opcional): use calc-size() se precisar fazer cálculos matemáticos em um tamanho intrínseco (por exemplo, auto + 2rem). O calc-size() também é compatível com a palavra-chave any para cálculos que não dependem da base.

Exemplo: padrão de expansão genérico

Você pode aplicar esse padrão a qualquer contêiner (como uma seção "Mostrar mais" ou um menu de navegação) para fazer a transição entre uma altura restrita e o tamanho natural do elemento.

/* Opt-in globally for all children */
:root {
  /* MANDATORY: Transitions to intrinsic keywords are disabled by default for compatibility */
  interpolate-size: allow-keywords;
}

.expandable-container {
  /* 1. Define a fixed initial size (or 0) and hide overflow */
  block-size: 100px;
  overflow: hidden;
  
  /* 2. Transition the sizing property */
  transition: block-size 0.4s ease-out;
}

.expandable-container.is-expanded {
  /* 3. Smoothly animate to the intrinsic natural height */
  block-size: auto;
}

Exemplo: tamanho inline intrínseco calculado

.badge {
  inline-size: 40px;
  overflow: hidden;
  white-space: nowrap;
  transition: inline-size 0.3s ease;
}

.badge:hover {
  /* calc-size(basis, calculation) */
  /* 'size' refers to the evaluated basis (max-content in this case) */
  inline-size: calc-size(max-content, size + 20px);
}

Exemplo: transição de intrínseco para fixo

Também é possível animar na direção oposta, começando de um tamanho natural e reduzindo para um comprimento específico. Isso é útil para componentes "dispensáveis".

.collapsible-alert {
  /* 1. Start with the natural content height */
  block-size: auto;
  overflow: hidden;
  transition: block-size 0.5s ease-in-out, opacity 0.5s ease;
}

.collapsible-alert.is-dismissed {
  /* 2. Smoothly collapse to zero */
  block-size: 0;
  opacity: 0;
  pointer-events: none;
}

/* MANDATORY Copy-Paste Safety: Disable sizing animations for sensitive users */
@media (prefers-reduced-motion: reduce) {
  .expandable-container,
  .badge,
  .collapsible-alert {
    transition: none !important;
  }
}
// MANDATORY Accessibility Synchronization: Ensure elements collapsed to zero dimensions are removed from the assistive technology tree, and sync aria-expanded states on triggers.
const alertElement = document.querySelector('.collapsible-alert');
alertElement.addEventListener('transitionend', (e) => {
  if (e.propertyName === 'block-size' && alertElement.classList.contains('is-dismissed')) {
    alertElement.hidden = true;
  }
});

// Example trigger syncer
const triggerBtn = document.querySelector('.accordion-trigger');
triggerBtn?.addEventListener('click', () => {
  const isExpanded = triggerBtn.getAttribute('aria-expanded') === 'true';
  triggerBtn.setAttribute('aria-expanded', !isExpanded);
});

Principais limitações

  • Restrição de palavra-chave para palavra-chave: não é possível animar entre duas palavras-chave diferentes diretamente (por exemplo, de min-content para max-content). Uma extremidade da transição precisa ser um comprimento ou uma porcentagem fixa (por exemplo, de 0 para auto).
  • Sintaxe de calc-size: em calc-size(), não é possível misturar palavras-chave intrínsecas diferentes na mesma expressão. O primeiro argumento (a base) define o que size representa.
  • Requisito de ativação: as transições para palavras-chave intrínsecas estão desativadas por padrão (numeric-only) para manter a compatibilidade com versões anteriores. É necessário aplicar interpolate-size: allow-keywords ao elemento ou a um ancestral. calc-size() atua como uma substituição por propriedade, ativando automaticamente a interpolação sempre que é usada.

Estratégias de fallback

interpolate-size e calc-size() são aprimoramentos progressivos. Os navegadores que não são compatíveis com eles vão pular instantaneamente para o tamanho desejado.

  • Degradação suave: para transições simples de block-size: auto, os navegadores padrão simplesmente alternam o tamanho instantaneamente, o que é funcional, mas menos refinado.
  • Palavras-chave substitutas manuais: ao usar calc-size(), sempre forneça uma palavra-chave substituta padrão para navegadores mais antigos, já que eles descartam toda a declaração calc-size().
.card {
  block-size: auto; /* Fallback for older browsers */
  block-size: calc-size(auto, size); /* Modern browsers use this */
  transition: block-size 0.3s ease;
}