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.
