This happens quickly enough so this copy can be read by the time I'm visible.
Animation
Guidance
Animating content is not more important than the content itself.
Use animation subtly and thoughtfully to draw attention to key elements. Don't animate things which need to be read, or distract the user from the content. Instead, animate accents and decorational elements around the content.
Don't make the user wait. Delaying things appearing to the user just to animate them can make an interface feel slow, isn't natural, and can be frustrating to a user.
Keep timings short. Between 200ms and 500ms is normally completely sufficient.
Waypoints
jQuery waypoints is included, and a generic trigger has been set up for when elements come into the viewport when scrolled.
Add a class of js-waypoint to an element, and when it has entered 5% into the bottom of the viewport, it will gain a class of is-waypoint-reached. You can use this to add CSS transitions/animations for
elements.
The class will be removed and re-added if a user scrolls back up, and then back down again, so if using a CSS transform, the transform will re-occur. If you want to avoid this behaviour, use a CSS animation (see below for example).
The <html> element has a class of no-js before JS runs, and a class of js when the browser starts executing JavaScript, you can use this to provide a fallback if JavaScript is not
present, though as it's just swapped by an inline line of JavaScript in the <head>, it isn't bulletproof.
Because we're using multiple classes for this, it does come with inherent risk of increasing specificity, which isn't necessarily a problem, just something to be mindful of.
Example (transform, repeatable):
Tails on titles have been given a js-waypoint behaviour. (This is an example with a bit of a high level of specificity, because we're animating an accent inside the main element)
To start with, we set the animation duration, and transform the tail off the screen for the initial state, styling js-waypoint (taking note of and adding it to the rotation transform it already has).
.title__highlight:before {
transition: transform 500ms;
}
.title--has-tail.js-waypoint {
.title__highlight {
&:before {
transform: translate(-50vw, -50vw) rotate(225deg);
}
}
}
Then we add the end-state to is-waypoint-reached, resetting the translation:
.title--has-tail.js-waypoint {
.title__highlight {
&:before {
transform: rotate(225deg);
}
}
}
For users without JavaScript, we're going to make sure the tail isn't transitioned off the screen in its initial state:
@at-root .no-js .title--has-tail {
.title__highlight {
&:before {
transform: rotate(225deg);
}
}
}
Result:
Demo Tail Drawing!
Example (animation, single):
You'll notice on the example above that if you scroll up and back down, the animation reoccurs, as the class is removed and added. This is sometimes desirable, and sometimes not.
It could be argued that if your animation is annoying to see repeatedly, well, it probably shouldn't be there.
We can, nevertheless, work around this by using a CSS animation which starts off in a paused state, and plays once. For example, for feature-tiles:
Sidebar: We break a couple of rules here by animating in what is technically 'content', but the animation is very quick, and due to the nature of the text being aligned to the bottom of a tile, shoulnd't be moving by the time a user wants to read it.
We start by defining a 'grow in' animation:
@keyframes tile__growIn {
from {
opacity: 0.8;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
Assign it to a tile's item where a waypoint is added, in a paused initial state, and set it to run when the is-waypoint-reached class is added:
.feature-tile__item {
&.js-waypoint {
animation-name: tile__growIn;
animation-play-state: paused;
animation-iteration-count: 1;
animation-duration: 200ms;
}
&.is-waypoint-reached {
animation-play-state: running;
}
}
If a user scrolls up and back down, re-adding the is-waypoint-reached class, the animation has already run, thus will not run again.
Finally, we add a fallback for cases where JavaScript is disabled to run the animation immediately. Again, this isn't perfect, as it doesn't cater for the JS failing to load, so it's important that content still be accessible (ie. don't fade things in from opacity: 0) in the elements' initial state.
@at-root .no-js .feature-tiles__item {
animation-play-state: running;
}