153

In the fiddle below, I've a transition on visibility and opacity separately. The latter works but the former doesn't. Moreover, in case of visibility, the transition time is interpreted as delay on hover out. Happens in both Chrome & Firefox. Is this a bug?

http://jsfiddle.net/0r218mdo/3/

Case 1:

#inner{
    visibility:hidden;
    transition:visibility 1000ms;
}
#outer:hover #inner{
    visibility:visible;
}

Case 2:

#inner1{
    opacity:0;
    transition:opacity 1000ms;
}
#outer1:hover #inner1{
    opacity:1;
}
user4150760
  • 2,739
  • 5
  • 18
  • 25

5 Answers5

220

This is not a bug- you can only transition on ordinal/calculable properties (an easy way of thinking of this is any property with a numeric start and end number value..though there are a few exceptions).

This is because transitions work by calculating keyframes between two values, and producing an animation by extrapolating intermediate amounts.

visibility in this case is a binary setting (visible/hidden), so once the transition duration elapses, the property simply switches state, you see this as a delay- but it can actually be seen as the final keyframe of the transition animation, with the intermediary keyframes not having been calculated (what constitutes the values between hidden/visible? Opacity? Dimension? As it is not explicit, they are not calculated).

opacity is a value setting (0-1), so keyframes can be calculated across the duration provided.

A list of transitionable (animatable) properties can be found here

SW4
  • 69,876
  • 20
  • 132
  • 137
  • 11
    http://dev.w3.org/csswg/css-transitions/#animtype-visibility specifies that intermediate values map to "visible". – Beni Cherniavsky-Paskin May 25 '15 at 11:57
  • @BeniCherniavsky-Paskin - this depends on the timing function: `other values of the timing function (which occur only at the start/end of the transition or as a result of cubic-bezier() functions with Y values outside of [0, 1]) map to the closer endpoint` – SW4 May 26 '15 at 07:37
  • 1
    The response by SW4 is misleading, and does not explain the misunderstanding as to the purpose of visibility. –  Jul 18 '15 at 00:07
  • 1
    @JesseMonroy650 - although I would hesitate to refute, it is easier to do so without any supplementary evidence for that claim being provided, it would be fascinating if you can elaborate? The OP was not asking the purpose of visibility (which is different to display, opacity) but why it cannot be animated as a property, namely for the reason given- it is effectively an on/off setting. The answer does not seek to tackle 'what visibility is' but 'why it cannot be animated' – SW4 Jul 22 '15 at 17:57
  • We could quibble as to the meaning of the OP, but I will counter. Annoyed by the constant (incomplete) theme and the inability to make this work, I decide to investigate it. First it is worth noting the [documentation is poor](https://developer.mozilla.org/en-US/docs/Web/CSS/visibility#Interpolation); the explanations are poor, the [spec is poorly written](https://drafts.csswg.org/css-transitions/#animatable-css) (editor has note too). While documented as `animatable`, in fact it has but few of the properties; one of those properties being *timing*. I will blog soon. –  Jul 22 '15 at 20:13
154

Visibility is not traditionally animatable as you might expect. However, you can fake it by using the opacity property. Check this blog post about it: http://www.greywyvern.com/?post=337

You can see it here too: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties

Let's say you have a menu that you want to fade-in and fade-out on mouse hover. If you use opacity:0 only, your transparent menu will still be there and it will animate when you hover the invisible area. But if you add visibility:hidden, you can eliminate this problem:

div {
    width:100px;
    height:20px;
}
.menu {
    visibility:hidden;
    opacity:0;
    transition:visibility 0.3s linear,opacity 0.3s linear;
    
    background:#eee;
    width:100px;
    margin:0;
    padding:5px;
    list-style:none;
}
div:hover > .menu {
    visibility:visible;
    opacity:1;
}
<div>
  <a href="#">Open Menu</a>
  <ul class="menu">
    <li><a href="#">Item</a></li>
    <li><a href="#">Item</a></li>
    <li><a href="#">Item</a></li>
  </ul>
</div>
TylerH
  • 20,799
  • 66
  • 75
  • 101
Sevban Öztürk
  • 1,796
  • 1
  • 11
  • 8
  • There is a big question here I think has not been answered: We can achieve the goal you stated in your example, by simply setting visibility to hidden (and visible on hover). I mean there is no need to write it in transition property. – Saleh Nov 17 '22 at 14:04
  • @Saleh When people ask to animate a property, the mean "over a period of time", rather than just toggling a property from one value to another. Typically in order to show intermediate values during the transaction. If there is no need to show intermediate values, then animation is not needed and the property can just be switched in one action. – TylerH May 19 '23 at 18:33
  • 1
    @TylerH Thank you. But my question was that when the visibility property is almost a boolean property, it is nonsense to animate its value over a period. In fact, its value takes the final value immediately regardless of `transition-duration`. َApart from delaying it with transition-delay I wondered if there is any use case for using it as a transition prop – Saleh May 29 '23 at 07:59
34

Visibility is an animatable property according to the spec, but transitions on visibility do not work gradually, as one might expect. Instead transitions on visibility delay hiding an element. On the other hand making an element visible works immediately. This is as it is defined by the spec (in the case of the default timing function) and as it is implemented in the browsers.

This also is a useful behavior, since in fact one can imagine various visual effects to hide an element. Fading out an element is just one kind of visual effect that is specified using opacity. Other visual effects might move away the element using e.g. the transform property, also see http://taccgl.org/blog/css-transition-visibility.html

It is often useful to combine the opacity transition with a visibility transition! Although opacity appears to do the right thing, fully transparent elements (with opacity:0) still receive mouse events. So e.g. links on an element that was faded out with an opacity transition alone, still respond to clicks (although not visible) and links behind the faded element do not work (although being visible through the faded element). See http://taccgl.org/blog/css-transition-opacity-for-fade-effects.html.

This strange behavior can be avoided by just using both transitions, the transition on visibility and the transition on opacity. Thereby the visibility property is used to disable mouse events for the element while opacity is used for the visual effect. However care must be taken not to hide the element while the visual effect is playing, which would otherwise not be visible. Here the special semantics of the visibility transition becomes handy. When hiding an element the element stays visible while playing the visual effect and is hidden afterwards. On the other hand when revealing an element, the visibility transition makes the element visible immediately, i.e. before playing the visual effect.

  • As an extension to this post, I want to share another useful link from this website: https://www.taccgl.org/blog/css-transition-visibility.html Also, it may be useful to see my "toggle popup" codepen demo: https://codepen.io/mundisyum/pen/mdqMMod – Pavel Rodionov Feb 14 '22 at 16:43
1

If you want to delay the visibility then the code snippet below could be a solution.
Because the 'visibility' property is on/off you could use the transition-delay property to control the timing of when the object should be visible.

div {
    width:100px;
    height:20px;
}
.menu {
    transition-delay: 0s;
    transition-duration: 0s;
    transition-property: opacity;
    background:#eee;
    width:100px;
    margin:0;
    height: 0px;
    opacity: 0;
    list-style:none;
    overflow: hidden;
}

div:hover > .menu {
    height: initial;
    transition-delay: 1s;
    opacity: 1;
}
<div>
  <a href="#">Open Menu</a>
  <ul class="menu">
    <li><a href="#">Item</a></li>
    <li><a href="#">Item</a></li>
    <li><a href="#">Item</a></li>
  </ul>
</div>
-2

You need to add the delay property into transition and then use the opacity.

I'm using chakraUI and React, but it's the same logic for plain CSS since everything gets compiled to plain CSS:

<Box
      display="flex"
      gap="2"
      flexWrap="wrap"
      visibility={shouldBeVisible ? 'visible' : 'hidden'}
      opacity={shouldBeVisible ? '1' : '0'}
      transition={
         shouldBeVisible
            ? 'opacity .3s linear'
            : 'visibility 0s linear .4s, opacity .3s ease-in-out'
        }
>
    {YOUR COMPONENTS}
<Box>

Notice that I use visibility 0s linear .4s, opacity .3s ease-in-out, the .4s is to delay the hidden visibility, and while it's waiting to be hidden the actual aim transition is running on the opacity property.

Federico Fusco
  • 545
  • 1
  • 5
  • 18
  • This does not appear to be HTML or CSS. – TylerH May 19 '23 at 18:31
  • 1
    So, this is React and chakra UI. But chakra ui its just another way to write CSS. Any way, what you need to know it's just the transition property `'visibility 0s linear .4s, opacity .3s ease-in-out'` if you apply this and make the opacity and visibility change between states/classes you achieve what you'll want – Luiz Felipe de Souza Barbosa May 20 '23 at 00:51
  • 1
    @LuizFelipedeSouzaBarbosa ok, but you cannot force the use of other tools/libraries/frameworks. Also, there is an already accepted answer, with a huge number of upvotes, so try to answer by staying in context. You can read [how to answer](https://stackoverflow.com/help/how-to-answer) – pierpy May 21 '23 at 08:42