Disable CSS animation on pseudo element inherited from parent

As the pseudo-element is a child element of the parent it will continue to get rotated as long as parent has the animation. Even setting animation: none on the pseudo element will have no effect.

The only way to make it look as though the child has no animation is to reverse the effect like shown in below snippet. What is being done is that the very same animation is added to the pseudo element but the animation-direction is set as reverse. This means that the pseudo get the exact reverse transform effect and thus would retain it in the same position.

.spinner {
  height: 50px;
  width: 50px;
  position: relative;
  animation: rotation .6s infinite linear;
  border-left: 6px solid #222;
  border-right: 6px solid #222;
  border-bottom: 6px solid #222;
  border-top: 6px solid #ccc;
  border-radius: 100%;
}
.spinner:after {
  position: absolute;
  content: 'Loading..';
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
  animation: rotation .6s infinite linear reverse; /* added this line */
}
@keyframes rotation {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(359deg);
  }
}
<div class="spinner"></div>

The above snippet uses the default setting for transform-origin which is 50% 50% but if the child pseudo-element has padding and/or margin then the transform-origin setting has to be adjusted accordingly to avoid the pseudo-element from producing a shivering like effect. The calculation logic is provided in the below snippet.

.spinner {
  height: 50px;
  width: 50px;
  position: relative;
  animation: rotation .6s infinite linear;
  border-left: 6px solid #222;
  border-right: 6px solid #222;
  border-bottom: 6px solid #222;
  border-top: 6px solid #ccc;
  border-radius: 100%;
}
.spinner.parent-padded-margin {
  padding: 10px;
  margin: 10px;
}
.spinner:after {
  position: absolute;
  content: 'Loading..';
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
  animation: rotation .6s infinite linear reverse;
  /* added this line */
}
.spinner.child-padded-margin:after {
  padding: 10px 8px;
  margin: 5px 4px;
  transform-origin: calc(50% - 12px) calc(50% - 15px);  /* calc(50% - ((padding-left + padding-right)/2 + margin-left)) calc(50% - ((padding-top + padding-bottom)/2 + margin-top)) */
}
.spinner.child-padded-margin-2:after {
  padding: 10px 6px 16px 14px;
  margin: 7px 12px 5px 10px;
  transform-origin: calc(50% - 20px) calc(50% - 20px);  /* calc(50% - ((padding-left + padding-right)/2 + margin-left)) calc(50% - ((padding-top + padding-bottom)/2 + margin-top)) */
}
@keyframes rotation {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(359deg);
  }
}
<div class="spinner"></div>
<div class="spinner parent-padded-margin"></div>
<div class="spinner child-padded-margin"></div>
<div class="spinner child-padded-margin-2"></div>

Positioning the pseudo-element (using top,left,bottom,right ) also has affects the animation. It would also require the transform-origin to be modified accordinly in-order for the animation to work properly. A sample is available in the below snippet.

.spinner {
  height: 50px;
  width: 50px;
  position: relative;
  animation: rotation .6s infinite linear;
  border-left: 6px solid #222;
  border-right: 6px solid #222;
  border-bottom: 6px solid #222;
  border-top: 6px solid #ccc;
  border-radius: 100%;
}
.spinner.parent-padded-margin {
  padding: 10px;
  margin: 10px;
}
.spinner:after {
  position: absolute;
  content: 'Loading..';
  height: 100%;
  width: 100%;
  animation: rotation .6s infinite linear reverse;  /* added this line */
}
.spinner.child-positioned{
    margin-bottom: 40px;
  }
.spinner.child-positioned:after {
  top: 120%;
  left: 2%;
  transform-origin: calc(50% - 2%) calc(50% - 120%); /* basically need to subtract the distance from the left and top of the container */
}
.spinner.child-positioned-negative:after {
  bottom: -120%;
  right: -2%;
  transform-origin: calc(50% - 2%) calc(50% - 120%); /* basically need to subtract the distance from the left and top of the container */
}
@keyframes rotation {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(359deg);
  }
}
<div class="spinner child-positioned"></div>
<div class="spinner child-positioned-negative"></div>

Note: Both the above solutions work perfectly fine in latest versions of Chrome, Opera and Safari but are causing the text to have a slanted appearance in IE 11, Edge and Firefox. Firefox seems to require a separate animation which goes from rotate(-10deg) to rotate(-370deg) for FF while it gets more complex in IE.


The only alternate without setting the reverse animation on pseudo (child) element would be to make use of the method mentioned by Chris in his comment. That would mean setting borders and the animation directly to the pseudo element. This would mean that the parent’s contents would remain unaffected as the parent won’t get affected by a transform on the child.

.spinner {
  height: 50px;
  width: 50px;
  position: relative;
}
.spinner:after {
  position: absolute;
  content: '';
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
  animation: rotation .6s infinite linear;
  border-left: 6px solid #222;
  border-right: 6px solid #222;
  border-bottom: 6px solid #222;
  border-top: 6px solid #ccc;
  border-radius: 100%;
}
@keyframes rotation {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(359deg);
  }
}
<div class="spinner">Loading...</div>

Leave a Comment