Waving Text Buttons: A Simple CSS Animation Tutorial

Creating a CSS button with a waving text effect can add a playful and engaging element to your website. This type of effect can make your calls-to-action stand out, encouraging users to interact more with your content. In this blog post, we’re going to walk through the process of creating such a button, step by step.

Step1: HTML Structure For Button

This code snippet crafts a button designed for both visual appeal and accessibility; it features a layered structure for aesthetic elements like outlines and shadows, while incorporating a hidden accessible text label for screen readers, alongside a container for visually-styled text characters.

<button class="button" type="button">
<span class="button-outline"> 
<span class="button-shadow">
<span class="button-inside">
<span class="button-text visually-hidden">Super Mario Bros. Wonder</span>
<span class="button-text-characters-container" aria-hidden="true"></span>
</span>
</span>
</span>
</button>

Step2: Styling With CSS

This CSS code styles a button to create dynamic visual effects, including a beveled outline, shadow layer, and an interactive inner layer that changes upon hover or focus, alongside a background animation and character jump animations for engaging user interaction.

@font-face {
src: url("https://assets.codepen.io/4175254/DIN2014-DemiBold.ttf") format("truetype");
font-family: 'DIN 2014';
font-weight: 600;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
min-height: 100dvh;
background-color: #1c1c1c !important;
}

.button {
--bevel: 3px;
--border-width: 3px;
font-family: 'DIN 2014';
font-weight: 600;
color: #1d2119;
filter: drop-shadow(1px 1px 1px rgba(0, 0, 0, 0.95));
min-width: 10em;
}

.button-outline {
--bevel-1: calc((var(--bevel) + (var(--border-width)) * 2) - ((var(--border-width) * 0.41421)) * 2);
--bevel-2: calc(var(--bevel-1) + var(--border-width));
--bevel-3: calc(var(--bevel-2) + var(--border-width));
display: block;
margin-top: calc(var(--border-width) * -1);
margin-left: calc(var(--border-width) * -1);
padding: var(--border-width);
background-color: #fff;
clip-path: polygon(var(--bevel-2) var(--border-width), calc(100% - var(--bevel-2)) var(--border-width), 100% var(--bevel-3), 100% calc(100% - var(--bevel-1)), calc(100% - var(--bevel-1)) 100%, var(--bevel-3) 100%, var(--border-width) calc(100% - var(--bevel-2)), var(--border-width) var(--bevel-2));
transition-property: clip-path;
transition-duration: .2s;
}
.button:hover:not(:active) .button-outline, .button:focus-visible:not(:active) .button-outline {
clip-path: polygon(var(--bevel-1) 0, calc(100% - var(--bevel-3)) 0, 100% var(--bevel-3), 100% calc(100% - var(--bevel-1)), calc(100% - var(--bevel-1)) 100%, var(--bevel-3) 100%, 0 calc(100% - var(--bevel-3)), 0 var(--bevel-1));
}

.button-shadow {
--padding: calc(var(--border-width) * 2);
--bevel-1: calc((var(--bevel) + var(--border-width)) - (var(--border-width) * 0.41421));
--bevel-2: calc(var(--bevel-1) + var(--border-width));
--bevel-3: calc(var(--bevel-2) + var(--border-width));
display: block;
padding: calc(var(--border-width) * 2) var(--padding) var(--padding) calc(var(--border-width) * 2);
background-color: #1d2119;
clip-path: polygon(var(--bevel-2) var(--border-width), calc(100% - var(--bevel-2)) var(--border-width), 100% var(--bevel-3), 100% calc(100% - var(--bevel-1)), calc(100% - var(--bevel-1)) 100%, var(--bevel-3) 100%, var(--border-width) calc(100% - var(--bevel-2)), var(--border-width) var(--bevel-2));
transition-property: clip-path;
transition-duration: .2s;
}
.button:hover:not(:active) .button-shadow, .button:focus-visible:not(:active) .button-shadow {
clip-path: polygon(var(--bevel-1) 0, calc(100% - var(--bevel-3)) 0, 100% var(--bevel-3), 100% calc(100% - var(--bevel-1)), calc(100% - var(--bevel-1)) 100%, var(--bevel-3) 100%, 0 calc(100% - var(--bevel-3)), 0 var(--bevel-1));
}

.button-inside {
--padding-vertical: 6px;
display: block;
padding: var(--padding-vertical) 24px calc(var(--padding-vertical) - .125em);
background-color: #fff;
clip-path: polygon(var(--bevel) 0, calc(100% - var(--bevel)) 0, 100% var(--bevel), 100% calc(100% - var(--bevel)), calc(100% - var(--bevel)) 100%, var(--bevel) 100%, 0 calc(100% - var(--bevel)), 0 var(--bevel));
text-align: center;
transition-property: transform;
transition-duration: .2s;
}
.button:hover:not(:active) .button-inside, .button:focus-visible:not(:active) .button-inside {
transform: translate(calc(var(--border-width) * -1), calc(var(--border-width) * -1));
}
.button:hover .button-inside, .button:focus-visible .button-inside {
background-color: #fcd200;
background-image:linear-gradient(to right, rgba(0, 0, 0, 0), rgba(36, 208, 192, 0.88)), radial-gradient(#9d7474 1px, rgba(0, 0, 0, 0) 0%), radial-gradient(#4b5dbd 1px, rgba(0, 0, 0, 0) 0%);
background-size: auto, 6px 6px, 6px 6px;
background-position: 0 0, 0 0, 3px 3px;
animation: scroll-background 1s linear infinite;
}

@keyframes scroll-background {
to {
background-position-x: 0, -6px, -3px;
}
}
.button-text-characters-container {
display: inline-block;
transform: skewX(-6deg);
}

.button-text-character {
display: inline-block;
}
.button:hover:not(:active) .button-text-character, .button:focus-visible:not(:active) .button-text-character {
animation: jump 4s cubic-bezier(0.75, 0.25, 1, 2) var(--delay) infinite;
}

@keyframes jump {
5% {
transform: translateY(-0.125em);
}
10% {
transform: translateY(0);
}
}

Step3: Text Animation for a Button

This JavaScript code splits the button’s text content into individual characters, wraps each non-whitespace character in a span with a unique animation delay, and then appends these spans to create a visually engaging animated text effect within the button.

const buttonText = document.querySelector('.button-text');
const buttonTextCharactersContainer = document.querySelector('.button-text-characters-container');
const buttonTextCharacters = buttonText.textContent.split('');
const characterCountWithoutWhitespaces = buttonTextCharacters.filter(character => character => !/\s/.test(character)).length;
const buttonTextCharactersFragment = document.createDocumentFragment();
let characterIndex = 1;
buttonTextCharacters.forEach(character => {
const span = document.createElement('span');
span.textContent = character;
if (!/\s/.test(character)) {
span.classList.add('button-text-character');
const delay = `calc(2s / ${characterCountWithoutWhitespaces} * ${characterIndex} + 1s)`;
span.style.setProperty('--delay', delay);
characterIndex++;
}
buttonTextCharactersFragment.appendChild(span);
});
buttonTextCharactersContainer.appendChild(buttonTextCharactersFragment);

Waving Text Buttons: A Simple CSS Animation Tutorial Demo


Adding a waving text effect to a button with CSS is a fun way to enhance the visual appeal of your website and make your buttons more engaging. By following these steps, you can easily incorporate this effect into your own projects.

You Might Be Interested In:

Ashfaq Ahmed is a freelance WordPress developer and owner of codeconvey.com. I developed modern and highly interactive websites. I always focus on improving my development and design skills.

Leave a Comment