Animate to height: auto; (e altre parole chiave di dimensionamento intrinseco) in CSS

Utilizza la proprietà interpolate-size o la funzione calc-size() per attivare transizioni e animazioni fluide dalle lunghezze alle parole chiave di dimensionamento intrinseco e viceversa.

Data di pubblicazione: 17 settembre 2024

Introduzione

Una funzionalità CSS spesso richiesta è la possibilità di animare height: auto. Una leggera variazione di questa richiesta consiste nel passare alla proprietà width anziché a height oppure a una delle altre dimensioni intrinseche rappresentate da parole chiave come min-content, max-content e fit-content.

Ad esempio, nella demo seguente sarebbe bello se le etichette si animassero gradualmente fino alla loro larghezza naturale quando si passa il mouse sopra le icone.

Il CSS utilizzato è il seguente:

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 */
    }
}

Anche se viene dichiarato un transition per eseguire la transizione della proprietà width e width: auto è dichiarato in :hover, non viene eseguita alcuna transizione fluida. Invece, il cambiamento è brusco.

Creare animazioni da e verso le parole chiave per le dimensioni intrinseche con interpolate-size

Browser Support

  • Chrome: 129.
  • Edge: 129.
  • Firefox: not supported.
  • Safari: not supported.

Source

La proprietà interpolate-size CSS ti consente di controllare se le animazioni e le transizioni delle parole chiave di dimensionamento intrinseco CSS devono essere consentite o meno.

Il valore predefinito è numeric-only, che non attiva l'interpolazione. Se imposti la proprietà su allow-keywords, attivi le interpolazioni dalle lunghezze alle parole chiave di dimensionamento intrinseco CSS nei casi in cui il browser possa animare queste parole chiave.

Secondo le specifiche:

  • numeric-only: un <intrinsic-size-keyword> non può essere interpolato.
  • allow-keywords: è possibile interpolare due valori se uno è <intrinsic-size-keyword> e l'altro è <length-percentage>. […]

Poiché la proprietà interpolate-size è una proprietà ereditata, puoi dichiararla in :root per attivare la transizione alle e dalle parole chiave di dimensionamento intrinseco per l'intero documento. Questo è l'approccio consigliato.

/* Opt-in the whole page to interpolate sizes to/from keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

Nella demo seguente, questa regola viene aggiunta al codice. Di conseguenza, le animazioni da e verso width: auto funzionano correttamente (nei browser con supporto):

Limita la copertura dell'attivazione restringendo il selettore

Se vuoi limitare l'attivazione di allow-keywords solo a un sottoalbero del documento, modifica il selettore da allow-keywords all'elemento che vuoi scegliere come target.:root Ad esempio, se il <header> della tua pagina non è compatibile con questo tipo di transizioni, puoi limitare l'attivazione solo all'elemento <main> e ai suoi discendenti come segue:

main { /* 👈 Scope the opt-in to only <main> and its descendants */
    interpolate-size: allow-keywords;
}

Perché non consentire l'animazione verso e dalle parole chiave per le dimensioni per impostazione predefinita?

Un feedback comune su questo meccanismo di attivazione è che i browser dovrebbero consentire solo le transizioni e le animazioni dalle parole chiave di dimensioni intrinseche alle lunghezze per impostazione predefinita.

L'opzione per attivare questo comportamento è stata studiata durante lo sviluppo della funzionalità. Il gruppo di lavoro ha scoperto che l'attivazione di questa opzione per impostazione predefinita non è compatibile con le versioni precedenti perché molti fogli di stile presuppongono che le parole chiave di dimensionamento intrinseco (ad es. auto o min-content) non possano essere animate. Puoi trovare i dettagli in questo commento sul problema pertinente del gruppo di lavoro CSS.

Pertanto, la proprietà è attiva. Grazie alla sua caratteristica di ereditarietà, l'attivazione di un intero documento è semplicemente una dichiarazione interpolate-size: allow-sizes su :root, come descritto in precedenza.

Esegui l'animazione verso e da parole chiave per le dimensioni intrinseche con calc-size()

Browser Support

  • Chrome: 129.
  • Edge: 129.
  • Firefox: not supported.
  • Safari: not supported.

Source

Un altro modo per attivare l'interpolazione verso e dalle parole chiave di dimensionamento intrinseco è utilizzare la funzione calc-size(). Consente di eseguire operazioni matematiche sulle dimensioni intrinseche in modo sicuro e ben definito.

La funzione accetta due argomenti, nell'ordine:

  • Un criterio di base per le dimensioni del calcolo, che può essere un <intrinsic-size-keyword>, ma anche un calc-size() nidificato.
  • Un calcolo delle dimensioni calcolate, che ti consente di eseguire calcoli utilizzando la base delle dimensioni calcolate. Per fare riferimento alla base di calcolo delle dimensioni, utilizza la parola chiave size.

Ecco alcuni esempi:

width: calc-size(auto, size);        // = the auto width, unaltered
width: calc-size(min-content, size); // = the min-content width, unaltered

Se aggiungi calc-size() alla demo originale, il codice avrà il seguente aspetto:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease;

    &:hover,
    &:focus-visible {
        width: calc-size(max-content, size); /* 👈 */
    }
}

Visivamente, il risultato è esattamente lo stesso che si ottiene utilizzando interpolate-size. Pertanto, in questo caso specifico devi utilizzare interpolate-size.

La vera forza di calc-size() è la sua capacità di eseguire calcoli, cosa che non è possibile con 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

Ad esempio, se vuoi che tutti i paragrafi di una pagina abbiano le dimensioni del multiplo più vicino di 50px, puoi utilizzare quanto segue:

p {
    width: calc-size(fit-content, round(up, size, 50px));
    height: calc-size(auto, round(up, size, 50px));
}

calc-size() consente inoltre di eseguire l'interpolazione tra due calc-size() quando entrambe le basi di calcolo delle dimensioni sono identiche. Anche questo è un risultato che non può essere ottenuto con interpolate-size.

#element {
    width: min-content; /* 👈 */
    transition: width 0.35s ease;

    &:hover {
        width: calc-size(min-content, size + 10px); /* 👈 */
    }
}

Perché non consentire <intrinsic-size-keyword> in calc()?

Una domanda che viene spesso posta in merito a calc-size() è perché il gruppo di lavoro CSS non ha modificato la funzione calc-size() per supportare le parole chiave di dimensionamento intrinseco.calc()

Uno dei motivi è che non puoi combinare le parole chiave di dimensionamento intrinseco durante i calcoli. Ad esempio, potresti essere tentato di scrivere calc(max-content - min-content), che sembra valido, ma in realtà non lo è. calc-size() applica la correttezza perché, a differenza di calc(), accetta un solo <intrinsic-size-keyword> come primo argomento.

Un altro motivo è la consapevolezza del contesto. Alcuni algoritmi di layout hanno un comportamento speciale per parole chiave di dimensionamento intrinseco specifiche. calc-size() è definito esplicitamente per rappresentare una dimensione intrinseca, non un <length>. Grazie a questo, questi algoritmi sono in grado di trattare calc-size(<intrinsic-size-keyword>, …) come <intrinsic-size-keyword>, mantenendo il suo comportamento speciale per quella parola chiave.

Quale approccio utilizzare?

Nella maggior parte dei casi, dichiara interpolate-size: allow-keywords su :root. È il modo più semplice per attivare l'animazione verso e dalle parole chiave di dimensionamento intrinseco, in quanto si tratta essenzialmente di un comando composto da una sola riga.

/* Opt-in the whole page to animating to/from intrinsic sizing keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

Questo frammento di codice è un buon miglioramento progressivo, in quanto i browser che non lo supportano torneranno a non utilizzare le transizioni.

Quando hai bisogno di un controllo più granulare, ad esempio per eseguire calcoli, o vuoi utilizzare un comportamento che solo calc-size() può eseguire, puoi ricorrere all'utilizzo di calc-size().

#specific-element {
    width: 50px;

    &:hover {
        width: calc-size(fit-content, size + 1em); /* 👈 Only calc-size() can do this */
    }
}

Tuttavia, l'utilizzo di calc-size() nel codice richiede l'inclusione di soluzioni di riserva per i browser che non supportano calc-size(). Ad esempio, aggiungendo dichiarazioni di dimensioni aggiuntive o tornando al rilevamento delle funzionalità utilizzando @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. */

Altre demo

Ecco altre demo che sfruttano interpolate-size: allow-keywords a proprio vantaggio.

Notifiche

La seguente demo è un fork di questa demo @starting-style. Il codice è stato modificato per consentire l'aggiunta di elementi con altezze diverse.

Per farlo, l'intera pagina attiva l'interpolazione delle parole chiave per le dimensioni e height su ogni elemento .item è impostato su auto. In caso contrario, il codice è esattamente lo stesso di prima del fork.

:root {
    interpolate-size: allow-keywords; /* 👈 */
}

.item {
    height: auto; /* 👈 */

    @starting-style {
        height: 0px;
    }
}

Animare l'elemento <details>

Un caso d'uso tipico in cui è consigliabile utilizzare questo tipo di interpolazione è l'animazione di un widget di informativa o di una fisarmonica esclusiva durante l'apertura. In HTML, utilizzi l'elemento <details> per questo scopo.

Con interpolate-size: allow-keywords puoi ottenere risultati sorprendenti:

@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 */
        }
    }
}

Come puoi vedere, però, l'animazione viene eseguita solo quando si apre il widget dell'informativa. Per risolvere il problema, in Chrome stiamo lavorando allo pseudo ::details-content che verrà rilasciato entro la fine dell'anno (e che verrà trattato in un post futuro). Combinando interpolate-size: allow-keywords e ::details-content, puoi ottenere un'animazione in entrambe le direzioni: