From initial analysis we can see the animation framework is being deprecated to switch over to CSS based animations:, looking at the error.
@deprecated — 20.2 Use animate.enter or animate.leave instead. Intent to remove in v23
We can find this deprecation notice, which explains the reason for the deprecation.
The @angular/animations package is deprecated as of v20.2, which also introduced the new animate.enter and animate.leave feature to add animations to your application. Using these new features, you can replace all animations based on @angular/animations with plain CSS or JS animation libraries. Removing @angular/animations from your application can significantly reduce the size of your JavaScript bundle. Native CSS animations generally offer superior performance, as they can benefit from hardware acceleration. This guide walks through the process of refactoring your code from @angular/animations to native CSS animations.
Migrating away from Angular's Animations package
We can also find the deprecation notice on the animation providers:
Deprecation warning
Use animate.enter or animate.leave instead. Intent to remove in v23
provideAnimationsAsync
provideAnimations
provideNoopAnimations
We will no longer need to use provideAnimations or provideAnimationsAsync to the bootstrapApplication or environment providers.
bootstrapApplication(App, {
providers: [
// provideAnimations() // no longer needed!
// provideAnimationsAsync() // no longer needed!
],
});
Instead there is tutorial for using animations going forward on the official site.
Animating your applications with animate.enter and animate.leave
Below is a description of the enter and leave:
Angular provides animate.enter and animate.leave to animate your application's elements. These two features apply enter and leave CSS classes at the appropriate times or call functions to apply animations from third party libraries. animate.enter and animate.leave are not directives. They are special API supported directly by the Angular compiler. They can be used on elements directly and can also be used as a host binding.
Conversion to new animation style:
I tried converting your example to the animation style. Below are my findings:
When creating the animate.open animation (Things to keep in mind):
We should declare the open (:enter) using keyframes (I was only able to get this working). In the below CSS we can see when open class is added we add animation: slide-fade 1s; to animate the slide-fade which contains the keyframes animation.
.open {
animation: slide-fade 1s;
}
@keyframes slide-fade {
from {
opacity: 0;
transform: translateY(-100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Same But with height transition instead of transform:
.open {
animation: slide-fade 300ms;
}
@keyframes slide-fade {
from {
max-height:0px;
}
to {
max-height:1000px;
}
}
When creating the animate.leave animation (Things to keep in mind):
We should declare the leave (:leave) using CSS transitions. In the below CSS we can see when closed class is added we add the @starting-style to define the initial state of the transition. We can also emit this step and the animations should work fine, when I tried it (I may be wrong).
@starting-style {
opacity: 0;
transform: translateY(-100%);
}
Same but with height transition instead of transform:
@starting-style {
max-height:1000px;
}
Then we define the CSS to first toggle opacity and then transform to the required position. So the final CSS will be:
.animate-item {
margin: 0px;
border: 1px solid #dddddd;
margin-top: 1em;
padding: 20px 20px 0px 20px;
position: relative;
@starting-style {
opacity: 0;
transform: translateY(-100%);
}
}
.closed {
opacity: 0;
transform: translateY(-100%);
transition: opacity 500ms ease-out, transform 500ms ease-out;
}
Same but with height transition instead of transform:
.closed {
transition: max-height 300ms ease-out;
max-height:0;
}
So Putting it all together the final code will be:
Full Code (Using Height):
import 'zone.js';
import { Component, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
@Component({
selector: 'app-root',
standalone: true,
providers: [],
styles: `
p {
overflow: hidden;
}
.animate-item {
margin: 0px;
border: 1px solid #dddddd;
margin-top: 1em;
padding: 20px 20px 0px 20px;
position: relative;
max-height:1000px;
@starting-style {
max-height:1000px;
}
}
.closed {
transition: max-height 300ms ease-out;
max-height:0;
}
.open {
animation: slide-fade 300ms;
}
@keyframes slide-fade {
from {
max-height:0px;
}
to {
max-height:1000px;
}
}
`,
template: `
<button type="button" (click)="toggleOpen()">Toggle</button>
@if(isOpen()) {
<p class="animate-item" animate.enter="open" animate.leave="closed">
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
}
`,
animations: [],
})
export class App {
readonly isOpen = signal(false);
toggleOpen() {
this.isOpen.set(!this.isOpen());
}
}
bootstrapApplication(App, {
providers: [],
});
Full Code (Using Transform):
import 'zone.js';
import { Component, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
@Component({
selector: 'app-root',
standalone: true,
providers: [],
styles: `
p {
overflow: hidden;
}
.animate-item {
margin: 0px;
border: 1px solid #dddddd;
margin-top: 1em;
padding: 20px 20px 0px 20px;
position: relative;
@starting-style {
opacity: 0;
transform: translateY(-100%);
}
}
.closed {
opacity: 0;
transform: translateY(-100%);
transition: opacity 500ms ease-out, transform 500ms ease-out;
}
.open {
animation: slide-fade 1s;
animation-composition: add;
}
@keyframes slide-fade {
from {
opacity: 0;
transform: translateY(-100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`,
template: `
<button type="button" (click)="toggleOpen()">Toggle</button>
@if(isOpen()) {
<p class="animate-item" animate.enter="open" animate.leave="closed">
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
}
`,
animations: [],
})
export class App {
readonly isOpen = signal(false);
toggleOpen() {
this.isOpen.set(!this.isOpen());
}
}
bootstrapApplication(App, {
providers: [],
});