CSS3 transitions and 2D transforms

By David Storey


For richer user interfaces it is often desirable to include some animation to make an effect smoother or more appealing, or effects such as rotating elements and text. Traditionally in HTML pages the primary means to add animations was to use JavaScript to adjust the desired CSS property value over a given period of time. This works but can be slower as the JavaScript code is not hardware or software accelerated. What's more, using JavaScript for animations creates more code to maintain. It has not been possible to apply effects such as text at an angle without resorting to using images or SVG.

In CSS3, these animations and transformations can be handed off to the browser and defined in the CSS layer using CSS3 transitions and transforms, which are supported in our Opera 10.50 and later. In this article, you'll learn about CSS3 transitions and transforms in Opera and see the SVG and SMIL corollaries to them, too.


Introduction to CSS transitions

When changing the value of a property in CSS, that new value is applied immediately. For example, in the CSS below the colour would change from red to blue when the user hovers over the element:

div {
      width: 3em;
      height: 3em;
      background-color: red;

div:hover {  background-color: blue; }

This article will guide you through how to use CSS3 transitions and briefly touch on how the equivalent can also be achieved in SVG via SMIL.

Working with CSS transitions

Setting up a transition

The first step in using CSS3 transitions is to define which elements you want to apply the transition to and which CSS properties will be used in the transition.

This is done with the transition-property property:

div {
      width: 3em;
      height: 3em;
      background-color: red;
      -o-transition-property: background-color;

Note, for brevity I've only used the Opera prefixed version of the property in the inline examples. The actual examples use the prefixes for other vendors and the standard (non-prefixed) version. It is important to use the standard version along with browser-specific prefixes so that your transitions automatically work in other browsers when they support it.

Once the transition-property is set and the element selected, the CSS property value will transition from the original value to the new value. Note that for current properties that can be animated via CSS3 transitions in CSS and SVG check the CSS3 transitions spec.

Next you'll define the duration of the transition. This is set with the transition-duration property, which by default is 0 seconds, so we add a time value, usually specified in seconds:

div {
      width: 3em;
      height: 3em;
      background-color: red;
      -o-transition-property: background-color;
      -o-transition-duration: 4s;

div:hover { background-color: blue; }

In the above example, when the user hovers over the div element the colour will transition from red to blue in 4 seconds. Try the colour transition example.

Delaying a transition

By default the transition happens as soon as the specified element is accessed, but the transition-delay property can be used to delay the start of the transition:

div {
      -o-transition-property: background-color;
      -o-transition-duration: 4s;
      -o-transition-delay: 1s;

Try the delayed transition example.

Controlling transition velocity

As well as defining the timing of the transition, you can also control the velocity. Instead of a smooth transition from A to B, you can define a transition to speed up or slow down along its duration. This is defined using a cubic bezier curve, which is a common method in computer animation. To make this simpler to use in CSS, there are a number of predefined curves: ease (the default), linear, ease-in, ease-out, and ease-in-out.

Try out the following transition example using all five pre-defined timing functions. I've used the width property for the transition to make the differences more obvious:

div {
      -o-transition-property: width; 
      -o-transition-duration: 4s; 
div:nth-of-type(1) { -o-transition-timing-function: ease;  }
div:nth-of-type(2) { -o-transition-timing-function: linear;  }
div:nth-of-type(3) { -o-transition-timing-function: ease-in;  }
div:nth-of-type(4) { -o-transition-timing-function: ease-out;  }
div:nth-of-type(5) { -o-transition-timing-function: ease-in-out;  }

To specify your own cubic bezier curve for the transition, you use the cubic-bezier value along with the X and Y co-ordinates for the P1 and P2 timing function control points. With cubic bezier curves there are 4 control points: P0 through to P3, and they take a number between 0 and 1 for the X and Y co-ordinate. P0 and P3 are constants which are always 0,0 and 1,1 respectively. The browser will use a mathematical equation to produce a smooth curve through P1 to P2. If you were to specify a linear transition by hand you would define it as cubic-bezier(0, 0, 1, 1);, which as you can see is a straight line from 0,0 to 1,1.

An ease-in curve would be defined as cubic-bezier(0.42, 0, 1, 1);. This would create a shallow curve which accelerates upwards to meet the P2 control point. You can see this as the third box in the example starts slowly and speeds up towards the end. An ease-out curve is the inverse of an ease-in curve and starts quickly where the curve is straighter and slows towards the end as it curves to meet the P2 control point. This would be specified as cubic-bezier(0, 0, 0.58, 1);.

Combining transitions

As well as just specifying one property to transition, each of the CSS3 transitions properties can take a comma-separated list of values. This allows multiple properties to be transitioned on each element, each with their own timing and velocity values. In the following example I transition the width, height and background colour, each over varying lengths of time:

div {
  -o-transition-property: background-color, width, height; 
  -o-transition-duration: 4s, 8s, 5s;
  -o-transition-delay: 0s, 0s 2s;

Try out the multiple transitions example.

The transition-property property defaults to the all keyword, which means that by default all properties will be transitioned when changed, providing the transition-duration is changed from the default value of zero seconds.

As with other CSS properties such as border and background, there is a transition shorthand property. The example above as a shorthand would be the following:

div {
  transition: background-color 4s 0s, width 8s 0s, height 5s 2s; 

Transitions in SVG

Similar effects to CSS transitions can be performed in SVG using the animate element from SMIL. The SVG equivalent of the delay transition example above would be:

<animate attributeType="CSS" attributeName="background-color"
  from="red" to="blue" begin="2s" dur="4s" fill="freeze" />

The animate element is placed as a child of the element you want to animate. You can use multiple animate elements to transition multiple properties. The attributes are all self-explanatory except for the freeze value. This is used to express that when the transition ends, it will stay in the final state rather than resetting to the initial state. With SVG, it is possible to animate along paths, fire animations by events such as clicking or hovering, repeat animations and so on, but that is beyond the scope of this article.

CSS 2D transforms

2D transforms in CSS allow for various transformation to be applied to elements, such as scaling or rotating. It is possible to apply one or many transforms to a single element. This allows for effects such as rotating text or images at an angle, and can be combined with transitions to apply interactive effects such as scaling up elements when the users interacts with them.

Applying a transform

Two CSS properties have to be specified to apply a transform: the transform property specifies the type(s) of transform(s) you want to apply to the element and the transform-origin property sets the point of origin from where the transform takes place. This differs from SVG where the transform origin is specified by translating the element first (similar to the CSS translate function described below), or as an optional 2nd and 3rd argument in the case of rotate. The CSS version is more flexible in that it is easy to specify things like the centre of the element to be the origin of the transform (which is a common need, especially when scaling or rotating), while in SVG the centre point has to be calculated yourself from a set point (often the top left corner) on the element in question.

Setting the origin of the transform

The initial transform-origin value is 50% 50% which is the centre of the element—50% from the left of the element and 50% from the top of the element. The origin can be specified using either a length (in the regular CSS units), a percentage or the keywords left, center or right (for the X co-ordinate) and top, center or bottom for the Y coordinate.

The first value specified is the X coordinate and the second value is the Y coordinate. The value of the X and Y coordinates are calculated from the top left-hand corner of the element. In the example below, the origin of the transform is set to 3 ems from the left of the element on the X axis and the bottom of the parent element on the Y axis:

-o-transform-origin: 3em bottom;

Technically the transform origin is applied by calculating the negation of the transform-origin value (-3em on the X axis, and the top of the element in the example above), translating the element to this value, then applying the transform that is specified in the transform property, then translating the element by the actual value of the transform-origin.

Transforming the element

Once the point of origin is set (or left to the default), the actual type of transform can be applied. This is set with the transform property with a list of one or more transforms as the value.


We'll start with a simple translate transform. This moves the element from its original position in the document to the new location specified by the X and Y co-ordinates supplied.

The following translate example moves the div element 50 pixels to the left and down 100 pixels from the centre of the element:

-o-transform: translate(50px, 100px);

We've created a second version of the translate example with a faded out element moved to the original position the element was in before it was transformed in order to highlight the transform.

Note that a transform doesn't affect the flow of the document, so if the element is moved to where another element is already positioned it will overlap that element rather than the content flowing around or under it. Elements later in the document also will not take up the space vacated by the transformed element. This is illustrated in this translate transform flow example where the blue div element stays in its natural position.

If the Y co-ordinate value is not specified then it is assumed to be zero. Negative values will move the element to the left and above the point of origin respectively. Alternatively the X and Y co-ordinate can be specified individually using translateX and translateY:

-o-transform: translateX(50px);
-o-transform: translateY(100px);

CSS transforms are very similar to the existing SVG transforms, except they are applied via CSS rather than a XML attribute. They also apply to HTML, while with SVG the HTML has to be wrapped in a SVG foreignObject element inside the actual SVG file.

In SVG, transforms are applied using the transform attribute. The SVG equivalent of the initial translate example would be:

<rect transform="translate(50px, 100px)" />

Note for the subsequent SVG examples we've missed out the styling information and attributes such as the width and height. We've also assumed the point of origin for the transforms are set up correctly.


The scale transform function scales the element it is applied on by the value specified from the point of origin. The following scale example scales the div element 2.5 times from the top left of the element:

-o-transform: scale(2.5);
-o-transform-origin: left top;

In this example only one value was specified. In this case the Y scaling direction is set to the same value as the X scaling direction. If you only want to scale in one direction you can use the scaleX or scaleY transform functions. To scale an image smaller, a value less than 1 is used such as 0.5 for halving the size of the element.

We've created a second scaling example to highlight what happens if the point of origin for the scale is set to the centre point of the element. Here the element scales slightly off the screen to the left and overlaps the element above it, as the element scales in all directions equally from the centre point.

In SVG, an initial scale example would be the following:

<rect transform="scale(2.5)" />


The skew transform function skews the element along the X or Y axis, or both. The value is specified in degrees (deg), and if only one value is specified the skew on the Y axis is set to 0 or no skew. In the following skew example the element is skewed 10 degrees on the X axis, so that it is leaning to the left, and 20 degrees on the Y axis, so that it is sloping from top to bottom. The skew is applied from the default centre point of the element:

-o-transform: skew(10deg, 20deg);

As with other transform functions the X and Y directions can be specified individually using skewX and skewY. If a negative value is supplied the skew is performed in the opposite direction, i.e. leaning to the right for a negative skew on the X axis, and from bottom to top for a negative skew on the Y axis.

In SVG there is no transform type for skew alone. The X and Y axes have to be skewed individually with skewX and skewY respectively:

<rect transform="skewX(10) skewY(20) />


The rotate transform function rotates the element around the point of origin and as with skewing is specified in degrees. The following rotation example rotates the element 30 degrees clockwise:

-o-transform: rotate(30deg);

A negative value will rotate the element anti-clockwise.

The SVG equivalent would be:

<rect transform="rotate(30) />

Combining transforms

Multiple transforms can be applied to one transform property using a space-separated list. When multiple transforms are used, they are applied one after another linearly from left to right. In the following multiple transform example, the element is initially scaled by a factor of two, then rotated 45 degrees clockwise, then moved 80 pixels along the X axis:

-o-transform: scale(2) rotate(45deg) translate(80px);

Note how the element doesn't just move to the right when it is translated 80 pixels. This is because the X and Y axes are transformed with the rotate function, so the X axis is pointing from top to bottom and from left to right. Also note that the transform origin moves with each transform. The initial transform origin is the centre of the element in its initial position, while the transform origin for the rotate transform is the centre point of the element after it has been scaled, and so on.

The SVG equivalent would be:

<rect transform="scale(2) rotate(45) translate(80px) />

Pulling it all together into a showcase

We’ve created an image gallery showcase to show off transitions and transforms with something more exciting than a bunch of red boxes. This is not meant to be a practical way to display or interact with photos, just a display of what is possible. Try out the transitions and transforms image gallery showcase.

There is not much to this that hasn't been explained previously. Each image is added to the document one after another. If transforms are not applied they will fill the width of the window then display on the next row. The figure element that wraps each image is then targeted individually using a :nth-of-type() CSS3 selector and the relevant transform is applied to it. This selector is used to avoid using classes but it may be more practical to explicitly target each element with an ID or class, depending on whether elements will change position in the DOM or not. I didn't worry about backwards compatibility of the CSS3 selectors as all browsers that support CSS transforms and transitions also support :nth-of-type().

Each figure element has a chain of transforms applied to it. They first have a scale applied to them to shrink them closer to a thumbnail size, then a transition applied to them to move them to the desired position in the photo stack. Finally a rotation is applied to make the images look randomly placed. Note that if you want to override one of the transforms you have to override them all otherwise the other transforms will move back to their original non-transformed state.

The transition-property value is set to all so any property that changes will have a transition applied to it. In the case of this demo, the z-index and box-shadow property values are changed on hovering the element so that the user can see the image more clearly. The z-index is animated over 0.5 second. In Safari/WebKit z-index doesn't work on transformed elements.

A small amount of JavaScript has been added to handle click events. When clicking on an image it will transition to the top left of the document, rotate so that it changes back to 0 degrees, and move to a higher z-index so it is on top of any elements in the same position. You will see when clicking on an image that it animates from its position to the new location and rotates over a half second. As with the hover transition, in Safari the transition effect isn't applied and instead moves instantly.


In this article we've covered the CSS3 transitions and CSS 2D transforms supported by Opera Presto 2.4 and above. I've also briefly covered the similar functionality in SVG from which they were inspired. This will allow you to transfer the knowledge when using both HTML and SVG.

With CSS transitions we've covered how to specify which properties to transition, how long the transition takes, how to delay the transition and how to control the speed of the transition. With CSS3 transforms we've covered how to set the origin of the transform and the types of transforms that can be applied. Finally we've pulled together both techniques into an image gallery showcase to show what kind of effects are possible.


David Storey heads up the Open the Web initiative at Opera. This small global team is tasked with improving the compatibility of web sites across Opera's wide range of browsers, as well as promoting web standards, accessibility and best practices, so that the Web will work for all standards-aware browsers, platforms, devices and users. He is a member of the W3C Mobile Web Best Practices Working Group.

On his blog, Slightly Ajar, he discusses this work, as well as random topics, from travel to music.

David previously worked for CERN, home of the World Wide Web, before taking up his post at Opera Software.

This article is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported license.


The forum archive of this article is still available on My Opera.

  • photo


    Wednesday, December 21, 2011

    Nice writeup. Note, that all your pages pointing to working online samples are 404'ed.
  • photo


    Tuesday, August 28, 2012

    Multiple transform does not work when translate is percentage like in example below.
    -o-transform: scale(1) rotate(45deg) translate(20%, -100%);
  • photo

    Chris Mills

    Tuesday, August 28, 2012

    @cactusek they seem to work ok for me - for example, the line you've provided above works fine in my test file. I wonder if it's a bug, and/or a strange combination of things that doesn't work? What version of Opera are you using, and on what platform? Have you got a test file you can point me at?

  • photo


    Sunday, February 3, 2013

    Multiple axis rotation any time soon?
No new comments accepted.