Ga naar hoofdinhoud

Monday Snack: Animating display: none

by Carl — Jun 15, 2026
2 minutes

When you use a control flow statement like @if or a structural directive like *ngIf, the element instantly appears and disappears from the DOM. Not always a stylish solution.

But what if you want it to enter and leave with a nice animation?

Then you can write extra CSS to hide it initially, and after a microsecond add a class to the element to start an animation to gracefully show the element and add component logic just to stall the DOM removal until the CSS transition finishes.

But there is another way. Actually, it was already in the Official Baseline since August 2024.

The css way: allow-discrete and @starting-style

To smoothly animate an element being inserted or removed from the DOM, we need two CSS pieces working together:

  • @starting-style: This defines the "before" state of an element when it first renders in the DOM so it can animate into view.
  • transition-behavior: allow-discrete;: This tells the browser to delay switching the display property (from block to none) until your visual transitions (like opacity or scale) have actually finished.

Clean, pure CSS

Here is how beautifully simple this is now. No complex JavaScript lifecycle hooks, animation modules, or state timers are required.

Template

@if (isCardVisible()) {
  <div class="card">
    <p>This is nice!</p>
  </div>
}
html

CSS

.card {
  /* 1. Define the normal, visible state */
  display: block;
  opacity: 1;
  transform: scale(1);
  
  /* 2. Transition visual properties AND display */
  transition: 
    opacity 0.3s ease, 
    transform 0.3s ease, 
    display 0.3s ease;
  
  /* 3. The magic keyword that delays display: none */
  transition-behavior: allow-discrete;
}

/* 4. The target exit state (when Angular/the browser applies display: none) */
.card[hidden], .card.ng-hide { 
  /* Note: With @if, the element is deleted entirely, so the exit animation 
     actually looks at the properties defined right here on the class when 
     paired with transition-behavior! */
  opacity: 0;
  transform: scale(0.95);
}

/* 5. The starting state when Angular inserts it into the DOM */
@starting-style {
  .card {
    opacity: 0;
    transform: scale(0.95);
  }
}
css

Check out on CodePen


Why this matters

By offloading layout animations to native CSS:

  • Your TypeScript files stay focused purely on business logic and state management.
  • The browser handles these transitions natively on the compositing thread.
  • Leveraging baseline web standards means less reliance on framework-specific animation packages that could change over time.

In real-world production apps, maintainability is everything. Every line of JavaScript or TypeScript you write to manage UI states is a line you have to test, debug, and maintain.

Next time you are building a toggled sidebar, a modal, or a dynamic alert banner, skip the complex setups. Give @starting-style a shot.

Have fun!