skip navigation

Button Hover Swipe 2

On this page

So this is a different way to achieve a sliding in effect on a button background. The first example animated a gradient background. This way is more complicated but understanding how it works opens up lots of possibilities.

How it works

A ::before pseudo-element is added to the button with a different coloured background. This is scaled to zero size and expands up to a size of 1 in the hover state, transform: scaleX(1). You could use the ::after pseudo-element just as easily.

We’ll need to work on 4 elements to do this:

  1. the button element
  2. the button::before pseudo-element
  3. the button:hover pseudo-selector (this is optional: just to add cursor: pointer)
  4. the button:hover::before pseudo-selector (but just to add cursor: pointer)

General styles

First add some general styles to the button element such as getting rid of the border, choosing a background-color, the text color and maybe adding some padding. Add a border-radius as this will demonstrate something else.

The pseudo element

Next the button pseudo-element button::before.

First we need to add some empty content. This has to be done to get the pseudo-elements to show up at all:

button::before {
    content: '';
}

Our element is created but has no size or colour. We could make it a block level element using display:block but for this we’ll be stretching it across the whole face of the button using position: absolute and inset: 0. But to get position: absolute to work on an element it’s parent must have a position other than the default, static, to work. So first add position: relative to the button element:

button {
    position: relative;
}

So now we can use position: absolute on our pseudo-element. Give it a background color so we can see it too:

button::before {
    content: '';
    position: absolute;
    inset: 0;
    background-color: green;
}

The inset property is a shorthand for top, left, bottom and right properties. And inset: 0; sets them all to zero, which covers the whole element.

The button is now completely covered with the new pseudo element. Even the round corners have disappeared. As this pseudo-element is the child of the button element it means it is overflowing it’s container. To hide this overflow of the button element we just add overflow: hidden and bring back the rounded corners.

button {
    position: relative;
    overflow: hidden;
}

Getting the stacking context right

Great we have our round corners but still can’t see the text. When this green background slides across it will hide the text: not what we want. The obvious thing to use is z-index but apply a z-index: -1 to the button::before pseudo-element and you’ll find it disappears completely. That’s because it’s been pushed behind the button element: also not what we want. We want this in front of the button’s blue background but behind the button’s text. To do this we use the CSS property isolation: isolate to create a new stacking context (z-index positions) that’s within the button element.

button {
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

Now z-index values applied to children of the button (including our ::before pseudo-element) will be stacked relative to each other and not go behind the background of the button.

Now if we add a negative z-index to the button::before pseudo-element we get:

The green pseudo-element is behind the text but in front of the button’s background.

The animation

Now we need to add a transform to the pseudo-element. We will scale it down to zero along the X (left / right) axis. We’ll add a transition time for it of 0.4 of a second. Without this it will just appear instantaneously and we won’t see the movement.

button::before {
    transform: scaleX(0);
    transition: transform 0.4s;
    transform-origin: left center;
}

The hover event

Now it’s gone, scaled down to zero, so we need to bring it back on the :hover event.

The order is important. We’re hovering over the button so button:hover and it’s the ::before element we are targeting to change.

button:hover::before {
    transform: scaleX(1);
}

The full code

So the whole code should look something like this:

button {
    position: relative;
    border-radius: 14px;
    background-color: #26b;
    color: #ddd;
    overflow: hidden;
    isolation: isolate;
}

button:hover {
  cursor: pointer;
}

button::before {
    content: '';
    position: absolute;
    inset: 0;
    background-color: #090;
    z-index: -5;
    transform: scaleX(0);
    transition: transform 0.4s;
    transform-origin: left center;
}

button:hover::before {
    transform: scaleX(1);
}