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 è la transizione della proprietà width anziché height o la transizione a qualsiasi 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 è stato dichiarato un transition per la transizione della proprietà width e width: auto viene dichiarato il giorno :hover, non avviene alcuna transizione senza problemi. Invece, il cambiamento è brusco.

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

Supporto dei browser

  • Chrome: 129.
  • Edge: non supportato.
  • Firefox: non supportato.
  • Safari: non supportato.

Origine

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 suo valore predefinito è numeric-only e non abilita 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 animarle.

In base alle 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 che supportano):

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 relative alle dimensioni intrinseche (ad esempio auto o min-content) non possano essere animate. Puoi trovare i dettagli in questo commento relativo al problema del gruppo di lavoro CSS pertinente.

Pertanto, la proprietà è attiva. Grazie alla sua caratteristica ereditaria, l'attivazione di un intero documento non è altro che 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()

Supporto dei browser

  • Chrome: 129.
  • Edge: 129.
  • Firefox: non supportato.
  • Safari: non supportato.

Origine

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:

  • Una base per il calcolo delle dimensioni, che può essere un elemento <intrinsic-size-keyword> ma anche un elemento 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 di quando si utilizza interpolate-size. Pertanto, in questo caso specifico devi utilizzare interpolate-size.

Il vantaggio di calc-size() è la sua capacità di fare calcoli, 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 è consentito combinare parole chiave relative alle dimensioni intrinseche durante i calcoli. Ad esempio, si potrebbe essere tentati di scrivere calc(max-content - min-content) che sembra valido, ma in realtà non è così. 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 da e verso parole chiave con dimensioni intrinseche, poiché si tratta essenzialmente di un testo di una riga.

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

Questa porzione di codice è un bel miglioramento progressivo, in quanto i browser che non lo supportano torneranno a non utilizzare 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, se utilizzi calc-size() nel codice, dovrai includere i fallback 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 di @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 andare abbastanza lontano:

@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: