November 15, 2012

Webkit Fix | CSS Transitions on pseudo elements

by Felix Leupold
Button Transition

Transition Error in Webkit (middle)

CSS generated content is not only the best way for clearfixes, it is also especially helpful if you want to create visually impressive objects with a minimal amount of markup. Due to a long existing bug in Webkit (3 years!) pseudo elements like :before and :after cannot be animated via transitions, yet.

But there is a way to fix this issue, as Roman Komarov presented at the Jam Session at Fronteers Conf this year. And a usually disregarded property value enters the stage for this work around.

Webkit Transition Bug (Example)

In this example I want to put a transition on an anchor element, which has a pointer arrow at its bottom, made of the css pseudo element :after. The whole thing should fade its color from pink to yellow on hover.

a,
a:after {
    transition: 500ms;
}

If we add the transition to the pseudo element as we did to the anchor element, everything looks fine in Firefox, where transitions are implemented correctly for css generated content. Webkit driven browsers, however, just animate the main element (parent of the pseudos) over the defined duration, which leads to a wrong appearance, because the pseudo element immediately jumps to the end values of the transition.

/* buggy pseudo element transition (excerpt) */
a:after {
	transition: 500ms;
	border-style: solid;
	border-width: 10px 10px 0 10px;
	border-color: #f26 transparent;
}
a:hover:after {
	border-color: #dd0 transparent;
}

view the full example code (with the bug)

The old hero: inherit

Some property values get by default inherited from their parent to their child elements, if nothing else is specified such as font-size or color. But every property can be forced to inherit the values from the element’s parent element.

.selector {
    property: inherit;
}

As the property value inherit was not supported in IE before version 8, web developers were not able to reliably use it and began to forget about it. But since we want to use it for a transition here, we can ignore this old IE issue.

Inherit the transition

Inherit does not just inherit the values set in CSS, but every state during a transition. This is what we can take advantage of to fix the bug in webkit driven browsers and every other browser that supports transitions and inherit them correctly (like Opera 12).

Because the border of the pseudo-element is used to create the arrow pointer, we want the border-color to be transitioned. We need to add the border-color (pink) to the parent element and the changed value to its hover style (yellow). But we don’t want the border to be visible there, so we have to set the border-width to 0 and the style to none.

/* fixed pseudo element transition (excerpt) */
a {
    transition: 500ms;
    background: #f26;
    border: 0 none; /* hide the border */
    border-color: #f26 transparent; /* the transitioned value, inherited by the pseudo element */
}
a:hover {
	background: #dd0;
	border-color: #dd0 transparent; /* changed hover value */
}
a:after {
    border-style: solid;
    border-width: 10px 10px 0 10px;
    border-color: inherit; /* inherit the transition */
}

view the full example code (fixed)

Limits of this technique

As we need the properties of the parent element to animate its pseudo elements, there are limits to the things we can animate without affecting the behavior and the look of the parent element. In my example I used the border-color, but set the border-width to 0 on the parent element, which had it just affect the pseudo element and left the parent element visually untouched.

So there are a lot of possibilites to use this technique for properties with sub-properties, to protect your parent element.

  • width, height: limit your parent element with min-width, max-width, min-height, max-height
  • background: hide/show the background only on one element using background-position, background-size
  • color: use text-shadow (maybe in combination with text-indent) for rendering text
  • clip: just don’t position it absolute!
  • top, right, bottom, left: set position to static on the element you want to protect

Head over to the Roman’s slides to find his awesome examples.

It’s fun to find ways to use this technique —  you should try it out and share your results :)

  • Antje

    Cool Thing! I was looking for is technique. Thx Felix

  • http://twitter.com/fizzard Fizz Orange

    Awesome! I was just trying to figure out how to do this.

  • Pingback: Finally Fixed | CSS Transitions on Pseudo-Elements | Webdev | xiel

  • Judah Stevenson

    Ah! What a lifesaver. And a very simple solution too. Thanks for the post. Modernizr 3 will supposedly include a test for support of transitions on css-generated content, but even that will only supply a means to provide a fallback. Very happy that I can still provide the transition-effect to Safari users.