<h1>My <span class="type-animation animating">Excellent</span> Website</h1>
<button>Retype</button>
// Learn more:
// @link https://dev.to/5t3ph/pure-css-typing-animation-1nld

// "Type" width
// - controls how much the word container expands when each letter is "typed"
// - will need adjusted depending on word and typeface
$chWidth: 0.49em;
// Word length
$chCount: 9;
// Typing animation length
$typeDuration: 180ms * $chCount;

$color: slateblue;
$bg: #f7f3ff;
$cursor: scale-color($color, $lightness: -60%);

body {
  display: grid;
  place-items: center;
  min-height: 100vh;
  background-color: $bg;
  color: $color;
  font-family: Rosario, sans-serif;
}

h1 {
  font-size: 4.5rem;
  text-align: center;
}

.type-animation {
  // Required for keeping expected alignment
  display: inline-flex;
  // Start out by hiding via width and overflow
  width: 0;
  overflow: hidden;
  // Leave room for cursor!
  padding-right: 0.08em;
  position: relative;

  &:after {
    content: "";
    background: $bg;
    position: absolute;
    right: 0;
    top: -0.05em;
    bottom: -0.05em;
    width: 0.08em;
    border-right: 2px solid transparent;
  }

  &.animating {
    animation: type $typeDuration;
    // "stick" to last frame
    animation-fill-mode: forwards;
    animation-delay: 1s;

    &:after {
      $cursorLoopCount: ((($chCount + 1) * 180)/320) + 3;
      animation: cursor 320ms $cursorLoopCount ease-in-out;
    }
  }
}

@keyframes cursor {
  0%,
  100% {
    border-color: $bg;
  }
  50% {
    border-color: $cursor;
  }
  100% {
    width: 0;
  }
}

$frameSize: 100 / $chCount;
@keyframes type {
  @for $ch from 1 to $chCount {
    $frame: $ch * $frameSize;
    #{$frame}% {
      width: $ch * $chWidth;
    }
  }
  100% {
    width: ($chCount - 1) * $chWidth;
    padding-right: 0;
  }
}

button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  padding: 0 0.75em;
  background: transparent;
  border-radius: 4px;
  color: inherit;
  border: 2px solid currentcolor;
  min-height: 34px;
  cursor: pointer;
  font-weight: bold;
  font-size: 18px;

  &:hover,
  &:focus {
    background-color: scale-color($bg, $lightness: -5%);
  }
}
View Compiled
// Not required for animation, just provides "Retype" button functionality

// Animation restart method from CSS-Tricks:
// @link https://css-tricks.com/restart-css-animation/

const button = document.querySelector("button");
const word = document.querySelector("h1 span");

// reset the transition by...
button.addEventListener(
  "click",
  function (e) {
    e.preventDefault;

    // -> removing the class
    word.classList.remove("animating");

    // -> triggering reflow /* The actual magic */
    void word.offsetWidth;

    // -> and re-adding the class
    word.classList.add("animating");
  },
  false
);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.