2

I have a webpage whose menu I'm trying to make with CSS animations:

<html>
<body>
    <div class="orange-button">
    </div>
</body>
</html>

Here is my CSS file that is linked:

.button-orange {
    border: none;
    transition: background 0.4s, border 0.4s, height 0.4s;
    background: orange;
}

.button-orange:hover {
    border-bottom: 3px solid blue;
    border-top: 3px solid rgba(0, 0, 0, 0);
    background: green;
    height: 94px;
}

The div is 100x100px, and the top border is only needed to keep the text in the middle (otherwise it goes up by 1.5px)

I tried using outline, but quickly dropped it because it can only be used on all sides.

When hovering, everything works correctly, but when moving the mouse somewhere else, the bordes quickly disappear and the height is animated. How can I get them to both animate?

DaniFoldi
  • 451
  • 4
  • 14

1 Answers1

3

The border property is a shorthand and changes more than one single property. By applying border: none you are changing border-style from solid to none, which cannot be transitioned (is not animatable).

In order to transition border-width you need to keep border-style:solid in both states and only change border-width:

.button-orange {
    border: 0 solid;
    border-color: transparent transparent blue;
    transition: background 0.4s, border-width 0.4s, height 0.4s;
    background: orange;
}

.button-orange:hover {
    border-bottom: 3px solid blue;
    border-top: 3px solid rgba(0, 0, 0, 0);
    background: green;
    height: 94px;
}
 <div class="button-orange">Button</div>

However, you'll notice transitioning border-width is not really good looking, mostly because major browsers apply anti-aliasing to sub-pixel values of the border and the transition will not look smooth.

The most commonly used and eye-catchy effect for "border-like" effects is to have an additional element (usually a pseudo-element acting as a "live" border), animating from 0 to full element width, on X axis.

For example:

.button-orange {
    transition: background-color 0.4s, height 0.4s;
    background: orange;
    position: relative;
    height: 18px;
}
.button-orange:hover {
    background-color: green;
    height: 94px;
}
.button-orange:after {
  content: '';
  height: 3px;
  width: 0;
  bottom: -3px;
  left: 50%;
  background-color: blue;
  position: absolute;
  transition: width .4s cubic-bezier(.4,0,.2,1);
  transform: translateX(-50%)
}

.button-orange:hover:after {
  width: 100%;
}
<div class="button-orange">Button</div>
(looks better when not moving the pseudo while changing its width - in our case, could have been placed on top of the element instead of bottom).

Another possible option is to keep the same border-width and animate the border from a "solid" color to transparent, keeping border-width unchanged. It looks smoother and keeps the element from "jumping" because of the difference in border-width.

For example:

.button-orange {
    border: 3px solid transparent;
    transition: background 0.4s, border-color 0.4s, height 0.4s;
    background: orange;
    height: 18px;
}

.button-orange:hover {
    border-bottom-color: blue;
    background: green;
    height: 94px;
}
<div class="button-orange">Button</div>

Also note that in your initial example (as well as in my first snippet), the height property is not, actually animating, because you're trying to animate from default, which is auto, to a specific value and that, again, is not animatable. You need to set a value on the default state if you want to animate height. It tricks you into believing it's working but, in fact, it's the border-width that's animating, creating the illusion the element height is changing. In fact, the border is changing width while having a style of none.

tao
  • 82,996
  • 16
  • 114
  • 150