Transition des entrées de la couche supérieure et la propriété Display dans CSS
@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: none
alors 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
, visibility
et 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: none
il 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-style
mais 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 addButton
un élément de liste vide est créé à l’intérieur de la liste non ordonnée. Lorsque vous cliquez sur le removeButton
le 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.
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: none
et 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: none
et 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-behavior
il 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.
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 leurz-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 ::backdrop
nous 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.
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.
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: none
la 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.
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-mode
S 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.
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.

(GG, YK)
Source link