FRZRBOX

Back to home

Split text on hover

This is a step-by-step tutorial on how I created my split text hover animation on the project tiles. While I do use Ultratype to split the text with JS on the site, this post will show you how to create the same effect with just good 'ole fashioned HTML and CSS.

Preview

Here is a final preview of what we will be building

Codepen Demo

See the Pen Split Text Animation by David (@FreezerBox) on CodePen.


Concept

In order to create this effect we have to take the text of an element and split it into different span tags. The problem with this is that it wouldn't be very accessible to screen readers. To remedy this we'll use aria labels. Once our text is split, we'll use CSS custom properties to give each letter an index to reference in css. Lastly we'll add in the animation with keyframes!


1. Split the text

When splitting the text, keep add an aria-label with the content of the original text within the element. Inside of the element, we'll add span tags for each letter, keep in mind that spaces will also count. We'll also give them a class of 'letter' just to make it easier to reference.

NOTE: This pattern can be used for any element of your choosing not just a h1.

Before:

<h1>
  Split Text
  <h1></h1>
</h1>

After:

<h1 aria-label="Split Text">
  <span aria-hidden="true">
    <span class="letter">S</span>
    <span class="letter">p</span>
    <span class="letter">l</span>
    <span class="letter">i</span>
    <span class="letter">t</span>
    <span class="letter"> </span>
    <span class="letter">T</span>
    <span class="letter">e</span>
    <span class="letter">x</span>
    <span class="letter">t</span>
  </span>
</h1>

2. Setup Custom Properties

Now that we have the base of our markup setup, well add custom properties to reference in our animation. This will help to create the staggered effect in many of the split text animations.

The great thing about custom properties is that they not only work with the cascade, but can be reassigned. Therefore on each span that contains a letter, we'll add a style attribute that will be the animation-delay between each letter. Let's call it --delay.

More information on custom properties here: MDN CSS Custom Properties

Example:

<!-- Each letter will add on a 0.1s delay -->
<h1 aria-label="Split Text">
  <span aria-hidden="true">
    <span class="letter" style="--delay: 0s">S</span>
    <span class="letter" style="--delay: 0.1s">p</span>
    <span class="letter" style="--delay: 0.2s">l</span>
    <span class="letter" style="--delay: 0.3s">i</span>
    <span class="letter" style="--delay: 0.4s">t</span>
    <span class="letter" style="--delay: 0.5s"> </span>
    <span class="letter" style="--delay: 0.6s">T</span>
    <span class="letter" style="--delay: 0.7s">e</span>
    <span class="letter" style="--delay: 0.8s">x</span>
    <span class="letter" style="--delay: 0.9s">t</span>
  </span>
</h1>

3. Animate

In the CSS let's define a couple custom properties that we will reference in our animation. The first will be the ease, followed by the colors that our letters will animate to.

:root {
  --ease: cubic-bezier(0.47, 0, 0.75, 0.72);
  --color-primary-accent: #f5cd79;
  --color-secondary-accent: #546de5;
  --color-tertiary-accent: #e15f41;
}

 

Next we'll add in some base styles to center our animation on page. Also a very important part is to add display: inline-block; to our letters. This will make sure that they are animatable.

NOTE: The body styling is only for presentation, this will work in any layout that you choose.

body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  height: 100vh;
  width: 100%;
  display: grid;
  place-items: center;
}

h1 .letter {
  display: inline-block;
  font-family: Karla, sans-serif;
  font-weight: 700;
  font-size: 60px;
}

 

Let's add our keyframe animation, this is what I used for my site, but any animation could be added here.

@keyframes fromLeft {
  0% {
    transform: skewX(0deg);
  }
  25% {
    color: var(--color-primary-accent);
  }
  50% {
    color: var(--color-tertiary-accent);
  }
  60% {
    transform: skewX(20deg);
  }
  75% {
    color: var(--color-secondary-accent);
  }
  80% {
    transform: skewX(-10deg);
  }
  100% {
    transform: skewX(0deg);
  }
}

 

All that's left to do is add the animation to our letters. For this example we will run the animation on hover.

h1:hover .letter {
  animation: fromLeft 0.4s var(--delay) var(--ease) both;
  transform-origin: bottom center;
}

 

Your final stylesheet should look something like this

:root {
  --ease: cubic-bezier(0.47, 0, 0.75, 0.72);
  --color-primary-accent: #f5cd79;
  --color-secondary-accent: #546de5;
  --color-tertiary-accent: #e15f41;
}

body {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  height: 100vh;
  width: 100%;
  display: grid;
  place-items: center;
}

h1 .letter {
  display: inline-block;
  font-family: Karla, sans-serif;
  font-weight: 700;
  font-size: 60px;
}

h1:hover .letter {
  animation: fromLeft 0.4s var(--delay) var(--ease) both;
  transform-origin: bottom center;
}

@keyframes fromLeft {
  0% {
    transform: skewX(0deg);
  }
  25% {
    color: var(--color-primary-accent);
  }
  50% {
    color: var(--color-tertiary-accent);
  }
  60% {
    transform: skewX(20deg);
  }
  75% {
    color: var(--color-secondary-accent);
  }
  80% {
    transform: skewX(-10deg);
  }
  100% {
    transform: skewX(0deg);
  }
}

Now your animation should be up and running. Like I said earlier, this pattern can be used thoroughout your website on links, button, etc. Keeping it flexible with custom-properties and CSS Keyframes makes it easy to quickly make inclusive split text animations.

NOTE: If you feel like that was a little bit too much markup, I also made a small library called Ultratype that will handle all of the heavy-lifting for you. Although if you are just gonna animate one or two things, I would suggest manually splitting the text to eliminate the need for extra JS. Thanks for reading!