Menu Search
Jump to the content X X
Smashing Conf Barcelona

You know, we use ad-blockers as well. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

CSS3 Transitions: Thank God We Have A Specification!

This article is packed with a number of quirks and issues you should be aware of when working with CSS3 transitions. Please note that I’m not showing any workarounds or giving advice on how to circumvent the issues discussed. Alex MacCaw has already written a very insightful and thorough article on “All You Need to Know About CSS Transitions661.”

Whereas Alex wrote about achieving particular effects, I’m going to talk about the technical background, especially the JavaScript-facing side. Pitfalls — this article is all about pitfalls. Here is the table of contents if you would like to skip to a specific section:

Separation of concerns is nothing new — we’ve been using template engines for years to accomplish exactly that, separating our HTML from whatever scripting language we were using. A website has three major concerns: structure (HTML), layout and style (CSS), and behavior (JavaScript). CSS crossed the line and became behavioral quite a while ago, but that’s a whole different discussion.

Further Reading on SmashingMag: Link

A couple of weeks ago, I was tasked with developing a JavaScript module that would allow for the use of CSS transitions in a way that the JavaScript side would know nothing about the transitions taking place. The actual problem is the asynchronousity of transitions. After writing a bunch of tests, I gave up on the task. It cannot be done with a reasonable amount of code and initialization time. My test results are what this article is all about.

Before getting started with transitions, we have to talk about a little, frequently used, helper function. getComputedStyle()20 is a JavaScript method that returns a CSS property’s value as the browser interprets it. This API goes back to “DOM Level 2: getComputedStyle()21” and “CSS Level 2: Computed Values22” — which basically specify that a computed style is an absolute value.

This is fine for properties such as font-size23, which take only one argument and are reliably converted to pixel values. However, it doesn’t cover how browsers should handle shorthand properties, such as margin24 — some browsers return nothing, others something semi-useful. Then there are properties with different but equivalent values to consider, such as font-weight25’s bold and 700. WebKit also has a bug26 that extracts the computed value of properties from pseudo-elements.

The quirks described here were identified in January 2013 using Firefox 18 (Gecko), Opera 12.12 (Presto), Internet Explorer 10 (Trident), Safari 6.0.2 (WebKit), Chrome 23 (WebKit), as well as Gecko’s and WebKit’s nightly build channels.

Without further ado, let’s dive into the specifications and implementations, a world riddled with misconceptions. Please note that in order to be concise, I’ve omitted vendor prefixes from the examples.

“Not knowing is difficult to handle. It’s easier to assume.”

– Dr. Axel Rauschmayer

… But assumptions are often wrong. I discovered the information in this article by creating a CSS3 Transitions Test Suite27.

1. Specifying A Transition Link

Besides the shorthand transition property28, the CSS3 transition specification defines the following four CSS properties for specifying an animated change of state:

  • transition-property,
  • transition-duration,
  • transition-delay,
  • transition-timing-function.

CSS Properties to Transition

The transition-property property4429 defines the property (or properties) to animate. The default is all, meaning that all properties a browser can transition will be animated on change (if there’s a transition-duration greater than 0s). The property accepts one value or a list of comma-separated values (like all other transition-* properties).

The specification states that a browser should accept and preserve any property it doesn’t recognize. So, the following example would still run a transition on padding lasting 2 seconds:

transition-property: foobar, padding;
transition-duration: 1s, 2s;

Contrary to the specification, WebKit parses the above to transition-property: all. Firefox and Opera parse it to transition-property: all, padding.

Duration of a Transition

The transition-duration property30 defines the amount of time a transition should take to get from the initial state to the target state. It accepts a <time>31 value in seconds or milliseconds (for example, 2.3s and 2300ms both specify 2.3 seconds).

While the specification makes it clear that values must be a positive number, Opera also accepts -5s — at least for getComputedStyle(). Opera and Internet Explorer (IE) do not accept values lower than 10ms, although the specification mentions no such limitation. In all fairness, you wouldn’t notice a transition lasting 9 milliseconds anyways. WebKit (except for the current WebKit nightly) has a bug in its getComputedStyle() implementation, returning values such as 0.009999999776482582s instead of 0.01s. At least all browsers agree on returning second-based values.

Delay of a Transition

The transition-delay property32 defines the time to wait before executing a transition, also using <time> values. The delay may be a negative value, which will start the transition immediately and make it appear as though the transition had started at the given offset in time — essentially starting with a jump.

As with transition-duration, IE and Opera don’t accept values between -10ms and 10ms. WebKit’s floating point issues appear here, too.

Timing Functions

The transition-timing-function property33 defines the mathematical function used to calculate a property’s value at time t. There are three basic types: cubic-bezier(x1, y1, x2, y2), step(<number>, start|end), and keywords that map to predefined cubic bezier curves. Most likely, you already know the keywords linear, ease, ease-in, ease-out and ease-in-out. The math behind cubic beziers gets ridiculously unimportant when using Lea Verou34’s charming little Cubic Bezier Editor35. While cubic bezier curves make smooth transitions, the step() functions don’t. They instead jump to the next value (i.e. the next step) at a regular interval. This allows for frame-by-frame animations; see “Pure CSS3 Typing Animation With steps()36” for an example.

The computed value of linear is usually represented as cubic-bezier(0, 0, 1, 1) — except for WebKit, which actually returns linear. But not to worry: WebKit will still return cubic-bezier(0.25, 0.1, 0.25, 1) instead of ease. The current WebKit nightly returns the keyword for all defined keywords, though. Looking on the bright side, in a couple of months WebKit won’t be inconsistent with itself — only with the rest of the browser world.

The specification stipulates that the x values must be between 0 and 1, while the y values may exceed that range. Contrary to the specification, WebKit allows x to exceed the bounds, at least computationally. At the time of writing, the Android browser (version 4.0) mixes up ranges for x and y, essentially disallowing “bounce” effects.

2. When A Transition Is Complete

I already mentioned that CSS transitions run asynchronously. The specification provides the TransitionEnd event to allow JavaScript to synchronize with the end of a transition. Sadly, the specification isn’t very specific about this event. In fact, it simply states that an event is to be fired for every property that has undergone a transition. If you need a single word to describe the situation, “nightmare” isn’t far off.

While the specification says that shorthand properties (such as padding) should run transitions for all properties that it covers (padding-top, padding-right, etc.), it doesn’t say which property should be named in a TransitionEnd event. While Gecko, Trident and Presto agree on triggering events for the longhand sub-properties (such as padding-top), even if a transition was defined for a shorthand property (such as padding), WebKit would take the opportunity to screw things up. WebKit would trigger an event for padding if (and only if) you specified transition-property: padding, but transition-property: all would trigger the event for padding-left et al. For some reason, iPhone 6.0.1’s Safari browser might also triggers events for font-size and line-height when padding is being transitioned. Confused yet?

.example {
  padding: 1px;
  transition-property: padding;
  transition-duration: 1s;

.example:hover {
  padding: 10px;

The above CSS will trigger different TransitionEnd events across browsers:

Gecko, Trident, Presto
padding-top, padding-right, padding-bottom, padding-left
.example {
  padding: 1px;
  transition-property: all, padding;
  transition-duration: 1s;

.example:hover {
  padding: 10px;

The CSS above will trigger different TransitionEnd events across browsers:

Gecko, Trident, Presto, WebKit
padding-top, padding-right, padding-bottom, padding-left
Safari 6.0.1 on iPhone (not iPad, mind you!)
padding-top, padding-right, padding-bottom, padding-left, font-size, line-height

I said that you could specify a negative transition-delay to “jumpstart” your transition. But what happens for transition-duration: 1s; transition-delay: -1s;? Gecko and WebKit immediately jump to the target value and trigger an event. Trident and Presto won’t trigger any events.

That floating point issue that WebKit experiences in getComputedStyle() is also present in TransitionEnd.elapsedTime — consistently in all browsers. Math.round(event.elapsedTime * 1000) / 1000 will “fix” that for you.

WebKit and IE have implemented an unspecified extension to background-position37 that causes them to trigger TransitionEnd events for background-position-x and background-position-y, instead of background-position.

So, even if you knew that a transition was taking place, you wouldn’t be able to rely on the TransitionEnd.propertyName that you’re given. While you could write loads of JavaScript to equalize the behavior, you wouldn’t be able to do this in a future-proof way without doing proper feature detection for every single property. And this could include properties you might not even know are animatable.

3. Transitionable Properties

The specification lists a number of CSS properties38 that a browser is supposed to support animated transition for. This list contains properties of CSS2.1. Any of the newer properties will be marked as animatable in their respective specifications — as order of the Flexible Box Layout39 shows.

The property’s value type is an important factor. The property margin-top accepts <length> and <percentage> values, but according to the list of transitionable CSS properties, only <length> is to be animated. But that didn’t keep browser vendors from implementing transitions for <percentage> values anyway. The word-spacing property is a different story, though. The specification includes <percentage> values, but at the time of writing, no browser is able to animate that.

Ignoring the (inherently unreliable) TransitionEnd events, a property is transitioned from value A to value B if its getComputedStyle() value is different from A and B at a given time during the transition. Because there is no such thing as a “CSS property value changed” event, you’re left with polling the DOM. setTimeout()40’s resolution is not good enough to do this for fast transitions (a duration of less than a few hundred milliseconds). requestAnimationFrame()41 is your friend for this. The browser will call you before it repaints to screen, allowing you to grab a couple of intermediate values during transitions. Except for Opera, all engines have this feature already.

Instead of bloating this article with a full compatibility table, I’ve sent my results to Oli Studholme (@boblet42), who has updated his list of “CSS Animatable Properties704543” accordingly.

4. Priority Of Transition Properties

The specification on the transition-property property4429 states that we’re allowed to define a property multiple times:

“If a property is specified multiple times in the value of ‘transition-property’ (either on its own, via a shorthand that contains it, or via the ‘all’ value), then the transition that starts uses the duration, delay, and timing function at the index corresponding to the last item in the value of ‘transition-property’ that calls for animating that property.”

So, we can make padding transition for 1 second, while making padding-left take 2 seconds; or define a default transition style using transition-property: all and overwrite that for particular properties.

In Firefox and IE, this works fine. Opera mixes up the priority order, though. Instead of simply using the last applicable property in the list, it treats padding-left as more specific than padding and all.

The real problem is WebKit. It’s somehow managed to execute a transition multiple times if a property is specified multiple times. To really freak out WebKit, try running a transition for transition-property: padding, padding-left with the very small transition-duration: 0.1s (warning: this is not a good idea for epileptics). WebKit will render the transition at least twice. But the real beauty is the TransitionEnd events, of which you could receive up to hundreds for a single transition.

5. Transitioning From And To auto

The CSS property value auto translates to “Dear browser, please calculate some reasonable value for this.” Paragraphs (<p>) and any block-level elements will be as wide as their parent if they have width: auto. There are times when you’ll change from width: auto to a specific width — and want to transition that change. The specification neither enforces nor denies the use of auto values for transitionable properties.

Firefox, IE and Opera cannot transition from or to auto values. IE makes a little exception for z-index, but that’s it. WebKit, on the other hand, is capable of transitioning from and to pretty much any CSS property that accepts the auto value. WebKit doesn’t like clip too much; for that property, it will only trigger a TransitionEnd event, without generating or showing any intermediate values or states during the transition.

For the other properties, such as width and height, WebKit’s behavior is not quite what you’d expect. If width: auto translated to a calculated width of 300px and you transitioned that to 100px, then your transition would not shrink from 300 to 100 pixels. Instead, it would grow from 0 to 100 pixels.

For a full compatibility table, have a look at “CSS Animatable Properties704543.”

6. Implicit Transitions

An “implicit transition” happens when a change to one property causes another property to be transitioned — or if you change a property on a parent element and cause a child to transition either the inherited property or a dependent property. Confused? Consider font-size: 18px; padding: 2em; — the padding is calculated as 2 × font-size, because that’s what em does, giving us 36 pixels.

There are various relative value types: <percentage>, <length>, em, rem, vh, vw, etc. Using a relative value, such as padding: 2em, makes the browser recalculate the property’s getComputedValue() every time its depending value (such as font-size) changes. That in turn triggers a transition for padding because the computed style has changed. This transition is considered to be “implicit” because the padding property was not modified explicitly.

Most browsers run these implicit transitions. The exception is IE 10, which runs them only for the line-height property. WebKit runs implicit transitions for all applicable properties except vertical-align. Besides font-relative property values, there are width-relative property values (usually <percentage>), viewport-relative property values (such as vh and vw), default initial values (such as column-gap: 1em in Opera), and then currentColor46. All of these might — or might not — trigger implicit transitions.

In Firefox, these implicit transitions get particularly interesting when both the depending and the dependent properties are transitioned but their transition-duration or transition-delay do not match. While WebKit and Opera produce transitions that make sense visually, Firefox garbles things a bit. In IE, this is a non-issue because it doesn’t do implicit transitions.

Don’t forget about inheritance within the cascade. A font-size on a DOM element will be inherited by its children, as long as it’s not overwritten, potentially causing implicit transitions.

id=”a7″>7. Transitions And Pseudo-Elements Link

Pseudo-elements (:before and :after) were introduced with CSS2 generated content47. Read “Learning to Use the :before and :after Pseudo-Elements in CSS48” if you’re not yet familiar with generated content. While CSS3 content49 defines additional pseudo-elements (::alternate, ::outside), they are not (yet) supported. All animatable CSS properties should also be animatable for pseudo-elements.

Firefox and IE 10 will transition properties on pseudo-elements. Opera, Chrome and Safari will not. WebKit added support in January 2013 — which you can already check out in WebKit nightly50 and Chrome Canary51.

Transitions of generated content bring their own set of funky issues. TransitionEnd events aren’t fired at all. At some point in the future, they’re supposed to be triggered on the owner element and provide their pseudo-element through TransitionEnd.pseudoElement. But even the “Transition Events52” section of the “CSS Transitions” editor’s draft doesn’t specify that properly yet.

There was a time when we would change the value of the content property so that IE 8 would re-render that element in certain circumstances (like when entering a :hover state). It turns out that this fix for old IE interferes with this ability for all other browsers. So, when trying to transition a property on a pseudo-element, make sure the content is not changed.

IE 10 will not run a transition for a pseudo-element’s :hover state if the owner element doesn’t have a :hover state as well:

.some-selector:before {
  content: "hello";
  color: red;
  transition: all 1s linear 0s;

.some-selector:hover:before {
  color: green;
/* This next rule is necessary for IE 10 to transition :before on hover */
.some-selector:hover {}

The weird thing about this issue isn’t that you need a (possibly empty) :hover state on the owner element. It’s that if you don’t have one, IE 10 will interpret the :hover as :active (i.e. active when you mousedown on the element). The even weirder part is that the :active state persists even after mouseup and is removed only by another click on the document.

8. Background Tabs

At the time of writing, IE 10 is the only browser that responds to a tab being in the background or foreground. While it will finish a running transition if the tab is pushed to the background, it won’t start any new transitions there. IE 10 will wait until the tab is pulled into the foreground before starting any new transitions. Fortunately, IE 10 already supports the Page Visibility API53, allowing developers to respond to this behavior.

We can expect similar things to happen with other browsers as they continue putting background tabs to sleep.

9. “Invisible” Elements

So, are transitions executed for DOM elements that are not attached to the DOM? Nope, not a single browser does that — why should they? Well, then, what about hidden elements? Most browsers have figured out that there’s no need to run a transition on an invisible (i.e. not painted) element. Opera thinks differently about this — it’ll run a transition regardless of whether it is painted or not.

10. Transitioning Before The DOM Is Ready?

The DOMContentLoaded event is triggered when the document leaves parsing mode. If you’re into jQuery, we’re talking about jQuery.ready()54 right now. Transitions can be run before this event happens.

11. Rendering Quirks

The issues I’ve described up to this point were found by testing against the specification. The tests were run automatically. But as it turns out, quite a few more problems are visible to the eye. The following quirks have been found by various other developers and could affect your meddling with transitions just as much.

At this time, transitioning a background from gradient to gradient is not possible. Transitioning from gradient to solid color is possible — with a big caveat. If a gradient is in play, then the color transition will happen from white to the target color, appearing to quickly flash white at the beginning of the transition. This can be observed55 in all current browsers.

Firefox seems to be using a different algorithm for rendering (or smoothing) images as they’re being animated (see an example56). Apparently, Gecko sacrifies quality for performance during animation. Note that this occurs if a low enough transform: scale() is in play57.

Firefox won’t properly animate from a:visited to a:hover or vice versa. Instead, it will jump from a:visited to a:link and then transition to a:hover, as you can see in this example58. This is mentioned somewhat in “Privacy and the :visited Selector59” on the Mozilla Developer Network. While IE 10 agrees with Chrome, Safari and Opera on the proper transition, it also runs the transition from a:link to a:visited on page load.

Transitioning multiple properties is not synchronized in Firefox and Webkit. You can see in this example60 how making the border smaller by the same amount that the padding increases (and vice versa) causes the following content to shake a bit. IE 10 and Opera get this right.

Firefox won’t animate an element’s properties if one of its parent’s position is changed, as you can see61. Webkit, Opera and IE 10 behave correctly.

12. Recommendations For The Specification

Having read the specification from top to bottom and actually tested all of the features, I think a few changes would help:

  • Introduce a TransitionsEnd (notice the plural), triggered once all transitions for an element have completed. It could provide a list of properties that have been animated — but I don’t see the use case for knowing what has transitioned, as long as I’m informed when all animations are done.
  • Introduce a TransitionStart event, triggered for every property about to be transitioned. Because DOM events don’t come cheap and the JavaScript event loop and the rendering thread are not necessarily blocking each other, a single event TransitionsStart (there is that plural again) might be the better solution. I don’t see why I should be able to cancel the event, so this would be a “fire and forget” kind of thing.
  • Make it clear what TransitionEnd is supposed to be triggered for. That padding versus padding-left issue in WebKit is rather annoying.
  • Clearly specify how “implicit transitions,” such as line-height: 1em for transition-property: font-size, are to be handled.
  • Add a ::transitioning pseudo-class that allows you to define pointer-events: none to prevent accidental hover states (among other things). The trick here is to prevent the application of styles that themselves would trigger a new transition or that would alter an already running transition.

In addition to these suggestions, we should be able to accomplish a number of common (simple) things without having to throw a lot of JavaScript at the problem:

  • Every once in a while, you’ll want to completely mute all transitions — for example, because you’re changing the layout and need to calculate dimensions and positions before unleashing your beautiful transitions upon the visitor.
  • Sometimes you’ll want to remove an object from the DOM and want that to be animated. Right now, you’d add a class, wait for the TransitionEnd event and then remove the element.
  • Just as with removing things, you’ll want to add a new element and animate its appearance. Right now, you have to insert the element, set some “invisible style,” force a repaint and then revert to the new element’s actual style.
  • Reordering, hiding and showing elements are common for any Web application. Giving that task a little style currently requires us to run utilities such as Isotope62. A vanilla CSS solution could shave off some bytes.

13. Use The delay, Luke!

Imagine a number of elements packed together tightly. Imagine that the styles of those elements change on hover. Imagine moving your cursor (moderately quickly) over that group. What happens? Exactly: you’ll see the styles of those elements flash63.

By adding a relatively short delay to your transitions, you can mitigate that effect64; 20 milliseconds is undetectable to the human eye, but it’s enough for the mouse cursor to pass over small elements. The transitions won’t appear to lag because of this, and the visual distraction you might have caused just disappears. Simple trick, I know.

14. Conclusion

  • Be very careful when using transition-property: all. You will get TransitionEnd events for properties that you didn’t expect to ever transition.
  • Be careful when using shorthand properties, because the number of triggered events varies between browsers.
  • Opera and IE don’t trigger events when a negative delay cancels out the duration.
  • WebKit has real issues with the priority of properties such as transition-property: margin, margin-left. Avoid this for now.
  • IE doesn’t support implicit transitions — for example, triggered for padding: 2em when font-size changes.
  • Firefox and Opera cannot parse transition-property: all, width.
  • Opera mixes up the priority of properties.
  • Transitions on pseudo-elements do not trigger TransitionEnd events.
  • IE 10 has a weird :hover bug when transitioning pseudo-elements.
  • The specification leaves a lot of room for improvement.

If you’re interested in transitions and animations — and how to use them wisely — have a look at these fantastic resources:

Thanks go to Oli Studholme71 and Lea Verou72 for taking the time to review this article, and Peter Linss73 for walking me through the CSS Working Group’s testing infrastructure. Front page image credit: “Transitional Interfaces7468,” Pasquale D’Silva


Footnotes Link

  1. 1
  2. 2 #a1
  3. 3 #a2
  4. 4 #a3
  5. 5 #a4
  6. 6 #a5
  7. 7 #a6
  8. 8 #a7
  9. 9 #a8
  10. 10 #a9
  11. 11 #a10
  12. 12 #a11
  13. 13 #a12
  14. 14 #a13
  15. 15 #a14
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35,.67,.83,.67
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74

↑ Back to top Tweet itShare on Facebook

Rod is a web developer based in southern Germany. Having been a freelancer for a decade, he’s now working on Qivicon, Deutsche Telekom’s Smart Home platform. Before focusing himself on all things in the browser, he mostly did PHP and was quite decent with databases and distributed systems for scalability and redundancy. Created URI.js, worked on Smarty.

  1. 1

    Wonderfully in-depth look at transitions. I really appreciated the rendering quirks and delay sections. Some of these issues have bothered me in the past, but I had yet to find any decent explanations/clear fixes.

  2. 2

    Nice elaborate article. There’s just one problem: it’s partially outdated already. Tests from three months ago might not be relevant to the browsers of today. The tested Firefox is 2 versions ago, and since Opera will move to the Blink rendering engine (together with Chrome) things will be different in the future as well.
    Have you considered reporting these bugs/inconsistencies to the Webkit/Blink/Gecko teams?

    • 3

      Any article on any browser-centric topic is going to be outdated at some point. While I haven’t run the whole suite in a while, individual tests (in current nightly builds) still show the same problems. Sadly there isn’t that much traction in fixing bugs like these – even though the version number increments every couple of weeks.

      I have to admit that I didn’t file any bugs yet. I could go on about finding creative excuses, but lack of time will have to suffice for now. It’s planned for the next couple of weeks, though.

      Please realize that *any* article is referring to a specific point in time, so it’s likely to be outdated sooner rather than later. In fact, that is one of the reasons I wrote this piece – to make people aware (and fix) things.

      • 4

        Please do file those bugs, and link to them in an update at the bottom of this post. This is really valuable research–but only if it drives future fixes. It sounds like you’ve got the test cases pretty much worked out already. I think the W3C might be receptive to your “pluralized” transition events…

  3. 5

    Roland Warmerdam

    April 26, 2013 6:25 am

    Chrome can now transition pseudo-elements (was added in Chrome 26). Also here’s a Modernizr test from detecting pseudo-element transition support:

  4. 6

    Seing all these possible struggling points makes me think that despite the many (so-called) experts’ declarations, flash still has a role to play in today’s web for animations (and only for that !). Am I crazy to think this ?

    • 7

      Loren Khulusi

      April 26, 2013 7:58 am

      Well, a lot of these inconsistencies can be overcome by using Javascript to trigger and serialize animations to mitigate a lot of the problems.

      Here are a couple sites I built, both in 2011 mind you, with purely CSS3 transitions and animations triggered by jQuery.

      I would suggest running them in a webkit browser as the code is still from 2011.

  5. 8

    I have come across many of these issues whilst developing Sequence.js – a jQuery slider which allows users to create slide transitions using just CSS3. Obviously, it relies heavily on CSS3 transitions.

    Although it probably has a very low use case, Sequence has a little work around for WebKit returning a keyword rather than the cubic-bezier(). Which I’ve roughly detailed here: This function will get the timing-function applied to an element, and if it is not a cubic-bezier, will convert the keyword into one, for better consistency across browsers.

    I think the transitionsEnd (plural) event is also a great idea and one that could greatly increase the performance of Sequence.js. Sequence actually has this sort of event in-built, but at the moment it can only determine when the transitions of top level elements within the parent element have ended and has nowhere near the performance a native event would. I may try pulling this out of Sequence and putting it into its own plugin if anyone is interested though.

  6. 9

    I want to create an even when transition is over! is there any way to do this without jQuery? :((
    for example, I hover a link (mouseIn), it changes it’s color in 2 seconds, once the change is complete, something else happens; or at least it returns back to the way it was.

  7. 11

    Jose pacheco

    April 26, 2013 8:05 am

    Ive a special interest about the point 9 of this article. Isnt that interesting if the invisible elements become visibel when you over the elements? Ive one particular job with an hidden object that when we mouse hover would be rly interesting in having a smoth transition. Any tip or related article about the point 9?

    • 12

      I wonder if “visibility:hidden” won’t work as well.
      if i understand this correctly, “visibility: hidden” hides the object, but reserves it’s space. try it ;-)

    • 13

      That is a common request. People want to be able to transition the display property (none to block and back) to show and hide elements in an animated fashion. But how would you visualize that?

      If you do not require layout, meaning your elements aren’t part of the natural flow of the document, you can simply transition the opacity. If showing/hiding your element affects the position of other elements on the page, you’re facing the problem of smoothly making space. This can be quite stressful for the CPU, as you might end up reflowing/layouting the document too often.

      If reflow/layout is not a problem, you could use opacity and negative margins on your element to make it appear as it was display: none. You would then set the margins and (with a slight delay) the opacity to their regular values.

      Make sure you look at the DevTools timeline to figure out if this is going to bite you.

  8. 14

    There’s one big issue with the TransitionEnd event – the event is not fired if no transition happens – this may surprise you if you set the transitions from JavaScript and rely on that event to chain them.

  9. 15

    John Polacek

    April 29, 2013 8:59 am

    CSS Transitions are great for doing little enhancements here and there, but if you want to do something really robust, just do yourself a favor and use Greensock.

    For example, this: or this:

  10. 16

    ULTRA thankyou for the detailed writeup! The web needs more richly detailed docs like this.

    After reading all of this and other research into canvas would Canvas not be a better solution for more complicated compositions? Rather then trying to get time delays to properly animate say an info graphic canvas .. Sorry I lost my train of thought.

    I have tested some info graphic css3 delay queues and have had other headaches getting those to play in the correct order and load properly. So I read a great book called html5 animation from apress. But everything inside of it was endless loop or continual movement physics. There was nothing for simple keyframe Ball got to x, chill out for 2 secs go to y.

    So I hacked together a detailed question and got some excellent help from stackoverflow, the original author on twitter, rkapi, and activetheroy detailing how javascript does not have such an engine and that frameworks have to be invented to do such. I miss flash.


  11. 17

    Pier Paolo Ramon

    May 8, 2013 1:04 am

    I think you wrote wrong the pseudo-class for a transitioning, you wrote “::transitioning” instead of “:transitioning” (notice the double colon)

  12. 18

    Thank you for this awesome article Rodnesy. Great job and very thorough with awesome examples and citations. Bookmarked, plus like 4 of the citations!

  13. 19

    Let me add one more gotcha:

    TransitionEnd event won’t be triggered if the initial value is equal to the new value.

    This may seem simple to spot but it is not.

    Happy coding!

  14. 20

    David Fregoli

    June 26, 2013 8:38 am

    What an awfull mess, this is far from usable as anybody that has tried to wrap his mind around it will tell you…made me go back to my safe jQuery.animate() kingdom in anxiety…

  15. 21

    Jonas Schubert Erlandsson

    November 29, 2013 2:14 am

    Another good idea would be to allow for an optional name attribute when defining a transition. Then the end event could include the name as a label to be used in javascript. It’s not such a stretch to imagine the same attribute being affected by several transitions and it being a mess to figure out what animation it was that actually ended …

  16. 22

    Jon Barratt

    July 12, 2014 8:34 am

    This is a fabulous article, and I am sure there is something in here that would provide the solution for my problem. But, I can’t find it.

    In a nutshell, I’m running a query class change (i.e. addClass/removeClass) on scroll (it’s also on load and resize events). It works perfectly on all browsers except Safari – where it works sometimes, but –

    If I click from the home page to sub-page and move the mouse just a little during the first milliseconds it starts loading, the transition won’t work on scroll although the class change still occurs. If I then rollover a button with a hover event, the transition is activated. I don’t understand why this happens and would love someone to identify the solution.

    Here is the StackOverflow with code –

    Here is the offending page (but click the logo for home and then return to this page) –

    Thanks to everyone for trying, and thanks a million to anyone who can solve it!

  17. 23

    Michael Pohoreski

    February 16, 2015 11:41 pm

    Great summary of the current clusterfrack of CSS. Every browser is half-implementing what they feel is “important”. :-/

    There is one minor mistake in the article:

    > 20 milliseconds is undetectable to the human eye

    This statement is **completely incorrect.**

    20 ms is only 1,000 ms/s / 20 ms = 50 Frames/Second.

    No offense intended but why does this myth keep popping up (almost always) from people have have *never* even used a 120 .. 144 Hz monitor?? Back in the CRT days us gamers would set our monitors refresh rate between 72 and 100 Hz for headache free flicker free smooth animation.

    Anything less then 60 fps looks stuttery and choppy as heck. I have a 144 Hz monitor can clearly tell the difference between 120 Hz (8 ms), 60 Hz (16 ms), and 30 Hz (33.33 ms) like night and day. And it not just because I am a graphics / OpenGL guru with some mythical “golden eyes” — the awesome LightBoost guys have a series of awesome tests so you can see it for yourself, especially that Chrome supports 60 fps.

    It is not just animation but video too looks horrible at less then 60 fps. Pans are the hardest hit as the the high-end camera maker Red will testify:

    * OWE my eyes @ 24 fps !

    * Silky smooth @ 60 fps !

    There is a reason YouTube is now supporting 60 fps videos.

    Please tell UI and UX designers to target 60 fps so our eyes don’t bleed.


↑ Back to top