Animer avec height: auto; (et d'autres mots clés de dimensionnement intrinsèque) en CSS

Utilisez la propriété interpolate-size ou la fonction calc-size() pour activer des transitions et des animations fluides entre les longueurs et les mots clés de dimensionnement intrinsèque, et inversement.

Publié le 17 septembre 2024

Introduction

Une fonctionnalité CSS souvent demandée est la possibilité d'animer vers height: auto. Une légère variation de cette requête consiste à effectuer la transition de la propriété width au lieu de height, ou à passer à l'une des autres tailles intrinsèques représentées par des mots clés tels que min-content, max-content et fit-content.

Par exemple, dans la démonstration suivante, il serait souhaitable que les libellés passent progressivement à leur largeur naturelle lorsque vous pointez sur les icônes.

Le CSS utilisé est le suivant:

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

Même si un transition est déclaré pour effectuer la transition de la propriété width et que width: auto est déclaré sur :hover, aucune transition en douceur ne se produit. Au lieu de cela, le changement est brutal.

Animer des mots clés de dimensionnement intrinsèque avec interpolate-size

Navigateurs pris en charge

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

Source

La propriété CSS interpolate-size vous permet de contrôler si les animations et les transitions des mots clés de dimensionnement intrinsèque CSS doivent être autorisées ou non.

Sa valeur par défaut est numeric-only, ce qui désactive l'interpolation. Lorsque vous définissez la propriété sur allow-keywords, vous activez les interpolations des longueurs vers les mots clés de dimensionnement intrinsèque CSS lorsque le navigateur peut animer ces mots clés.

Conformément aux spécifications:

  • numeric-only: un <intrinsic-size-keyword> ne peut pas être interpolé.
  • allow-keywords: deux valeurs peuvent être interpolées si l'une d'elles est un <intrinsic-size-keyword> et l'autre un <length-percentage>. […]

Étant donné que la propriété interpolate-size est héritée, vous pouvez la déclarer sur :root pour permettre la transition entre les mots clés de dimensionnement intrinsèque et l'ensemble du document. Il s'agit de l'approche recommandée.

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

Dans la démonstration suivante, cette règle est ajoutée au code. Par conséquent, les animations vers et depuis width: auto fonctionnent correctement (dans les navigateurs compatibles):

Limiter la couverture de l'acceptation en réduisant le sélecteur

Si vous souhaitez limiter l'activation de allow-keywords à un sous-arbre de votre document, ajustez le sélecteur de :root à l'élément que vous souhaitez cibler. Par exemple, si l'<header> de votre page n'est pas compatible avec ce type de transitions, vous pouvez limiter l'activation à l'élément <main> et à ses descendants comme suit:

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

Pourquoi ne pas autoriser l'animation vers et depuis les mots clés de dimensionnement par défaut ?

Les commentaires les plus courants concernant ce mécanisme d'activation sont les suivants : les navigateurs ne devraient autoriser que les transitions et les animations des mots clés de dimensionnement intrinsèque vers des longueurs par défaut.

L'option permettant d'activer ce comportement a été étudiée lors du développement de la fonctionnalité. Le groupe de travail a découvert que l'activation de cette fonctionnalité par défaut n'est pas rétrocompatible, car de nombreuses feuilles de style supposent que les mots clés de dimensionnement intrinsèque (tels que auto ou min-content) ne peuvent pas être animés. Vous trouverez plus d'informations dans ce commentaire sur le problème du groupe de travail CSS concerné.

Par conséquent, la propriété est activée. Grâce à sa caractéristique d'héritage, l'activation d'un document entier est simplement une déclaration interpolate-size: allow-sizes sur :root, comme indiqué précédemment.

Animer des mots clés de dimensionnement intrinsèque avec calc-size()

Navigateurs pris en charge

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

Source

Vous pouvez également activer l'interpolation à partir et vers les mots clés de dimensionnement intrinsèque à l'aide de la fonction calc-size(). Il permet d'effectuer des opérations mathématiques sur des tailles intrinsèques de manière sécurisée et bien définie.

La fonction accepte deux arguments, dans l'ordre suivant:

  • Une base de calcul de la taille, qui peut être un <intrinsic-size-keyword>, mais aussi un calc-size() imbriqué.
  • Un calcul calc-size, qui vous permet d'effectuer des calculs en utilisant la base calc-size. Pour faire référence à la base de taille de calcul, utilisez le mot clé size.

Voici quelques exemples :

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

Si vous ajoutez calc-size() à la démo d'origine, le code se présente comme suit:

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

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

Visuellement, le résultat est exactement le même que lorsque vous utilisez interpolate-size. Dans ce cas précis, vous devez utiliser interpolate-size.

calc-size() se distingue par sa capacité à effectuer des calculs, ce qui est impossible avec 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

Par exemple, si vous souhaitez que tous les paragraphes d'une page soient dimensionnés au multiple le plus proche de 50px, vous pouvez utiliser la formule suivante:

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

calc-size() vous permet également d'interpoler deux calc-size() lorsque leurs bases de taille de calcul sont identiques. Cela ne peut pas non plus être réalisé avec interpolate-size.

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

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

Pourquoi ne pas autoriser <intrinsic-size-keyword> dans calc() ?

La question qui s'affiche fréquemment avec calc-size() concerne la raison pour laquelle le groupe de travail CSS n'a pas ajusté la fonction calc() pour accepter les mots clés de dimensionnement intrinsèque.

En effet, vous n'êtes pas autorisé à combiner des mots clés de dimensionnement intrinsèque lorsque vous effectuez des calculs. Par exemple, vous pourriez être tenté d'écrire calc(max-content - min-content), qui semble valide, mais qui ne l'est pas. calc-size() applique la correction, car contrairement à calc(), il n'accepte qu'un seul <intrinsic-size-keyword> comme premier argument.

Une autre raison est la prise en compte du contexte. Certains algorithmes de mise en page ont un comportement spécial pour certains mots clés de dimensionnement intrinsèque. calc-size() est défini explicitement pour représenter une taille intrinsèque, et non un <length>. Grâce à cela, ces algorithmes peuvent traiter calc-size(<intrinsic-size-keyword>, …) comme <intrinsic-size-keyword>, tout en conservant son comportement spécial pour ce mot clé.

Quelle approche adopter ?

Dans la plupart des cas, déclarez interpolate-size: allow-keywords sur :root. Il s'agit du moyen le plus simple d'activer l'animation vers et depuis les mots clés de dimensionnement intrinsèque, car il s'agit essentiellement d'une ligne.

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

Ce morceau de code est une amélioration progressive intéressante, car les navigateurs qui ne le prennent pas en charge n'utilisent aucune transition.

Lorsque vous avez besoin d'un contrôle plus précis sur des éléments (par exemple, effectuer des calculs) ou que vous souhaitez utiliser un comportement que seul calc-size() peut effectuer, vous pouvez utiliser calc-size().

#specific-element {
    width: 50px;

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

Toutefois, si vous utilisez calc-size() dans votre code, vous devrez inclure des créations de remplacement pour les navigateurs qui ne sont pas compatibles avec calc-size(). Par exemple, en ajoutant des déclarations de taille supplémentaires ou en revenant à la détection de fonctionnalités à l'aide de @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. */

Autres démonstrations

Voici d'autres démonstrations qui tirent parti de interpolate-size: allow-keywords.

Notifications

La démonstration suivante est un fork de cette démonstration @starting-style. Le code a été ajusté pour permettre d'ajouter des éléments de différentes hauteurs.

Pour ce faire, l'interpolation de la taille des mots clés est activée pour l'ensemble de la page, et la valeur auto est définie pour height sur chaque élément .item. Sinon, le code est exactement le même qu'avant la duplication.

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

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

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

Animer l'élément <details>

Un cas d'utilisation typique où vous pouvez utiliser ce type d'interpolation est d'animer un widget de divulgation ou un accordéon exclusif lorsqu'il s'ouvre. En HTML, vous utilisez l'élément <details> pour cela.

Avec interpolate-size: allow-keywords, vous pouvez aller très loin:

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

Toutefois, comme vous pouvez le constater, l'animation ne s'exécute que lorsque le widget de divulgation s'ouvre. Pour y remédier, Chrome travaille sur le pseudo ::details-content, qui sera disponible dans Chrome plus tard cette année (et qui sera abordé dans un prochain article). En combinant interpolate-size: allow-keywords et ::details-content, vous pouvez obtenir une animation dans les deux sens: