How to Make a Stunning CTA Animation with Tailwind CSS
Tags: webdev, tailwindcss, html, tutorial
Published: 2023-10-24 07:29:21

####**[Live Demo](https://cruip-tutorials.vercel.app/cta-animation/) / [Download](https://github.com/cruip/cruip-tutorials/blob/main/cta-animation/index.html)**

The **Call To Action** is the most important element of an interface, as it enables users to turn their intentions into actions, and products to finalize a specific goal (e.g., converting a prospect to a free trial).

In all our [Tailwind templates](https://cruip.com/) you'll find at least one Call To Action. We designed them in unlimited shapes and styles, with the ultimate goal of creating a connection between products and end users.

Generally, we like to design simple and straightforward CTAs, but for this tutorial (which was inspired by [Glide](https://www.glideapps.com/)), we decided to **create a Call To Action animation using Tailwind CSS**.

We are going to build a fake button that triggers multiple animations when you hover over it:

- [Changing the section's color scheme from light to dark.](#changing-the-section-color-mode-on-hover)
- [Adding a scrolling effect to the text on the button.](#creating-the-text-scrolling-animation)
- [Highlighting the button's border with a “shine” effect.](#creating-the-shine-effect)

## Creating the base structure

Alright, let's start by creating the basic structure for our CTA. We'll use the `<section>` tag as a container and the `<a>` tag to make everything clickable.

```html
<section class="relative z-0">
  <div class="w-full max-w-5xl mx-auto px-4 md:px-6 py-48">
    <div class="text-center">
      <a
        class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-6 items-center justify-center text-3xl sm:text-4xl md:text-5xl font-semibold text-slate-900"
        href="#0"
      >
        <!-- Fake Button -->
        <span>
          <!-- Default content: "Build the UI you need" -->
          <span>Build the UI you need</span>
          <!-- Hover content: "Create beautiful user interfaces" -->
          <span
            class="before:content-['Create_beautiful_user_interfaces'] after:content-['Create_beautiful_user_interfaces']"
            aria-hidden="true"
          ></span>
        </span>

        <!-- Text: "with Cruip" -->
        <span>with Cruip</span>
      </a>
    </div>
  </div>
</section>
```

The setup is pretty simple. The only thing to highlight is that we've used pseudo-attributes `before` and `after` for the moving text. This lets us duplicate the text without creating another element.

## Changing the section color mode on hover

Before defining the rest of the style, let's see how to change the background color when the mouse hovers over it. We will use the `before` pseudo-attribute to create an overlay.

```html
<section class="relative z-0">
  <div class="w-full max-w-5xl mx-auto px-4 md:px-6 py-48">
    <div class="text-center">
      <a
        class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-6 items-center justify-center text-3xl sm:text-4xl md:text-5xl font-semibold text-slate-900 before:absolute before:inset-0 hover:before:bg-slate-900 before:-z-10 before:transition-colors before:duration-500 group"
        href="#0"
      >
        <!-- Fake Button -->
        <span>
          <!-- Default content: "Build the UI you need" -->
          <span class="group-hover:opacity-0 transition-opacity duration-500 ease-in-out">Build the UI you need</span>
          <!-- Hover content: "Create beautiful user interfaces" -->
          <span
            class="before:content-['Create_beautiful_user_interfaces'] after:content-['Create_beautiful_user_interfaces']"
            aria-hidden="true"
          ></span>
        </span>

        <!-- Text: "with Cruip" -->
        <span>with Cruip</span>
      </a>
    </div>
  </div>
</section>
```

## Creating the fake button interaction

To make a smooth transition between the default content and the hover content, we'll position the latter absolutely with opacity 0. And with Tailwind's `group-hover:` modifier, we'll switch the opacities.

```html
<section class="relative z-0">
  <div class="w-full max-w-5xl mx-auto px-4 md:px-6 py-48">
    <div class="text-center">
      <a
        class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-6 items-center justify-center text-3xl sm:text-4xl md:text-5xl font-semibold text-slate-900 before:absolute before:inset-0 hover:before:bg-slate-900 before:-z-10 before:transition-colors before:duration-500 group"
        href="#0"
      >
        <!-- Fake Button -->
        <span class="relative">
          <!-- Default content: "Build the UI you need" -->
          <span class="group-hover:opacity-0 transition-opacity duration-500 ease-in-out">Build the UI you need</span>
          <!-- Hover content: "Create beautiful user interfaces" -->
          <span
            class="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 before:content-['Create_beautiful_user_interfaces'] after:content-['Create_beautiful_user_interfaces']"
            aria-hidden="true"
          ></span>
        </span>

        <!-- Text: "with Cruip" -->
        <span>with Cruip</span>
      </a>
    </div>
  </div>
</section>
```

## Styling the button

Now, let's style the button. First, we'll define the style for the default text. We'll use a bunch of Tailwind utility classes to set padding, background gradients, border gradients, and more.

```html
<!-- Fake Button -->
<span
  class="relative p-0.5 rounded-full bg-slate-200 group-hover:bg-slate-800 transition duration-500 overflow-hidden flex items-center justify-center"
>
  <span class="relative whitespace-nowrap">
    <!-- Default content: "Build the UI you need" -->
    <span
      class="block px-8 py-6 rounded-full bg-gradient-to-r from-slate-200 to-slate-100 z-10 group-hover:opacity-0 transition-opacity duration-500 ease-in-out"
      >Build the UI you need</span
    >
    <!-- Hover content: "Create beautiful user interfaces" -->
    <span
      class="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 before:content-['Create_beautiful_user_interfaces'] after:content-['Create_beautiful_user_interfaces']"
      aria-hidden="true"
    ></span>
  </span>
</span>
```

Once we've got that down, we can move on to styling the “hover content”:

```html
<!-- Fake Button -->
<span
  class="relative p-0.5 rounded-full bg-slate-200 group-hover:bg-slate-800 transition duration-500 overflow-hidden flex items-center justify-center"
>
  <span class="relative whitespace-nowrap">
    <!-- Default content: "Build the UI you need" -->
    <span
      class="block px-8 py-6 rounded-full bg-gradient-to-r from-slate-200 to-slate-100 z-10 group-hover:opacity-0 transition-opacity duration-500 ease-in-out"
      >Build the UI you need</span
    >
    <!-- Hover content: "Create beautiful user interfaces" -->
    <span
      class="absolute inset-0 rounded-full bg-gradient-to-r from-slate-900 to-slate-800 z-10 inline-flex items-center whitespace-nowrap overflow-hidden opacity-0 group-hover:opacity-100 transition-opacity duration-500 before:bg-clip-text before:text-transparent before:bg-gradient-to-r before:from-indigo-500 before:to-indigo-300 after:bg-clip-text after:text-transparent after:bg-gradient-to-r after:from-indigo-500 after:to-indigo-300 before:content-['Create_beautiful_user_interfaces'] after:content-['Create_beautiful_user_interfaces'] before:px-2 after:px-2"
      aria-hidden="true"
    ></span>
  </span>
</span>
```

And finally, we'll add a few more classes to define the style for the last portion of the text:

```html
<span class="group-hover:text-slate-300 transition-colors duration-500 ease-in-out">with Cruip</span>
```

And that's it! We've created a smooth transition from light to dark mode when you hover over the button, and we've added a cool crossfade effect.

Now we can move on to the most interesting part: creating the text scrolling animation and the button border shine effect.

## Creating the text scrolling animation

To create this type of effect, all we need to do is define a **custom animation** in the Tailwind configuration file. We'll call it `infinite-scroll` and set it up to scroll from right to left. Here's the code:

```js
tailwind.config = {
  theme: {
    extend: {
      animation: {
        'infinite-scroll': 'infinite-scroll 6s linear infinite',
      },
      keyframes: {
        'infinite-scroll': {
          from: { transform: 'translateX(0)' },
          to: { transform: 'translateX(-100%)' },
        },
      },
    },
  },
};
```

Once we have that animation set up, we can easily apply it to our pseudo-element by using the class `animate-infinite-scroll`. Like this:

```html
<!-- Hover content: "Create beautiful user interfaces" -->
<span
  class="absolute inset-0 rounded-full bg-gradient-to-r from-slate-900 to-slate-800 z-10 inline-flex items-center whitespace-nowrap overflow-hidden opacity-0 group-hover:opacity-100 transition-opacity duration-500 before:bg-clip-text before:text-transparent before:bg-gradient-to-r before:from-indigo-500 before:to-indigo-300 after:bg-clip-text after:text-transparent after:bg-gradient-to-r after:from-indigo-500 after:to-indigo-300 before:content-['Create_beautiful_user_interfaces'] after:content-['Create_beautiful_user_interfaces'] before:px-2 after:px-2 before:animate-infinite-scroll after:animate-infinite-scroll"
  aria-hidden="true"
></span>
```

## Creating the shine effect

This is the final step to complete our CTA. To make the shine effect, we'll use a linear gradient and apply it to the before pseudo-attribute of the button element. Since this gradient is a bit complex, we'll take advantage of Tailwind's [arbitrary values](https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values) feature to achieve it.

To really make the shine effect pop, we'll also add an **infinite rotation** to the gradient. We don't need to create a custom animation for this. We can just use Tailwind's `animate-spin`, and use a custom class to change the default rotation time, just like this: `animate-[spin_3s_linear_infinite]`.

So, here's the updated code:

```html
<section class="relative z-0">
  <div class="w-full max-w-5xl mx-auto px-4 md:px-6 py-48">
    <div class="text-center">
      <a
        class="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-6 items-center justify-center text-3xl sm:text-4xl md:text-5xl font-semibold text-slate-900 before:absolute before:inset-0 hover:before:bg-slate-900 before:-z-10 before:transition-colors before:duration-500 group"
        href="#0"
      >
        <!-- Fake Button -->
        <span
          class="relative p-0.5 rounded-full bg-slate-200 group-hover:bg-slate-800 transition duration-500 overflow-hidden flex items-center justify-center before:opacity-0 group-hover:before:opacity-100 before:absolute before:w-1/2 before:pb-[100%] before:bg-[linear-gradient(90deg,_theme(colors.indigo.500/0)_0%,_theme(colors.indigo.500)_35%,_theme(colors.indigo.200)_50%,_theme(colors.indigo.500)_65%,_theme(colors.indigo.500/0)_100%)] before:animate-[spin_3s_linear_infinite]"
        >
          <span class="relative whitespace-nowrap">
            <!-- Default content: "Build the UI you need" -->
            <span
              class="block px-8 py-6 rounded-full bg-gradient-to-r from-slate-200 to-slate-100 z-10 group-hover:opacity-0 transition-opacity duration-500 ease-in-out"
              >Build the UI you need</span
            >
            <!-- Hover content: "Create beautiful user interfaces" -->
            <span
              class="absolute inset-0 rounded-full bg-gradient-to-r from-slate-900 to-slate-800 z-10 inline-flex items-center whitespace-nowrap overflow-hidden opacity-0 group-hover:opacity-100 transition-opacity duration-500 before:bg-clip-text before:text-transparent before:bg-gradient-to-r before:from-indigo-500 before:to-indigo-300 after:bg-clip-text after:text-transparent after:bg-gradient-to-r after:from-indigo-500 after:to-indigo-300 before:content-['Create_beautiful_user_interfaces'] after:content-['Create_beautiful_user_interfaces'] before:px-2 after:px-2 before:animate-infinite-scroll after:animate-infinite-scroll"
              aria-hidden="true"
            ></span>
          </span>
        </span>

        <!-- Text: "with Cruip" -->
        <span class="group-hover:text-slate-300 transition-colors duration-500 ease-in-out">with Cruip</span>
      </a>
    </div>
  </div>
</section>
```

## Conclusions

With the new arbitrary values feature in version 3, Tailwind CSS has become an incredibly flexible tool. It lets us make complex effects like the one we've shown you without writing a single line of CSS.

If you're hungry for more CSS animations like this, we recommend checking out our [Tailwind tutorials](https://cruip.com/tutorials/). For example, we've got a guide on [how to make an animated number counter](https://cruip.com/how-to-make-an-animated-number-counter-with-tailwind-css/), as well as one on [how to create an infinite horizontal scroll animation](https://cruip.com/create-an-infinite-horizontal-scroll-animation-with-tailwind-css/).