Solving Animation Layout Flickering Caused by CSS Transitions
Tags: css, webdev, a11y, animation
Published: 2023-02-06 13:11:52

---
title: Solving Animation Layout Flickering Caused by CSS Transitions
published: true
date: 2023-02-04 17:12:58 UTC
tags: css, webdev, a11y, animation
canonical_url: https://stevenwoodson.com/blog/solving-animation-layout-flickering-caused-by-css-transitions
---

I recently had the opportunity to redo the header section of an inherited codebase. Everything was going great, my version was nearly 30% less code, more accessible with larger clickable targets and focus handling, and life was good.

But then it happened…

## The Layout Flickering Problem

As I’m testing in a few browsers I noticed the mobile version of the header menu was flickering closed when resizing from the desktop to the mobile breakpoint. A quick debug later and I realized it was because of the `transition` CSS property I have set on the menu so it’ll slide out and fade in when opened.

Specifically, that transition is triggered when going from desktop to mobile because I’m visually hiding it once it gets to a point where it needs to be opened with a button. When those styles are applied, the transition animation triggers.

### Before Example

Here’s a live example of the CSS animation flashing I’m talking about:

{% codepen https://codepen.io/stevenwoodson/pen/abjGjrr %}

I didn’t want to remove the CSS transition, but I also didn’t want this weird animation flashing on resize, so what do I do?

## The Prevent Animation Transition Solution

I wasn’t very happy with any of the solutions I found out there, they were some variation of:

- really heavy-handed, like removing all transitions while the browser is resized
- relied on fragile solutions like `setTimeout`
- “just don’t use animation 🤷‍♀️”

So I got to work. The obvious problem was the `transition` applying when adding the mobile-only styles so there’s gotta be a way to prevent just that one use case, right? You bet there is, and it’s only two small changes!

### Isolate `transition` Into a Unique Class

First, we need to isolate the transition separately from the styles that are applied when the menu is opened. In my case it was a new class called `nav-opening`:

```
.nav-opening {
  transition: all 0.5s ease;
}
```

### Add & Remove the Transition

Second, we need to add the transition-only `nav-opening` class when adding the open class `nav-opened`. This happens on click of the mobile menu button:

```
this.el.classList.add('nav-opening');
this.el.classList.add('nav-opened');
```

And then we need to remove `nav-opening` when the animation is done closing, for this I turned to `transitionend`. That way I don’t have to guess with something like `setTimeout`, I trigger the removal of that class when I know the animation is done. Like this:

```
this.el.addEventListener('transitionend', this.setFocus.bind(this));

SlideOutMenu.prototype.setFocus = function (e) {
  if (e.target == this.el && e.propertyName == 'opacity') {
    const compStyles = window.getComputedStyle(this.el);
    if (compStyles.opacity == 1) {
      this.firstTabStop.focus();
    } else {
      this.openButton.focus();
      this.el.classList.remove('nav-opening');
    }
  }
};
```

The `setFocus` function here is checking that the animation `transitionend` happened on the element we care about, and only triggering for the opacity transition. The latter is just in case you have multiple transitions happening (opacity, left, visibility, etc.), you don’t want this running for all of them every time.

### After Example

Here’s the same example with the above modifications applied, no more layout flickering on resize!

{% codepen https://codepen.io/stevenwoodson/pen/KKBJYKE %}

## Wrapping Up

The code examples in CodePen were intentionally kept lean on styles and uses vanilla JavaScript in the hopes that you can more easily make use of it in your project. Be sure to copy from the After example, otherwise I wrote this whole blog post for nothing!

I wanted to also note that, while a bit too much for my use case, I do really like this [Stop Animations During Window Resizing](https://css-tricks.com/stop-animations-during-window-resizing/) example that Chris worked up. If you have a lot going on transitions-wise this may be a cleaner solution for you than having to manage each piece individually.

### Further Reading

- [Menus & Menu Buttons](https://inclusive-components.design/menus-menu-buttons/) – Inclusive Components by Heydon Pickering
- [CSS-Only Accessible Dropdown Navigation Menu](https://moderncss.dev/css-only-accessible-dropdown-navigation-menu/) – Stephanie Eckles
- [An Interactive Guide to CSS Transitions](https://www.joshwcomeau.com/animation/css-transitions/) – Josh W Comeau