
When we talk about using animation we often discuss the practical ways we can create transitions, animations, and so on. Animation is a powerful way to make our designs stand out, but rather than focus on how individual animations work, the way multiple animations combine can create something even more powerful.
Appeal
Disney’s 12 basic principles of animation defines “Appeal” as corresponding to “charisma in an actor”. It’s how the realism, style, and substance of animation adds up to create a sense of character that viewers find real and interesting.

It’s a technique used to great effect by Stripe. Stripe is a payments processor, and provides tools for embedding payment forms in sites. These forms are famous for being beautifully designed, particularly their use of animation.
In this article I’ll discuss how multiple animations can be used to create an effect more than the sum of its parts, and show how you can use this knowledge in a practical way.
Social Media Card
In this example we’ll use animation to present a card with details and links to web and other social accounts.
When a button is pressed, it will prompt a card to appear with some follow options. Rather than use a plain old modal window, we’ll use animation to make it more interesting.
To make it appealing, there are several animations taking place. The various pieces animate in and introduce each element in order. This helps the viewer understand the connection between what they’ve pressed and the content and the order of animations helps draw the eye to the follow icons.
Setting up the HTML
To start we need a button to press and a card to show. The card will be hidden when the content is first shown.
<button class="show-card"><img src="https://avatars2.githubusercontent.com/u/853536?v=3&s=48"> Press here</button><article class="card"><section class="close"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 32 32"><path d="M31.708 25.708c-0-0-0-0-0-0l-9.708-9.708 9.708-9.708c0-0 0-0 0-0 0.105-0.105 0.18-0.227 0.229-0.357 0.133-0.356 0.057-0.771-0.229-1.057l-4.586-4.586c-0.286-0.286-0.702-0.361-1.057-0.229-0.13 0.048-0.252 0.124-0.357 0.228 0 0-0 0-0 0l-9.708 9.708-9.708-9.708c-0-0-0-0-0-0-0.105-0.104-0.227-0.18-0.357-0.228-0.356-0.133-0.771-0.057-1.057 0.229l-4.586 4.586c-0.286 0.286-0.361 0.702-0.229 1.057 0.049 0.13 0.124 0.252 0.229 0.357 0 0 0 0 0 0l9.708 9.708-9.708 9.708c-0 0-0 0-0 0-0.104 0.105-0.18 0.227-0.229 0.357-0.133 0.355-0.057 0.771 0.229 1.057l4.586 4.586c0.286 0.286 0.702 0.361 1.057 0.229 0.13-0.049 0.252-0.124 0.357-0.229 0-0 0-0 0-0l9.708-9.708 9.708 9.708c0 0 0 0 0 0 0.105 0.105 0.227 0.18 0.357 0.229 0.356 0.133 0.771 0.057 1.057-0.229l4.586-4.586c0.286-0.286 0.362-0.702 0.229-1.057-0.049-0.13-0.124-0.252-0.229-0.357z" fill="#aaa"></path></svg></section><section class="details"><h2 class="name">Donovan Hutchinson</h2><p class="description">Donovan is a Dublin-based front-end developer with a passion for CSS, animation and making the web fun.</p></section><section class="headshot"><img src="https://avatars2.githubusercontent.com/u/853536?v=3&s=280"></section><section class="icon-bar"><ul><li><a href="http://cssanimation.rocks"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"><path d="M32 19l-6-6v-9h-4v5l-6-6-16 16v1h4v10h10v-6h4v6h10v-10h4z" fill="#fff"></path></svg></a></li><li><a href="https://twitter.com/cssanimation"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"><path d="M32 6.076c-1.177 0.522-2.443 0.875-3.771 1.034 1.355-0.813 2.396-2.099 2.887-3.632-1.269 0.752-2.674 1.299-4.169 1.593-1.198-1.276-2.904-2.073-4.792-2.073-3.626 0-6.565 2.939-6.565 6.565 0 0.515 0.058 1.016 0.17 1.496-5.456-0.274-10.294-2.888-13.532-6.86-0.565 0.97-0.889 2.097-0.889 3.301 0 2.278 1.159 4.287 2.921 5.465-1.076-0.034-2.088-0.329-2.974-0.821-0.001 0.027-0.001 0.055-0.001 0.083 0 3.181 2.263 5.834 5.266 6.437-0.551 0.15-1.131 0.23-1.73 0.23-0.423 0-0.834-0.041-1.235-0.118 0.835 2.608 3.26 4.506 6.133 4.559-2.247 1.761-5.078 2.81-8.154 2.81-0.53 0-1.052-0.031-1.566-0.092 2.905 1.863 6.356 2.95 10.064 2.95 12.076 0 18.679-10.004 18.679-18.68 0-0.285-0.006-0.568-0.019-0.849 1.283-0.926 2.396-2.082 3.276-3.398z" fill="#fff"></path></svg></a></li><li><a href="https://github.com/cssanimation"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"><path d="M0 17.388c0 1.45 0.136 2.762 0.407 3.935s0.647 2.193 1.127 3.059 1.090 1.627 1.831 2.285c0.741 0.657 1.544 1.195 2.41 1.612s1.854 0.756 2.965 1.017c1.111 0.261 2.245 0.443 3.403 0.548s2.431 0.156 3.818 0.156c1.398 0 2.676-0.052 3.834-0.156s2.295-0.287 3.411-0.548 2.11-0.6 2.981-1.017c0.871-0.417 1.68-0.954 2.425-1.612s1.361-1.419 1.846-2.285 0.863-1.886 1.134-3.059c0.271-1.174 0.407-2.486 0.407-3.935 0-2.587-0.866-4.825-2.597-6.713 0.094-0.25 0.18-0.535 0.258-0.853s0.151-0.772 0.219-1.361c0.068-0.589 0.042-1.27-0.078-2.042s-0.342-1.56-0.665-2.363l-0.235-0.047c-0.167-0.031-0.441-0.023-0.822 0.024s-0.824 0.141-1.33 0.282c-0.506 0.141-1.158 0.412-1.956 0.814s-1.64 0.905-2.527 1.51c-1.523-0.417-3.615-0.626-6.275-0.626-2.65 0-4.736 0.209-6.259 0.626-0.887-0.605-1.734-1.108-2.543-1.51s-1.453-0.673-1.933-0.814c-0.48-0.141-0.928-0.232-1.346-0.274s-0.681-0.055-0.79-0.039-0.196 0.034-0.258 0.055c-0.323 0.803-0.545 1.591-0.665 2.363s-0.146 1.453-0.078 2.042 0.141 1.043 0.219 1.361c0.078 0.318 0.164 0.602 0.258 0.853-1.732 1.888-2.598 4.126-2.598 6.713zM3.928 21.315c0-1.502 0.683-2.879 2.050-4.131 0.407-0.376 0.881-0.66 1.424-0.853s1.155-0.302 1.839-0.329c0.683-0.026 1.338-0.021 1.964 0.016s1.398 0.086 2.316 0.149c0.918 0.063 1.711 0.094 2.379 0.094s1.46-0.031 2.378-0.094c0.918-0.063 1.69-0.112 2.316-0.149s1.28-0.042 1.964-0.016c0.683 0.026 1.296 0.136 1.839 0.329s1.017 0.477 1.424 0.853c1.367 1.231 2.050 2.608 2.050 4.131 0 0.897-0.112 1.693-0.337 2.386s-0.511 1.275-0.861 1.745-0.834 0.868-1.455 1.197c-0.621 0.329-1.226 0.582-1.815 0.759s-1.346 0.316-2.269 0.415c-0.923 0.099-1.747 0.159-2.472 0.18s-1.646 0.031-2.762 0.031-2.037-0.010-2.762-0.031c-0.725-0.021-1.549-0.081-2.472-0.18s-1.68-0.237-2.269-0.415c-0.589-0.177-1.194-0.43-1.815-0.759s-1.106-0.728-1.455-1.197c-0.349-0.469-0.636-1.051-0.861-1.745s-0.336-1.489-0.336-2.386zM20 21c0-1.657 0.895-3 2-3s2 1.343 2 3c0 1.657-0.895 3-2 3s-2-1.343-2-3zM8 21c0-1.657 0.895-3 2-3s2 1.343 2 3c0 1.657-0.895 3-2 3s-2-1.343-2-3z" fill="#fff"></path></svg></a></li></section></article>
The button is an image and some text. When pressed, it'll show the card. The card is made up of four sections; a close button, a details section, headshot and an icon bar containing social icons. For this example we’re using inline SVG icons taken from the Entypo collection.
Styling
Before getting into animations let’s add some style to the button and card. First, the button:
.show-card { background: #fff; border-radius: 4em; border: 0.25em solid #1fa756; color: #1fa756; font-size: 18px; left: 50%; line-height: 1.5; padding: 1em 1em 1em 4em; position: absolute; top: 50%; transform: translate(-50%, -50%); outline: none; } .show-card img { border-radius: 50%; left: 0.3em; position: absolute; top: 0.25em; width: 3em; }
Note: we’ve added an outline: none;
to the .show-card
element because some browsers add a peculiar glow to buttons’ focus states (which we don’t want):

Then we’ll style the card and each of the sections in it.
.card { bottom: calc(50% - 8em); display: none; height: 16em; left: calc(50% - 10em); position: absolute; transition: all 0.4s 0.4s cubic-bezier(.5,.2,.43,1.33); width: 20em; } .close { color: #aaa; cursor: pointer; height: 1em; opacity: 0; position: absolute; right: 0.5em; top: 0.5em; width: 1em; z-index: 10; } .details { position: absolute; bottom: 2.5em; background: #fff; border-radius: 0.25em; height: 0; overflow: hidden; text-align: center; width: 20em; } .name { color: #333; font-weight: bold; margin: 3em 0 0; opacity: 0; } .description { color: #666; font-size: 1em; font-weight: 200; line-height: 1.5; margin: 0.75em 2em 2em; opacity: 0; } .headshot img { border-radius: 50%; border: 0.5em solid #fff; display: block; height: 6em; margin: -3em auto 0.5em; opacity: 0; width: 6em; } .icon-bar { background: #1fa756; border-radius: 0.25em; left: -1em; position: absolute; right: -1em; top: 13em; } .icon-bar ul { display: flex; flex-display: column; flex-wrap: nowrap; padding: 0; } .icon-bar li { display: inline-block; font-size: 2em; opacity: 0; padding: 0 1em; width: 33%; } .icon-bar a { color: #fff; text-decoration: none; }
Note that we set this card to display: none
initially. We will then control this using JavaScript.
The Basic Show and Hide
Rather than dive in to the CSS, we’ll first set up the basic action of showing and hiding the contact information. This will make use of a little JavaScript (and in this case jQuery) to add and remove classes depending on what’s clicked:
// Show the card on clicking the button $('.show-card').click(function(e) { $('.card').addClass('show').css('display', 'block'); $('.show-card').addClass('hide'); }); // Hide the card on clicking the "x" $('.card .close').click(function(e) { $('.card').css('display', 'none'); });
We should now be able to show and hide the card using jQuery and the display
CSS property.
See the basic show and hide example here:
Before Animating
With the layout in place we need to add a few extra styles to hide the various elements so that we can animate them into view.
.close, .name, .description, .headshot img, .icon-bar li { opacity: 0; } .details { height: 0; }
These elements will have a delay before their animation takes effect, so need to be out of sight initially.
Basic Animations
With the mechanism in place to show and hide the card, we can begin to attach animations. The JavaScript above adds a show
state to the card on show, and we can attach animations to this class which will introduce the individual parts of the card.
To do this we’ll use the CSS animation
property and sets of keyframes
.
It’s helpful to create general-purpose animations in CSS and reuse them. Simple animations such as fading in and out can be defined once and used in multiple places.
To show how keyframes
are defined, let’s create the fade in and fade out animations.
@keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } }
Keyframes are a series of steps, defined as percentages. They can be any number of steps but in our example we only define the start (0%) and end (100%) keyframes. In the fade-in
we start with no opacity (0) and end in full opacity (1). The fade-out
animation does the opposite.
We can use the animation
property to apply these fade animations to the button when it is shown and hidden.
.show-card { animation: fade-in 0.4s 1s forwards ease-out; } .show-card.hide { animation: fade-out 0.4s forwards ease-out; }
The animation shorthand here tells the card to use the fade-in
animation, lasting 0.4 seconds with a 1 second delay. The animation will play once and stop at the end (forwards) and use the ease-out
timing function.
When the hide
class is applied to the button, the fade-out
animation takes effect.
Bouncy Effect Timing Function
The first part we’ll introduce is the icon bar. Since this is where the links will be, we want it to stand out and be the first thing people notice.
When animating we can create interesting effects using the timing function property. In this case we’ll use a cubic-bezier
timing function to add some “bounce” when the icon bar appears.
First we’ll create some simple keyframes to have the bar start small and grow in height.
@keyframes show-icon-bar { 0% { height: 0; } 100% { height: 4.5em; } }
We can now apply this set of keyframes to the icon bar when the show
class is added to the card.
.card.show .icon-bar { animation: show-icon-bar .5s forwards cubic-bezier(.64,1.87,.64,.64); }
The bouncy effect is created by the use of the cubic-bezier
timing function. Timing functions describe the changes in speed through an animation, and can be designed to over-shoot the beginning or end of an animation. This animation goes a little over, then corrects, creating a bounce.
Animation Delay
With the icon bar introduced, we need to animate the section that contains the details and headshot. We would like this section to appear after the icon bar has been introduced. To achieve this we’ll use the animation-delay
property.
.card.show .details { animation: show-detail-container 0.7s 0.5s forwards cubic-bezier(.54,-0.38,.34,1.42); }
The animation applied to the details
section is set to have a duration of 0.7 seconds, and a delay of 0.5 seconds. This means the animation will not begin until after the icon bar has been introduced.
We also use a cubic bezier timing function here to give it some bounce.
Next we define the keyframes for this container’s animation.
@keyframes show-detail-container { 0% { height: 0; } 100% { height: 13.5em; } }
The show-detail-container
animation keyframes starts with the container invisible with a height of zero, and animates to the full height. The cubic bezier timing function then makes the animation overshoot a little to give it some bounce.
Combining Multiple Animations
Multiple animations can be applied in a single property. As long as the animations don’t conflict with each other (by trying to animate the same property), they can be used to add nuance to an animation. Multiple animations are defined just like single animations, but separated by commas.
In this case we are going to fade in the headshot, name and description content while at the same time using a pop-in
function to have them appear to zoom from a distance away.
If we had used one single animation, with a cubic bezier timing function, the fade effect would appear to bounce and look odd. Instead, we’ll have the fade use a linear timing function and the zoom will have the bounce effect.
First we define the pop-in
keyframes.
@keyframes pop-in { 0% { transform: scale(0.7); } 100% { transform: scale(1); } }
This uses the scale
transform to have each element begin smaller and scale up to their normal size.
Let’s bring in the content. Each element has two animations, and each of the elements has a slightly longer animation delay to stagger their appearance.
.card.show .headshot img { animation: fade-in 0.4s 1.2s forwards, pop-in 0.6s 1.2s forwards cubic-bezier(.64,1.87,.64,.64); } .card.show .name { animation: fade-in 0.3s 1.3s forwards, pop-in 0.3s 1.3s forwards cubic-bezier(.64,1.87,.64,.64); } .card.show .description { animation: fade-in 0.3s 1.4s forwards, pop-in 0.3s 1.4s forwards cubic-bezier(.64,1.87,.64,.64); }
Close Icon
The close icon animation makes use of the fade-in
keyframes we defined earlier.
.card.show .close { animation: fade-in 0.3s 1.3s forwards; }
Calls to Action
We just need to show the icons now. When presenting animations, the last thing animated is where the viewer’s attention will end up. This means that the viewers eye will follow the animation and finish looking at the icons we’d like them to select.
We’ll again make use of the animation delay property for these to ensure they’re introduced last. First we define keyframes of an animation for the icons:
@keyframes show-icon { 0% { opacity: 0; transform: rotateZ(-40deg); } 100% { opacity: 1; transform: rotateZ(0); } }
Each icon will spin a little as they fade in. We can now attach this animation to each of the icons, using a delay on each to stagger them.
.card.show .icon-bar li { animation: show-icon 0.5s forwards cubic-bezier(.64,1.87,.64,.64); } .card.show .icon-bar li:nth-child(1) { animation-delay: 1.8s; } .card.show .icon-bar li:nth-child(2) { animation-delay: 1.9s; } .card.show .icon-bar li:nth-child(3) { animation-delay: 2s; }
Put these all together and we now have a card that appears nicely when the button is selected.
One More Thing
Before we can finish, we need to add a hide
action to the close button and animate the card away. First we’ll update the JavaScript. On pressing the close
icon, the JavaScript waits for one second before removing the card with display: none
.
$('.show-card').click(function(e) { $('.card').addClass('show').css('display', 'block'); $('.show-card').addClass('hide'); }); $('.card .close').click(function(e) { $('.card').addClass('hide'); setTimeout(function() { $('.card').css('display', 'none').removeClass('show').removeClass('hide'); }, 1000); $('.show-card').removeClass('hide'); });
This gives us one second to animate the card–we’ll have it fall off the bottom of the screen. The keyframes that achieve this are as follows:
@keyframes drop-card { 100% { bottom: -100%; transform: rotateZ(20deg); opacity: 0; } }
We then apply this as an animation to the card.
.card.hide { animation: drop-card 1s forwards cubic-bezier(.54,-0.38,.34,1.42); }
Demo
Putting it all together, you can see the result here.
A Note on Prefixes and Browser Compatibility
Animations are well supported across browsers. Depending on your audience, they should work in most situations. It’s worth making sure the basic show and hide functions work and any animation is added as a progressive enhancement.
Summary
Through the use of multiple animations, and particularly the animation-delay
property, we’ve taken what could have been a very simple widget and made it more appealing. The animation guides the viewer through each of the elements and directs them toward the calls to action.
Animation can be a great way to communicate within your designs. This communication helps visitors understand your intent as well as improve credibility and confidence in the quality of your work.