Skip to content

## Bug: ease-typewriter-loop caret visibly jumps at the end of each animation cycle #1378

@Pcmhacker-piro

Description

@Pcmhacker-piro

Bug: ease-typewriter-loop caret visibly jumps at the end of each animation cycle

Description

The ease-typewriter-loop animation shows a visible blink/jump at the moment the animation resets from 100% to 0%. The typing cursor (caret) abruptly disappears and reappears at the start, and the typed text flashes briefly before retyping. This creates a jarring visual break at the end of each loop iteration.

Steps to Reproduce

  1. Link EaseMotion CSS v1.0.0
  2. Add a typewriter element:
    <h1 class="ease-typewriter-loop">Hello World</h1>
  3. Watch the full animation cycle and observe the moment the text finishes typing and resets

Expected Behavior

The typewriter loop should smoothly transition between cycles — either by backspacing the text before retyping, or by having a brief pause with the full text visible before a clean reset, without any visible flash or jump.

Actual Behavior

At the end of each iteration, the steps() timing function causes the typed text to immediately snap from fully visible to completely empty. The caret (blinking cursor) disappears for one frame and reappears at the starting position. The transition is instant rather than smooth, and the visual flash is clearly noticeable, especially on dark backgrounds.

Implementation Hints

The issue is in core/animations.css where the typewriter keyframes are defined:

/* Current implementation */
.ease-typewriter-loop {
  overflow: hidden;
  white-space: nowrap;
  border-right: 2px solid currentColor;
  width: 0;
  animation: ease-typewriter 4s steps(40) infinite;
}

@keyframes ease-typewriter {
  0% {
    width: 0;
  }
  100% {
    width: 100%;
  }
}

The fix should split the animation into multiple keyframe stages — typing, pause, deletion, pause — for a smoother loop:

/* Fixed implementation with smooth loop cycle */
.ease-typewriter-loop {
  overflow: hidden;
  white-space: nowrap;
  border-right: 2px solid currentColor;
  width: 0;
  animation: ease-typewriter 6s steps(60) infinite;
}

@keyframes ease-typewriter {
  0% {
    width: 0;
  }
  40% {
    width: 100%;  /* Typing phase */
  }
  50% {
    width: 100%;  /* Pause with full text visible */
    border-color: currentColor;
  }
  60% {
    border-color: transparent; /* Blink caret off before deletion */
  }
  90% {
    width: 0;     /* Deletion phase (reverse typing) */
    border-color: transparent;
  }
  100% {
    width: 0;     /* Pause before restart */
    border-color: currentColor;
  }
}

Alternatively, for a true smooth loop, animate in both directions:

@keyframes ease-typewriter-smooth {
  0% {
    width: 0;
    border-color: currentColor;
  }
  35% {
    width: 100%;
    border-color: currentColor;
  }
  45% {
    width: 100%;
    border-color: transparent;  /* Blink */
  }
  46% {
    border-color: currentColor;
  }
  50% {
    width: 100%;
    border-color: currentColor;
  }
  85% {
    width: 0;
    border-color: currentColor;
  }
  100% {
    width: 0;
    border-color: currentColor;
  }
}

Using animation-direction: alternate with steps() can also create a smoother back-and-forth typewriter loop without the hard reset.

Affected Files

  • core/animations.css.ease-typewriter-loop keyframes timing and reset behavior

Labels

type:bug, level:intermediate, GSSoC-26

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions