Fermer

janvier 29, 2025

Transition des entrées de la couche supérieure et la propriété Display dans CSS

Transition des entrées de la couche supérieure et la propriété Display dans CSS


Nous sommes gâtés avec autant de nouvelles fonctionnalités impliquant des animations avec CSS, de Animations pilotées par défilement à Voir les transitionset beaucoup de choses entre les deux. Mais ce ne sont pas toujours les grandes fonctionnalités qui facilitent notre vie quotidienne; Parfois, ce sont ces fonctionnalités de facilité de vie qui améliorent vraiment nos projets. Dans cet article, Brecht de Ruyte met deux fonctionnalités exposées: @starting-style et transition-behavior – Deux propriétés qui sont absolument les bienvenues des ajouts à votre travail quotidien avec les animations CSS.

Animer de et à display: none; Était quelque chose que nous ne pouvions réaliser qu’avec JavaScript pour modifier des classes ou créer d’autres hacks. La raison pour laquelle nous ne pouvions pas faire cela dans CSS est expliquée dans le nouveau CSS Transitions Niveau 2 Spécification:

«Au niveau 1 de cette spécification, les transitions ne peuvent commencer que lors d’un événement de changement de style pour les éléments qui ont un style défini avant le changement établi par l’événement de changement de style précédent. Cela signifie qu’une transition n’a pas pu être démarrée sur un élément qui n’était pas rendu pour l’événement de changement de style précédent. »

En termes simples, cela signifie que nous ne pouvions pas démarrer une transition sur un élément caché ou qui vient d’être créé.

Qu’est-ce que transition-behavior: allow-discrete Faire?

allow-discrete est un peu un nom étrange pour une valeur de propriété CSS, non? Nous poursuivons la transition display: nonealors pourquoi ce n’est pas nommé transition-behavior: allow-display plutôt? La raison en est que cela fait un peu plus que de gérer le CSS display propriété, car il existe d’autres propriétés «discrètes» dans CSS. Une règle de base simple est que les propriétés discrètes ne font pas de transition mais se retournent généralement immédiatement entre deux états. D’autres exemples de propriétés discrètes sont visibility et mix-blend-mode. J’en inclurai un exemple à la fin de cet article.

Pour résumer, en définissant le transition-behavior propriété allow-discrete nous permet de dire au navigateur qu’il peut échanger les valeurs d’une propriété discrète (par exemple, display, visibilityet mix-blend-mode) à la marque de 50% au lieu de la marque de 0% d’une transition.

Qu’est-ce que @starting-style Faire?

Le @starting-style La règle définit les styles d’un élément juste avant qu’il ne soit rendu à la page. Ceci est fortement nécessaire en combinaison avec transition-behavior Et c’est pourquoi:

Lorsqu’un élément est ajouté au DOM ou est initialement défini sur display: noneil a besoin d’une sorte de «style de départ» à partir de laquelle il doit faire la transition. Pour prendre l’exemple plus loin, Popouts et éléments de dialogue sont ajoutés à une couche supérieure qui est une couche qui est en dehors de votre flux de documents, vous pouvez en quelque sorte le considérer comme un frère <html> élément de la structure de votre page. Maintenant, lors de l’ouverture de cette boîte de dialogue ou de cette popover, ils sont créés à l’intérieur de cette couche supérieure, donc ils n’ont pas de styles pour commencer la transition, c’est pourquoi nous définissons @starting-style. Ne vous inquiétez pas si tout cela semble un peu déroutant. Les démos pourraient le rendre plus clairement. La chose importante à savoir est que nous pouvons donner au navigateur quelque chose pour démarrer l’animation car il n’a autrement rien à animer.

Une note sur le support du navigateur

Au moment de l’écriture, le transition-behavior est disponible en Chrome, Edge, Safari et Firefox. C’est la même chose pour @starting-stylemais Firefox ne prend actuellement pas en charge l’animation de display: none. Mais n’oubliez pas que tout dans cet article peut être parfaitement utilisé comme amélioration progressive.

Maintenant que nous avons la théorie de tout cela derrière nous, soyons pratiques. Je couvrirai trois cas d’utilisation dans cet article:

  • Animer de et à display: none Dans le dom.
  • Animer les boîtes de dialogue et les popovers entrant et sortant de la couche supérieure.
  • Plus de «propriétés discrètes» que nous pouvons gérer.

Animer de et à display: none Dans le dom

Pour le premier exemple, jetons un coup d’œil à @starting-style seul. J’ai créé cette démo uniquement pour expliquer la magie. Imaginez que vous souhaitez deux boutons sur une page pour ajouter ou supprimer les éléments de liste à l’intérieur d’une liste non ordonnée.

Cela pourrait être votre HTML de départ:

<button type="button" class="btn-add">
  Add item
</button>
<button type="button" class="btn-remove">
  Remove item
</button>
<ul role="list"></ul>

Ensuite, nous ajoutons des actions qui ajoutent ou supprimons ces éléments de liste. Cela peut être n’importe quelle méthode de votre choix, mais à des fins de démonstration, j’ai rapidement écrit un peu de javascript:

document.addEventListener("DOMContentLoaded", () => {
  const addButton = document.querySelector(".btn-add");
  const removeButton = document.querySelector(".btn-remove");
  const list = document.querySelector('ul[role="list"]');

  addButton.addEventListener("click", () => {
    const newItem = document.createElement("li");
    list.appendChild(newItem);
  });

  removeButton.addEventListener("click", () => {
    if (list.lastElementChild) {
      list.lastElementChild.classList.add("removing");
      setTimeout(() => {
        list.removeChild(list.lastElementChild);
      }, 200);
    }
  });
});

Lorsque vous cliquez sur le addButtonun élément de liste vide est créé à l’intérieur de la liste non ordonnée. Lorsque vous cliquez sur le removeButtonle dernier élément obtient un nouveau .removing classe et finalement retiré du DOM après 200 ms.

Avec cela en place, nous pouvons écrire des CSS pour nos articles pour animer la partie de suppression:

ul {
    li {
      transition: opacity 0.2s, transform 0.2s;

      &.removing {
        opacity: 0;
        transform: translate(0, 50%);
      }
    }
  }

C’est super! Notre .removing L’animation a déjà l’air parfaite, mais ce que nous recherchions ici était un moyen d’animer l’entrée des éléments à l’intérieur de notre Dom. Pour cela, nous devrons définir ces styles de départ, ainsi que l’état final de nos éléments de liste.

Tout d’abord, mettons à jour le CSS pour avoir l’état final à l’intérieur de cet élément de liste:

ul {
    li {
      opacity: 1;
      transform: translate(0, 0);
      transition: opacity 0.2s, transform 0.2s;

      &.removing {
        opacity: 0;
        transform: translate(0, 50%);
      }
    }
  }

Peu de choses ont changé, mais maintenant c’est à nous de faire savoir au navigateur quels devraient être les styles de départ. Nous pouvions régler cela de la même manière que nous avons fait le .removing styles comme ainsi:

ul {
    li {
      opacity: 1;
      transform: translate(0, 0);
      transition: opacity 0.2s, transform 0.2s;

      @starting-style {
        opacity: 0;
        transform: translate(0, 50%);
      }

      &.removing {
        opacity: 0;
        transform: translate(0, 50%);
      }
    }
  }

Maintenant, nous avons fait savoir au navigateur que le @starting-style devrait inclure une opacité zéro et être légèrement poussé vers le bas en utilisant un transform. Le résultat final est quelque chose comme ceci:

Mais nous n’avons pas besoin de nous arrêter là! Nous pourrions utiliser différentes animations pour entrer et sortir. Nous pourrions, par exemple, mettre à jour notre style de départ à ce qui suit:

@starting-style {
  opacity: 0;
  transform: translate(0, -50%);
}

En faisant cela, les éléments entreront du haut et sortent en bas. Voir l’exemple complet dans ce codep:

Voir le stylo [@starting-style demo – up-in, down-out [forked]](https://codepen.io/smashingmag/pen/xjropgg) par Utilitybend.

Voir le stylo @ Demo de style de départ – Up-in, enrouillant [forked] par Utilitybend.

Quand utiliser transition-behavior: allow-discrete

Dans l’exemple précédent, nous avons ajouté et supprimé les éléments de notre DOM. Dans la prochaine démo, nous afficherons et masquerons les éléments à l’aide du CSS display propriété. La configuration de base est à peu près la même, sauf que nous ajouterons huit éléments de liste à notre DOM avec le .hidden classe attachée à lui:

  <button type="button" class="btn-add">
    Show item
  </button>
  <button type="button" class="btn-remove">
    Hide item
  </button>

<ul role="list">
  <li class="hidden"></li>
  <li class="hidden"></li>
  <li class="hidden"></li>
  <li class="hidden"></li>
  <li class="hidden"></li>
  <li class="hidden"></li>
  <li class="hidden"></li>
  <li class="hidden"></li>
</ul>

Encore une fois, à des fins de démo, j’ai ajouté un peu de javascript que, cette fois, supprime le .hidden classe de l’élément suivant lorsque vous cliquez sur le addButton et ajoute le hidden classe de retour lorsque vous cliquez sur le removeButton:

document.addEventListener("DOMContentLoaded", () => {
  const addButton = document.querySelector(".btn-add");
  const removeButton = document.querySelector(".btn-remove");
  const listItems = document.querySelectorAll('ul[role="list"] li');

  let activeCount = 0;

  addButton.addEventListener("click", () => {
    if (activeCount < listItems.length) {
      listItems[activeCount].classList.remove("hidden");
      activeCount++;
    }
  });

  removeButton.addEventListener("click", () => {
    if (activeCount > 0) {
      activeCount--;
      listItems[activeCount].classList.add("hidden");
    }
  });
});

Assemblons tout ce que nous avons appris jusqu’à présent, ajoutez un @starting-style à nos articles et effectuez la configuration de base dans CSS:

ul {
    li {
      display: block;
      opacity: 1;
      transform: translate(0, 0);
      transition: opacity 0.2s, transform 0.2s;

      @starting-style {
        opacity: 0;
        transform: translate(0, -50%);
      }

      &.hidden {
        display: none;
        opacity: 0;
        transform: translate(0, 50%);
      }
    }
  }

Cette fois, nous avons ajouté le .hidden classe, réglez-le sur display: noneet a ajouté le même opacity et transform Déclaration comme nous l’avons fait auparavant avec le .removing classe dans le dernier exemple. Comme vous pouvez vous y attendre, nous obtenons un joli fondu pour nos articles, mais les supprimer est toujours très abrupte alors que nous définissons nos articles directement sur display: none.

C’est là que le transition-behavior la propriété entre en jeu. Pour le décomposer un peu plus, supprimons le transition Repreinte des biens de notre précédent CSS et l’ouvrez un peu:

ul {
    li {
      display: block;
      opacity: 1;
      transform: translate(0, 0);
      transition-property: opacity, transform;
      transition-duration: 0.2s;
    }
  }

Il ne reste plus qu’à faire la transition du display propriété et définissez le transition-behavior propriété allow-discrete:

ul {
    li {
      display: block;
      opacity: 1;
      transform: translate(0, 0);
      transition-property: opacity, transform, display;
      transition-duration: 0.2s;
      transition-behavior: allow-discrete;
      /* etc. */
    }
  }

Nous animons maintenant l’élément de display: noneet le résultat est exactement comme nous le voulions:

Nous pouvons utiliser le transition Raccourcissement des propriétés pour rendre notre code un peu moins verbeux:

transition: opacity 0.2s, transform 0.2s, display 0.2s allow-discrete;

Vous pouvez ajouter allow-discrete là-dedans. Mais si vous le faites, notez que si vous déclarez une transition de sténographie après transition-behavioril sera annulé. Donc, au lieu de ceci:

transition-behavior: allow-discrete;
transition: opacity 0.2s, transform 0.2s, display 0.2s;

… Nous voulons déclarer transition-behavior après le transition sténographie:

transition: opacity 0.2s, transform 0.2s, display 0.2s;
transition-behavior: allow-discrete;

Sinon, le transition Remplacement des propriétés scoris transition-behavior.

Voir le stylo [@starting-style and transition-behavior: allow-discrete [forked]](https://codepen.io/smashingmag/pen/ggkpxda) par Utilitybend.

Voir le stylo @ Style de départ et de transition-comportement: [forked] par Utilitybend.

Animer les dialogues et les popovers entrant et sortant de la couche supérieure

Ajoutons quelques cas d’utilisation avec des boîtes de dialogue et des popovers. Les dialogues et les popovers sont de bons exemples car ils sont ajoutés à la couche supérieure lors de leur ouverture.

Quelle est cette couche supérieure?

Nous avons déjà comparé la «couche supérieure» à un frère ou une sœur du <html> Élément, mais vous pourriez également le considérer comme une couche spéciale qui se trouve surtout sur une page Web. C’est comme une feuille transparente que vous pouvez placer sur un dessin. Tout ce que vous dessinez sur cette feuille sera visible sur le dessus du dessin d’origine.

Le dessin original, dans cet exemple, est le dom. Cela signifie que la couche supérieure est hors du flux de documents, ce qui nous offre quelques avantages. Par exemple, comme je l’ai déjà dit, des dialogues et des popovers sont ajoutés à cette couche supérieure, et cela est parfaitement logique car ils devraient toujours être au-dessus de tout le reste. Pas plus z-index: 9999!

Mais c’est plus que ça:

  • z-index n’est pas pertinent: Les éléments sur la couche supérieure sont toujours en haut, quelle que soit leur z-index valeur.
  • La hiérarchie DOM n’a pas d’importance: La position d’un élément dans le DOM n’affecte pas son ordre d’empilement sur la couche supérieure.
  • Fond de dos: Nous avons accès à un nouveau ::backdrop Pseudo-élément qui nous permet de coiffer la zone entre la couche supérieure et le dom en dessous.

J’espère que vous commencez à comprendre l’importance de la couche supérieure et comment nous pouvons transformer des éléments à l’intérieur et à l’extérieur comme nous le ferons avec les popovers et les dialogues.

Transition de l’élément de dialogue dans la couche supérieure

Le HTML suivant contient un bouton qui ouvre un <dialog> élément, et que <dialog> L’élément contient un autre bouton qui ferme le <dialog>. Donc, nous avons un bouton qui ouvre le <dialog> et celui qui le ferme.

<button class="open-dialog" data-target="my-modal">Show dialog</button>

<dialog id="my-modal">
  <p>Hi, there!</p>
  <button class="outline close-dialog" data-target="my-modal">
    close
  </button>
</dialog>

Beaucoup de choses se produisent en HTML avec Commandes d’invocateur Cela facilitera un peu l’étape suivante, mais pour l’instant, ajoutons un peu de javascript pour que ce modal fonctionne réellement:

// Get all open dialog buttons.
const openButtons = document.querySelectorAll(".open-dialog");
// Get all close dialog buttons.
const closeButtons = document.querySelectorAll(".close-dialog");

// Add click event listeners to open buttons.
openButtons.forEach((button) =< {
  button.addEventListener("click", () =< {
    const targetId = button.getAttribute("data-target");
    const dialog = document.getElementById(targetId);
    if (dialog) {
      dialog.showModal();
    }
  });
});

// Add click event listeners to close buttons.
closeButtons.forEach((button) =< {
  button.addEventListener("click", () =< {
    const targetId = button.getAttribute("data-target");
    const dialog = document.getElementById(targetId);
    if (dialog) {
      dialog.close();
    }
  });
});

J’utilise les styles suivants comme point de départ. Remarquez comment je stylise le ::backdrop En prime!

dialog {
  padding: 30px;
  width: 100%;
  max-width: 600px;
  background: #fff;
  border-radius: 8px;
  border: 0;
  box-shadow: 
    rgba(0, 0, 0, 0.3) 0px 19px 38px,
    rgba(0, 0, 0, 0.22) 0px 15px 12px;
    
  &::backdrop {
    background-image: linear-gradient(
      45deg in oklab,
      oklch(80% 0.4 222) 0%,
      oklch(35% 0.5 313) 100%
    );
  }
}

Il en résulte une transition assez difficile pour l’entrée, ce qui signifie qu’elle n’est pas très fluide:

Ajoutons des transitions à cet élément de dialogue et à la toile de fond. Je vais un peu plus vite cette fois parce que maintenant, vous voyez probablement le modèle et savez ce qui se passe:

dialog {
  opacity: 0;
  translate: 0 30%;
  transition-property: opacity, translate, display;
  transition-duration: 0.8s;

  transition-behavior: allow-discrete;
  
  &[open] {
    opacity: 1;
    translate: 0 0;

    @starting-style {
      opacity: 0;
      translate: 0 -30%;
    }
  }
}

Lorsqu’une boîte de dialogue est ouverte, le navigateur gifle un open attribut dessus:

<dialog open> ... </dialog>

Et c’est quelque chose d’autre que nous pouvons cibler avec CSS, comme dialog[open]. Donc, dans ce cas, nous devons définir un @starting-style car lorsque la boîte de dialogue est dans un open État.

Ajoutons une transition pour notre contexte pendant que nous y sommes:

dialog {
  /* etc. */
  &::backdrop {
    opacity: 0;
    transition-property: opacity;
    transition-duration: 1s;
  }

  &[open] {
    /* etc. */
    &::backdrop {
      opacity: 0.8;

      @starting-style {
        opacity: 0;
      }
    }
  }
}

Maintenant, vous pensez probablement: A-ha! Mais vous auriez dû ajouter le display la propriété et le transition-behavior: allow-discrete Sur le contexte!

Mais non, ce n’est pas le cas. Même si je changerais mon pseudo-élément de toile de fond au CSS suivant, le résultat resterait le même:

 &::backdrop {
    opacity: 0;
    transition-property: opacity, display;
    transition-duration: 1s;
    transition-behavior: allow-discrete;
  }

Il s’avère que nous travaillons avec un ::backdrop Et lorsque vous travaillez avec un ::backdropnous travaillons implicitement également avec le CSS overlay propriété, qui spécifie si un élément apparaissant dans la couche supérieure est actuellement rendu dans la couche supérieure.

Et overlay Il se trouve que c’est une autre propriété discrète que nous devons inclure dans le transition-property déclaration:

dialog {
  /* etc. */

&::backdrop {
  transition-property: opacity, display, overlay;
  /* etc. */
}

Malheureusement, cela n’est actuellement pris en charge que dans les navigateurs de chrome, mais il peut être parfaitement utilisé comme amélioration progressive.

Et, oui, nous devons l’ajouter au dialog Styles aussi:

dialog {
  transition-property: opacity, translate, display, overlay;
  /* etc. */

&::backdrop {
  transition-property: opacity, display, overlay;
  /* etc. */
}

Voir le stylo [Dialog: starting-style, transition-behavior, overlay [forked]](https://codepen.io/smashingmag/pen/pvzqoge) par Utilitybend.

Voir le stylo Dialogue: Style de démarrage,-comportement, superposition [forked] par Utilitybend.

C’est à peu près la même chose pour un popover au lieu d’un dialogue. J’utilise la même technique, en travaillant uniquement avec les popovers cette fois:

Voir le stylo [Popover transition with @starting-style [forked]](https://codepen.io/smashingmag/pen/emoblxe) par Utilitybend.

Voir le stylo Popover Transition avec @ Style-Style [forked] par Utilitybend.

Autres propriétés discrètes

Il y a quelques autres propriétés discrètes en plus de celles que nous avons couvertes ici. Si vous vous souvenez de la deuxième démo, où nous sommes en transition de certains articles et à display: nonela même chose peut être réalisée avec le visibility propriété à la place. Cela peut être pratique pour les cas où vous voulez que les articles préservent l’espace pour la boîte de l’élément, même s’il est invisible.

Donc, voici le même exemple, en utilisant uniquement visibility au lieu de display.

Voir le stylo [Transitioning the visibility property [forked]](https://codepen.io/smashingmag/pen/lepmjqx) par Utilitybend.

Voir le stylo Transition de la propriété de visibilité [forked] par Utilitybend.

Le CSS mix-blend-mode La propriété est une autre qui est considérée comme discrète. Pour être complètement honnête, je ne trouve pas un bon cas d’utilisation pour une démo. Mais je suis allé de l’avant et j’ai créé un exemple quelque peu banal où deux mix-blend-modeS commutant en plein milieu de la transition au lieu de tout de suite.

Voir le stylo [Transitioning mix-blend-mode [forked]](https://codepen.io/smashingmag/pen/bnboxzp) par Utilitybend.

Voir le stylo Transition en mode mélange de mélange [forked] par Utilitybend.

Emballage

C’est un aperçu de la façon dont nous pouvons transmettre les éléments à l’intérieur et à l’extérieur de la couche supérieure! Dans un monde idéal, nous pourrions nous éloigner sans avoir besoin d’une propriété complètement nouvelle comme transition-behavior Juste pour faire la transition autrement des propriétés «non transitionnables», mais nous y sommes, et je suis content que nous l’ayons.

Mais nous avons également appris sur @starting-style Et comment il fournit aux navigateurs un ensemble de styles que nous pouvons appliquer au début d’une transition pour un élément qui se trouve dans la couche supérieure. Sinon, l’élément n’a rien à passer à partir de rendu, et nous n’aurions aucun moyen de les faire passer en douceur dans et hors de la couche supérieure.

Smashing Editorial
(GG, YK)




Source link