Animating the height of an element between 0 and the automatically calculated size is not a straightforward task. However, it seems that a recent update to browsers has made it possible to use a CSS Grid approach.
Using this method, I implemented the directive as an easy-to-use component within an Angular application. The following sample code is fully functional, so please try it out. Feel free to incorporate it into your project if you wish.
Expandable
Directive
The Expandable
directive applies styles to the host element it is assigned to. As mentioned in the previous article, for the element that serves as the container for the expansion panel, you should include display: grid
and grid-template-rows
in the styling. This allows for animating changes in the grid structure using transition-property: grid-template-rows
. You can use any values for duration
and timing-function
.
When applying styles using directives, you can simply pass an object to the style
property through host binding. You can apply styles collectively without using features like ngStyle
or [style.xxx]
.
@Directive({
selector: '[expandable]',
standalone: true,
})
export class Expandable {
@Input({ alias: 'expandable' })
isExpanded = false;
@HostBinding('style')
get styles() {
return {
display: 'grid',
'transition-property': 'grid-template-rows',
'transition-duration': '250ms',
'transition-timing-function': 'ease',
'grid-template-rows': this.isExpanded ? '1fr' : '0fr',
};
}
}
Usage
Apply the Expandable
directive to any container element and add the overflow: hidden
style to its immediate child elements. This will hide the overflowing content when the height of the grid becomes 0fr
.
@Component({
selector: 'app-root',
standalone: true,
imports: [Expandable],
template: `
<h1>Expansion with grid-template-rows</h1>
<button (click)="toggle()">toggle</button>
<div [expandable]="isExpanded()" style="border: 1px solid black;">
<div style="overflow: hidden;">
<p>
Lorem ipsum dolor sit amet, ...
</p>
</div>
</div>
`,
})
export class App {
isExpanded = signal(false);
toggle() {
this.isExpanded.update((v) => !v);
}
}
Thoughts
Angular has its animation feature, but I think CSS alone is sufficient for this expansion panel use case. It is a highly versatile mechanism and its implementation is not difficult, so I felt it is a technique that I want to actively use. (In the first place, it would be great if we could animate with height: auto
.)