<div id="output"></div>
@keyframes wiggle {
  from { transform: translateY(-0.25ex); }
  to   { transform: translateY(0.25ex); }
}

#output > span {
  animation-name: wiggle;
  animation-duration: 1s;
  animation-iteration-count: infinite;
  animation-timing-function: ease-in-out;
  animation-direction: alternate;

  /* Inline content isn't `transform`able, switch to something that is */
  display: inline-block;

  /* Preserve whitespace characters */
  white-space: pre;
}



/* The rest of this isn't relevant to squiggling */
:root {
  font-family:'Segoe UI', 'Helvetica Neue', Tahoma, Geneva, Verdana, sans-serif;
}

body {
  width: 100svw;
  height: 100svh;
  margin: 0;
  display: flex;
  flex-direction: column;
  place-content: center;
  place-items: center;
}
#output {
  font-size: max(24px, 4vmax);
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
  }
}
const SOURCE_TEXT = 'lorem ipsum'

// Split the text into characters and create a `span` element for each one
const $characters = []
SOURCE_TEXT.split('').forEach((char, idx) => {
  const $char = document.createElement('span')
  $char.textContent = char
  // Set an animation delay based on the index. This is how I get the staggered
  // animation effect: I'm using a negative value here so that the animation
  // starts in a running state immediately; a positive value would cause the
  // animation to pause for a bit before starting
  $char.style.setProperty('animation-delay', `${idx * -150}ms`)
  $characters.push($char)
})

// Insert all those spans into the output node
document.getElementById('output')
  .replaceChildren(...$characters)

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.