The Guide To CSS Animation: Principles and Examples

Advertisement

With CSS animation now supported in both Firefox and Webkit browsers, there is no better time to give it a try. Regardless of its technical form, whether traditional, computer-generated 3-D, Flash or CSS, animation always follows the same basic principles. In this article, we will take our first steps with CSS animation and consider the main guidelines for creating animation with CSS. We’ll be working through an example, building up the animation using the principles of traditional animation. Finally, we’ll see some real-world usages.

screenshot

CSS Animation Properties

Before diving into the details, let’s set up the basic CSS:

Animation is a new CSS property that allows for animation of most HTML elements (such as div, h1 and span) without JavaScript or Flash. At the moment, it’s supported in Webkit browsers, including Safari 4+, Safari for iOS (iOS 2+), Chrome 1+ and, more recently, Firefox 5. Unsupported browsers will simply ignore your animation code, so ensure that your page doesn’t rely on it!

Because the technology is still relatively new, prefixes for the browser vendors are required. So far, the syntax is exactly the same for each browser, with only a prefix change required. In the code examples below, we use the -webkit syntax.

All you need to get some CSS animation happening is to attach an animation to an element in the CSS:

/* This is the animation code. */
@-webkit-keyframes example {
   from { transform: scale(2.0); }
   to   { transform: scale(1.0); }
}

/* This is the element that we apply the animation to. */
div {
   -webkit-animation-name: example;
   -webkit-animation-duration: 1s;
   -webkit-animation-timing-function: ease; /* ease is the default */
   -webkit-animation-delay: 1s;             /* 0 is the default */
   -webkit-animation-iteration-count: 2;    /* 1 is the default */
   -webkit-animation-direction: alternate;  /* normal is the default */
}

First, we have the animation code itself. This can appear anywhere in the CSS, as long as the element that you’re animating can find the relevant animation-name.

When assigning the animation to your element, you can also use the shorthand:

div {
-webkit-animation: example 1s ease 1s 2 alternate;
}

We can cut this down further by not entering all of the values. Without a value specified, the browser will fall back to the default.

Those are the basics. We’ll work through more code in the following section.

Applying Principles of Traditional Animation

Disney — the masters of traditional animation, in my opinion — developed the 12 principles of traditional animation early on and documented them in its famous book The Illusion of Life. These basic principles can be applied to all manner of animation, and you needn’t be an expert in animation to follow along. We’ll be working through an example of CSS animation that uses the 12 principles, turning a basic animation into a more believable illusion.

These may just be bouncing balls, but you can see a world of difference between the two versions.

This example demonstrates the features of CSS animation. In the code below, we use empty divs to show how it works; this isn’t the most semantic way to code, as we all know, but the point is to show how simple it is to bring a page to life in a way that we haven’t been able to do before in the browser.

Squash and Stretch

screenshot

The crude bouncing ball is a great demonstration of this first point. If the ball falls at a high velocity and hits the floor, you’ll see it squash down from the force and then stretch back out as it bounces up.

At a basic level, this should give our animation a sense of weight and flexibility. If we dropped a bowling ball, we wouldn’t expect it to flex at all — it might just damage the floor.

We can apply this squash and stretch effect through a CSS3 property, transform:

@-webkit-keyframes example {
   0% { -webkit-transform: scaleY(1.0); }
   50% { -webkit-transform: scaleY(1.2); }
   100% { -webkit-transform: scaleY(1.0); }
}

This will scale the object lengthwise (on the y axis, up and down) to 1.2 times the original size, and then revert to the original size.

We’re also using more complex timing for this animation. You can use from and to for basic animations. But you can also specify many actions for your animation using percentages, as shown here.

That covers the squashing. Now we need to move the object using translate. We can combine transforms together:

50% {
-webkit-transform: translateY(-300px) scaleY(1.2);
}

The translate property allows us to manipulate the object without changing any of its base properties (such as position, width or height), which makes it ideal for CSS animation. This particular translate property makes it look like the ball is bouncing off the floor at the mid-point of the animation.

(Please note: to view the sample animations, you’ll need the latest version of Firefox, Chrome or Safari. At the time of writing, Safari provides the best viewing experience of CSS animation.)

Yes, it still looks rubbish, but this small adjustment is the first step in making this animation more believable.

Anticipation

Anticipation adds suspense, or a sense of power, before the main action. For example, the bend in your legs before you jump helps viewers anticipate what will come next. In the case of our bouncing ball, simply adding a shadow beforehand suggests that something is falling from above.

We’ve added another div for the shadow, so that we can animate it separate from the ball.

To create anticipation here, we keep the ball from dropping into the scene immediately. We do this simply by adjusting the percentage timings so that there is no movement between the start point and the first action.

@-webkit-keyframes example {
   0% { -webkit-transform: translateY(-300px) scaleY(1.2); }
   35% { -webkit-transform: translateY(-300px) scaleY(1.2); } /* Same position as 0% */
   65% { -webkit-transform: translateY(0px) scaleY(1.2); }    /* Starts moving after 35% to this position */
   67% { -webkit-transform: translateY(10px) scaleY(0.8); }
   85% { -webkit-transform: translateY(-100px) scaleY(1.2); }
   100% { -webkit-transform: translateY(0px); }
}

At the 35% point of the animation, the ball is in the same location, positioned off the stage, not moving. Then, between 35% and 65%, it suddenly moves onto the stage, and the rest of the animation follows.

You can also use animation-delay to create anticipation:

div {
-webkit-animation-delay: 1s;
}

However, this could have an undesired effect. The animation-delay property simply ignores any animation code until the specified time. So, if your animation starts in a position different from the element that you are animating, then the object will appear to suddenly jump as soon as the delayed animation starts.

This property works best for looping animations that begin and end in the same location.

Staging

screenshot

Try to give a stage to the scene; put the animation in context. Thinking back to Disney films, what would they be without the fantastic background artwork? That’s half of the magic!

The stage is also key to focusing attention. Much like on a theater stage, lighting will be cast on the most important area. The stage should add to the illusion. With our bouncing ball, I’ve added a simple background to focus on where the ball will land. Now the viewer knows that the action will take place in the center, and the scene is no longer lost in snow.

Straight-Ahead vs. Pose to Pose

In traditional animation, this is a choice in how to construct your animation. The straight-ahead option is to draw out every frame in the sequence. The pose-to-pose option is to create a few keyframes throughout the sequence, and then fill in the gaps later. Filling in these gaps is known as “in-betweening,” or “tweening,” a familiar term for those used to animating in Flash.

With CSS animation, we typically use the latter, pose to pose. That is, we’ll add keyframes of action, and then the browser will “tween” the intermediate frames automatically. However, we can learn from the straight-ahead technique, too. The browser can do only so many effects; sometimes, you have to do it the hard way and put in more animation hard-graft to get the desired effect.

Follow-Through and Overlapping

Also known as physics! Follow-through and overlapping are more commonly used in character animation for body movement, such as to show arms swaying as the character drops them or long hair falling. Think of someone with a big stomach turning quickly: their body will turn first, and their bulging gut will follow shortly after.

For us, this means getting the physics right when the ball drops. In the demonstrations above, the ball drops unnaturally, as if beyond the control of gravity. We want the ball to drop and then bounce. However, this is better achieved through the next principle.

Slow In and Out

This has to do with speeding up and slowing down. Imagine a car that is speeding along and has to come to a stop. If it were to stop instantly, it wouldn’t be believable. We know that cars take time to slow down, so we would have to animate the car braking and slowly coming to a stop.

This is also relevant to showing the effect of gravity. Imagine a child on a swing. As they approach the highest point, they will slow down. As they come back down and gain speed, their fastest point will be at the bottom of the arc. Then they will rise up on the opposite side, and the action repeats.

screenshot

Back to our example, by adjusting the in and out speeds, we can make the ball much more believable (finally).

When the ball hits the floor, the impact will make it bounce back up instantly. As it reaches its highest point, it will slow down. Now it looks like the ball is really dropping.

In CSS, we can control this with the animation-timing-function property:

-webkit-animation-timing-function: ease-out;

This property takes the following values:

  • ease-inSlow at the beginning, and then speeds up.
  • ease-outFast at the beginning, and then slows to a stop.
  • ease-in-outStarts slow, speeds up in the middle, and then slows to a stop.
  • linearMoves at an even speed from start to finish.

You can also use the bezier-curve function to create your own easing speeds.

Arcs

screenshot

Similar to the follow-through principle of physics, arcs follow the basic principle of “what goes up must come down.” Arcs are useful in thinking about the trajectory of an object.

Let’s throw the ball in from the left of the stage. A convincing animation would predict the arc along which the ball will fall; and in our example it will have to predict the next arc along which the ball will fall when it bounces.

This animation can be a bit more fiddly to adjust in CSS. We want to animate the ball going up and down and side to side simultaneously. So, we want our ball to move in smoothly from the left, while continuing the bouncing animation that we’ve been working on. Rather than attempt to capture both actions as one animation, we’ll do two separate animations, which is easiest. For this demonstration, we’ll wrap our ball in another div and animate it separately.

The HTML:

<div class="ball-arc">
   <div class="ball"></div>
</div>

And the CSS:

.ball-arc {
-webkit-animation: ball-x 2.5s cubic-bezier(0, 0, 0.35, 1);
}
   /* cubic-bezier here is to adjust the animation-timing speed.
   This example makes the ball take longer to slow down. */

@-webkit-keyframes ball-x {
   0% { -webkit-transform: translateX(-275px); }
   100% { -webkit-transform: translateX(0px); }
}

Here, we have one animation to move the ball sideways (ball-x) and another animation to bounce the ball (ball-y). The only downside to this method is that if you want something really complex, you could end up with a code soup with poor semantics!

Secondary Action

A secondary action is a subtlety that makes the animation much more real. It addresses the details. For example, if we had someone with long hair walking, the primary action would be the walking, and the secondary action would be the bounce of the hair, or perhaps the ruffling of the clothes in the wind.

In our example, it’s much simpler. By applying more detail to the ball, we make the secondary action the spinning of the ball. This will give the illusion that the ball is being thrown in.

Rather than add another div for this animation, we can be more specific by adding it to the new img element that we’re using to give the ball texture.

.ball img {
-webkit-animation: spin 2.5s;
}

@-webkit-keyframes spin {
   0% { -webkit-transform: rotate(-180deg); }
   100% { -webkit-transform: rotate(360deg); }
}

Timing

screenshot

This is simply the timing of your animation. The better the timing of the animation, the more realistic it will look.

Our ball is a perfect example of this. The current speed is about right for a ball this light. If it were a bowling ball, we would expect it to drop much more quickly. Whereas, if the animation were any slower, then it would look like we were playing tennis in space. The correct timing basically helps your animation look realistic.

You can easily adjust this with the animation-duration property, and you can adjust the individual timings of your animation using percentage values.

Exaggeration

Cartoons are known for exaggeration, or impossible physics. A cartoon character can contort into any shape and still manage to spring back to normal. In most cases, though, exaggeration is used for emphasis, to bring to life an action that would otherwise look flat in animation.

Nevertheless, use exaggeration modestly. Disney had a rule to base its animations on reality but push it slightly further. Imagine a character running into a wall; its body would squash into the wall more than expected, to emphasize the force of impact.

We’re using exaggeration in combination with squash and stretch to make it really obvious when the ball hits the floor. I’ve also added a subtle wobble to the animation. Finally, we also stretch the ball in and out as it bounces up and down to emphasize the speed.

Just as when we added one animation onto another, here we’ll add another div, which will wobble in sync with the ball hitting the floor:

@-webkit-keyframes wobble {

0%, 24%, 54%, 74%, 86%, 96%, 100% {
   -webkit-transform: scaleX(1.0);
/* Make the ball a normal size at these points */
}

25%, 55%, 75% {
   -webkit-transform: scaleX(1.3) scaleY(0.8) translateY(10px);
/* Points hitting the floor: squash effect */
}

30%, 60%, 80% {
   -webkit-transform: scaleX(0.8) scaleY(1.2);
/* Wobble inwards after hitting the floor */
}

75%, 87% {
   -webkit-transform: scaleX(1.2);
/* Subtler squash for the last few bounces */
}

97% -webkit-transform: scaleX(1.1);
/* Even subtler squash for last bounce */
}

}

The code looks more complex than it is. It’s simple trial and error. Keep trying until you get the right effect!

Solid Drawing and Appeal

I have nothing more to teach you… at least not in code. These final two animation principles cannot be shown in code. They are skills you will have to perfect in order to make truly amazing animations.

When Disney started production on Snow White, it had its animators go back to life drawing classes and learn the human form again. This attention to detail is evident in the film, which goes to show that good animation requires solid drawing skills and sound knowledge of the form you are animating.

Most CSS animation will likely not be as complex as intricate figure animations, but the basic principle holds true. Whether a door is opening to reveal content or a “contact us” envelope is being sealed and delivered, the animation should be believable, not robotic… unless you’re animating a machine.

The appeal, or charisma, of each character will be unique. But as Disney has always shown, anything can have character: a teapot, a tree, even spoons. But with CSS, consider how the overall animation will contribute to the design and make the overall experience more satisfying. We don’t want to make clippy animations here.

Go Forth And Animate!

CSS animation is a great new feature. As with every new CSS feature, it will be overused and misused at first. There is even the slight danger that we’ll see a return of those long-winded Flash-style animated splash pages. Although I have faith in the Web community not to do this.

CSS animation can be used to really bring a website to life. While the code for our bouncing ball may not be the most semantic, it hopefully shows how simple it can be to bring almost anything on the page to life with CSS.

It can bring much-needed interaction to your elements (sans Flash!); it can add excitement to the page; and in combination with JavaScript, it can even be an alternative way to animate for games. By taking in the 12 principles above and working away at your animation, you can make your websites more convincing, enticing and exciting, leading to a better experience overall.

CSS Animation Tools

While knowing the CSS itself is great, plenty of tools are popping up that will help you animate. The 12 principles apply regardless, but if you’re worried about the code, these great tools let you try out CSS animation without getting too technical.

CSS Animation in the Wild

Finally, to get you excited about what is possible, here are some great examples of CSS animation being used on live websites:

(al) (vf) (kw)

↑ Back to top

Pixel pusher by day, illustrator by night. Tom is a lead designer and when he isn't ruining his eyes in front of the computer, he'll be ruining them in front of a games console.

  1. 1

    Informative and helpful post. Keep it up!

    0
  2. 2

    For improved reality the shadow should also change in size and lightning.

    Since the theoretical light source is placed at the top, when the ball is near the light, the shadow at the ground should be wider and more transparent. Opposite case when the ball hits the ground.

    This is my new favorite article on Smashing Magazine.
    Great Content!

    0
    • 3

      Agreed! For the demonstration, I decided to go against changing the size or blurring out the shadow to keep it a bit more simple… but it wouldn’t take too much more to add those effects – I’m glad you liked the article!

      0
  3. 4

    Have you tried Hippo Animator?
    It does all this without the need to do any coding and it also works on IE6.
    hippostudios.co.uk

    0
  4. 5

    Helpful post. Thanks!!!

    0
  5. 6

    Loved reading and observing the effects.Thanks !!

    0
  6. 7

    Wow, lots of informative and theoretical readings.
    The name of all properties are quite long though, very flexible and good moving!

    0
  7. 8

    Solid article, but it’s a shame it dodges the most important principle: -when- to use css animations.

    0
    • 9

      Yup, unfortunately that difficult decision is unto the designer for the specific scenario, so it’s quite difficult to answer!

      Will the design benefit from the animation?
      Will the design suffice without it (for the browsers that can’t handle them)?

      But it can also be used in subtle ways that can be put into any design, such animating content boxes appearing. It’s up to you, but hopefully this article helps with the animation theory :)

      0
    • 10

      I used in a recent project. We build an animated book for Ipad and others tables with Android. No canvas, just CSS3 /,,/

      0
  8. 11

    Michaël van Oosten

    September 14, 2011 7:31 am

    Interesting read and the result is very nice! It’s really cool to see how much can be achieved with some simple lines of CSS, but I’m not sure: should we animate with CSS instead of JS? What is your opinion?

    0
    • 12

      That decision is up to you! There are benefits to both methods, but personally I find that CSS animation in combination with newer CSS3 properties such as transform creates an overall better experience and is typically smoother.

      However on the downside, it’s limited to newer browsers. Internet Explorer and Opera currently have no support, although I’m sure it’s only a matter of time.

      0
  9. 13

    “Our ball is a perfect example of this. The current speed is about right for a ball this light. If it were a bowling ball, we would expect it to drop much more quickly. ”

    This is only a minor point in the context of the article but it’s worth taking into account for realistic animation; mass doesn’t affect acceleration and so given neglible wind resistance (e.g. indoor environments or a calm day) a bowling bowl will fall at pretty much the same speed as a tennis ball.

    0
    • 14

      When you say that a bowling ball and a tennis ball fall at the same speed, are you also concluding that a feather and a bowling ball fall at the same speed?

      0
      • 15

        I’ll leap in for Iain and say no, he isn’t. But that is only because of the effects of wind resistance become considerable for a feather. A parachutist weighs more than a bowling ball, but again the wind resistance (not the weight) slows her down.

        You’re certainly not the first person to incorrectly assume that an object’s mass will noticeably affect the speed at which it falls to earth (the author of the article did as well), but you’re wrong.

        See the simple version of the wikipedia entry (http://simple.wikipedia.org/wiki/Gravity) for further explanation.

        0
      • 16

        With no wind-resistance, yes they would.

        0
      • 17

        Just to clarify what Michael stated above, a feather has enough surface area relative to it’s mass that the normal atmosphere provides enough resistance to slow it’s fall so no, I wouldn’t conclude that it would fall at the same speed as the bowling ball. It would however fall at the same speed as the bowling ball if they were dropped in a vacuum, and infact the moon landing astronauts demonstrated this with a hammer and a feather.

        In the tennis/bowling ball example the objects are a similar enough size and shape that the normal atmosphere with negligible wind would not slow one down significantly more than the other.

        0
    • 18

      Galileo rolls over in his grave every time someone uses the feather vs. something heavy argument. This always jumps out at me every time I see someone makes the false assumption. Thanks Iain for pointing it out, saved me the trouble.

      Other than that minor gaffe, great article. I just hope that this doesn’t signal the return of animation as a common (overused) design feature.

      0
  10. 19

    I may be a n-00b asking this but why I opt to go with animations as opposed to using the canvas element and animating what I need to? I kind of need to do this for my website, but I am unsure as to what to use. Canvas or animations.

    0
    • 20

      This one is really up to you! It depends on what you’re going to be doing with the website and content.

      If you’re animating content, then I’d say CSS animation, but if it’s purely decorative (such as an animated background) then I think canvas would probably be better.

      0
  11. 21

    This one really hit me! Great article (and great animation)! Like always you guys at smashing magazine not only show the technical details but provide very profund background information and context; that’s what makes the smashing articles so precious!

    0
  12. 22
  13. 23

    Cool, I fell in love with css3 now! :-)

    0
  14. 24

    Very helpful! Thank you very much – i want more from this :)

    0
  15. 25

    WOW! Truly Amazing, the possibilities are really unlimited with CSS3. Bravo [SM] Keep them coming..

    0
  16. 26

    Very informative post.
    Well done!

    0
  17. 27

    Erikas Mališauskas

    September 15, 2011 3:21 am

    that’s unbelievable! great post.

    0
  18. 28

    Come on man.. that spiderman animation (http://www.optimum7.com/css3-man/animation.html) is a joke! Looks like somesort of flashanimation made in 1997

    0
    • 29

      It’s still early days for CSS animation, I’m sure we’re yet to see something truly amazing!

      Regardless of the animation itself, the technique and skill behind the code is pretty amazing.

      0
  19. 30

    First of all, congrats! The best animation tutorial i’ve read.

    A question:

    Does CSS3 Animation has hardware acceleration? I ask this because Canvas does not has hardware acceleration ( I think ) , so, in mobile devices the performace ( depending of complexity ) can be bad, then, CSS3 can be a solution for devices low-end?

    0
    • 31

      I believe all of the currently supported browsers (Safari, Chrome and Firefox) have hardware acceleration for this.

      It’s definitely true for iOS devices, as test have shown that using CSS animation is superior to using Javascript animation on these devices.

      I can’t say for all devices, but I think it is seen as a good solution for low end devices like phones.

      0
  20. 32

    This is really great. Unfortunately though, one of the leading browsers, opera, still doesn’t support it which is quite puzzling. Had a debate with this guy on Opera forums. He called css 3 animations “dumb”.

    The webkit browsers also support 3D matrix. The effects are simply brilliant. I am doing experiments with CSS 3 myself.

    http://dulcetsolutions.com/lab/experiments/intro_animation/

    http://dulcetsolutions.com/lab/experiments/cars/1-a.php

    Please see only in webkit. (Chrome)

    0
  21. 33

    Amazing article! CSS has come quite a long way to include animation. This is an awesome example of its potential.

    0
  22. 34

    oh it’s really awesome..thanks…keep it up… :)

    0
  23. 35

    “The browser can do only so many effects; sometimes, you have to do it the hard way and put in more animation hard-graft to get the desired effect.”

    Damn. I was really hoping that we could use CSS3 + HTML for our animated storybook (so it would play on multiple tablets) but sadly it is still too primitive.

    Quality lifelike animation, specifically Disney/Pixar/Dreamworks-level animation, requires a large variety of changes in shape and line thickness for each animated element. This cannot be achieved simply by distorting the X or Y axis. For instance, imagine that bouncing ball is a rabbit hopping. If you stretch/squash the entire shape, he will look unrealistic. If you stretch/squash just the legs and leave the rest of the body intact, it will still look odd. The ears have to move and turn slightly, the hips have to raise up as the legs push down, and on and on. Yes, you can break up the parts of the body into separate divs, as Andrew Hoyer bravely attempted with his Walking Man but it still looks stiff, mainly because his hips, torso and shoulders do not sway or lean forward, the muscles are static in shape, and the feet do not change shape. Plus, you end up with a zillion lines of code.

    Machine-automated “tweens” work well for animating robots and other man-made elements (rockets, etc) but are useless for anything organic, i.e. anything with life. I love a perfect bezier curve as much as anyone, but organic lifeforms generally follow asymmetric or random patterns of timing and movement. Muscles change shape as they tense and relax, which in turn changes the shape of a character. And the human eye is hard-wired to detect the tiniest differences between live creatures and inanimate objects, perhaps for survival (“That’s not a tiger! It’s a tiger-shaped rock!”).

    I really wish I could use CSS3 and I’m still hopeful that these issues can be resolved in the future, but CSS3 is not yet ready for realistic life-like animation IMHO.

    0
  24. 36

    got a resource anymore…
    thanks^^

    0
  25. 37

    Thank you, this is just what I am in need of right now.

    0
  26. 38

    well.. still it’s better to use flash for that kind of stuff

    0
  27. 39

    I was wondering what the part the Javascript reference in your code was playing?

    The animation is awesome!

    0
    • 40

      Ah, that’s just a small bit of JS to restart the animation. Simply because there isn’t a way in CSS to restart the animation, but this kind of behaviour control really should be kept within Javascript.

      All it does it remove and straight away add the class back in to restart the animation.

      0
  28. 41

    This is incredible! This is what keeps me excited about my job. Can’t wait to see the progression of web design over the next year.

    0
  29. 42

    Nice and interesting… Theres no way I’m gonna use something like this in the field though… so much trial and error and messing about to make it look anything good.

    0
  30. 43

    Wow! This is everything I ever needed to know about animating CSS3. So much better than Flash. Thanks!

    0
  31. 44

    This is one of the best articles I’ve read on SmashingMagazine in a long time. Thank you very very much. Impressive! I’m buying that Disney book as well, seems to contain a lot of good information.
    Cheers!

    0
  32. 45

    Are you sure the ARC is actually a curved ARC? It seems that the trajectory is a straight line. As you said, the cubic-bezier is for the animation speed, not the trajectory. How can we achieve a curved trajectory using CSS3 only?

    0
  33. 46

    I have what may be a relatively stupid question in the arc example say i want to completely remove any javascript from the page , what would i need to change in the css so that the animation starts on page load , i dont need it to play again just the once on page load…

    Thanks

    0
  34. 47

    I thing to created animations with JQuery or other script only, now i know CSS3 can make it too. Thanks for useful tuts.

    0
  35. 48

    Awesome css animations here is simple Animated smiley with css3 anyone can create with ease http://iwebeffects.com/demos/css-smiley.html

    0
  36. 49

    Well..where you adding the ball image in your code…?thanks

    0
  37. 50

    Nice work Tom!

    Nice effects to look forward to in the browser

    Thanks

    0
  38. 51

    Wow, awesome introduction into the magical world of animation with css. This is exactly what makes webdesign so exciting to me: new features, new ways of doing things, new user experience through innovation-I’m loving it. Keep up the great work!

    0
  39. 52

    Links is dead :/

    0
  40. 53

    CSS animations are part of the course at w3university
    http://www.w3university.co.uk/csscourseanim.html

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top