Understanding timeline range names
By default, view progress timelines track elements across the entire viewport. The animation timeline begins when the first pixel of the element's start edge crosses the viewport's end edge, and ends when the element's end edge crosses the viewport's start edge. You can customize this range, limiting the animation timeline to a specific portion of the viewport. For example, you can restrict the view progress timeline to begin only when the subject element has fully entered the viewport.
This guide explains how to modify timeline range names, specifically examining the various timeline range names, their meanings, and how they are used in the animation-range property and its longhands.
Viewport progress timelines primer
CSS animations are created by attaching @keyframes animations to an element using the animation-name property (or animation shorthand). The keyframes define the animation's behavior, while the animation-timeline determines when and how the element progresses through those keyframes.
With CSS scroll-driven animations, the animation's timeline, or progress, is driven by user scroll and element visibility.
In view-progress timelines, keyframe progression is tied to how much of the subject element is visible within the scroller, and its position within the scroller. As the element enters the viewport, the timeline advances. If the user reverses the scrolling, the timeline reverses: as the view progress element comes into or moves out of view, the timeline progresses or reverses, respectively. The animation only occurs when the element is visible within its scrollport. If scrolling stops while the element is in view, the animation pauses.
By default, the view timeline progress starts when the tracked subject's start edge intersects the scrollport at the end edge and ends when the subject's end edge exits the scrollport at the start edge. These are the subject and scrollport's top and bottom edges when scrolling vertically, and the left and right or right and left edges when scrolling horizontally, depending on the writing mode.
In the following example, try scrolling down. Note how the animation begins just as the top edge of the animated element aligns with the bottom edge of the scroll container and ends, reaching 100% progress, when the bottom edge aligns with the top edge of the container, no matter how tall the animated element is.
The animation attachment range
By default, the element is being animated the entire time any portion of the subject element is visible. This means the animation attachment range is the sum of the height of the scroll container and the height of the subject element, with that extra height being at the scroll end edge. For example, if the scroll container is 250px tall and the animated element is 50px tall, the vertical animation attachment range will be 300px tall, starting at the bottom of the scrollport in our example. If the element is 250px tall, the range becomes 500px; if it's 500px tall, the range becomes 750px. This behavior is consistent regardless of element size; so we can use scroll view timelines even when we don't know the exact dimensions of our scroll containers or the dimensions of our subjects.
The animation-range properties define the attachment range for the animation timeline, including the animation attachment range's start and end edges, including insets. The CSS scroll driven animations module provides properties and values that accept <length-percentage> values (such as 30% or 100px) and <timeline-range-name> keywords that specify the edges those values are relative to.
The scroll container's writing mode and scroll direction determine the scroll container's start and end edges.
Setting insets using percentages
The animation-range-start and animation-range-end properties—which can both be set using the animation-range shorthand—define an animation's attachment range, limiting the keyframe's active interval to that specific portion of the range.
.animated_element {
animation-range-start: 30%;
animation-range-end: 70%;
}
Here we use animation-range-start and animation-range-end to inset the animation timeline, defining a subsection of the element's full animation attachment range as the active interval. In this example, the active interval begins 30% into the default attachment range and ends at 70% of that same range. Because no <named-timeline-range> keyword is included, these values are interpreted relative to the default cover animation attachment range. The cover value represents the full range of a view progress timeline, from the point where the subject element's start border edge first enters the scrollport's view progress visibility range to the point where the end border edge has completely left it.
For illustrative purposes, horizontal lines were added 30% from the top and bottom of both the container and the animated element. You may notice when the animation begins and ends, the lines in the element do not line up with the lines in the container. This is because the scroll-port's line percentages are relative to the height of the container and the animated element's lines are relative to the height of the animated element. Neither is relative to the height of the animation attachment range, which is the sum of both the element and the container. Because the full attachment range equals the container height plus the element height, the 30% mark does not occur when the top edge of the animated element aligns with the 30% mark.
Fortunately, the properties defining the start and end of the range properties accept a <timeline-range-name>, which can be used to make the lines align (with the cover value).
Timeline range names
The animation-range properties accept the keyword normal, a <timeline-range-name>, a <length-percentage>, or both a <timeline-range-name> and a <length-percentage>.
The <timeline-range-name> value type accepts six keywords: cover, contain, entry, exit, entry-crossing, and exit-crossing. Each of these represents a predefined named timeline range. A named timeline range is a named segment of an animation timeline. These keywords allow the developer to set the animation attachment range base that offsets are relative to. The start of the segment is represented as 0% progress through the range; the end of the segment is represented as 100% progress through the range. Where these points are depend on the named range used.
Cover
The animation attachment range in the previous examples all "cover" the entire range. This range represents the full range of the view progress timeline, with 0% progress representing the point at which the start border edge of the subject aligns with the end edge container and 100% progress being the when the subject's end border edge reaches the start edge viewport. As we've seen, the size of the cover is the sum of subject and viewport dimensions in the scroll direction. In all the examples thus far, the height of the animation attachment range was the height of the container plus the height of the animated element.
The cover named timeline is the default range. We could have explicitly set the <timeline-range-name> to achieve the same results:
.animated_element {
animation-range-start: cover 30%;
animation-range-end: cover 70%;
}
The image demonstrates the animation timeline. Before the element reaches the start of the animation range, either the 0% view progress or the 30% view progress mark depending on the animation-range-start, the element is shown in yellow. This represents the position of the element when the from keyframe is applied. The red represents the location of the animated element relative to the scrollport when the to keyframe is applied. This is the position of the animated element when it reaches the end of the animation. The animation occurs when the element is between these areas, represented by the striped areas.
We can set the attachment range to a different named timeline range to have the animation start when 30% of it is 30% of the way through the container and end when 70% of it is 70% of the way through.
Contain
The contain keyword fully contains the animation within the scrollport, making it so the range starts when the animated element is 100% visible (if it can be fully visible). With contain, the start of the animation (0%) occurs when the subject element's end edge aligns with the end edge of the scroll container. The end of the animation (100%) occurs when the start edge of the subject element reaches the start edge of the scroll container.
.animated_element {
animation-range: contain;
}
When the animated element is smaller than the scroll container, the contain value ranges from the point where the subject element is first completely contained by the scroll port (0%), to the point where it is no longer completely contained by the scroll port (100%). If the animated element is the same size as the scroll container in the scroll direction, the animation occurs over 0px, which is not visible to the user. If the animation is larger than its container, the animated element is never fully visible as it is not "contained" within the viewport, and the animation starts when the start edge reaches the start edge of the scrollport, and ends when the animated element's end edge reaches the end edge of the container. Change the size of the animated element in the demonstration and scroll to see this in action.
In this example, the original height of the animated element is 20% of the height of the viewport, and therefore can be completely contained within it.
If you check the 500px radio button, the subject will be twice as tall as the viewport. With contain, if the subject element is larger than the scrollport, the animation range is from the point where the subject element first completely covers the scroll port, with the 0% occurring when the start edge reaches the start edge of the container, to the point where it no longer completely covers the scrollport, with the 100% occurring when the end edge crosses the containers end edge.
If the subject is the same size as the scroll container, which occurs if you select the 250px size, the animation still occurs, but over 0px, so is not perceivable to the user.
The animated element has its animation-fill-mode value set to forwards, meaning the property values defined in the @keyframes animation get applied when the 0% progress is reached, and remain as defined in the 100% keyframe after the animation concludes. We did this because otherwise the middle-sized 250px subject, being the same height as the scroll container, would appear to not animate at all. As the 0% and 100% occur at the same time, the animation is instantaneous, and is only perceivable because the properties defined in the 100% keyframe state are maintained after the animation ends.
The contain value represents the range during which the principal box is either fully contained by, or fully covers, its view progress visibility range within the scrollport. What the 0% progress represents depends on whether the animated element is smaller or larger than the scrollport. If the element is smaller than the scrollport in the scroll direction, 0% occurs when the animated element's end border edge aligns with the end edge of the scrollport and 100% and 100% occurs when the animated element's start border edge aligns with the start edge of the scrollport. If the element is larger than the scrollport, the 0% progress is when the animated element's start border edge reaches the start edge of the viewport and and 100% is when the end border edge aligns with the end edge. If the animated element is the same size as its container, the animation still happens, but over 0px.
In the next example, we use the same percentage inset values as we did in our cover example, but use the contain timeline range name value instead:
.animated_element {
animation-range-start: contain 30%;
animation-range-end: contain 70%;
}
Again we use the animation-range-start and animation-range-end properties to inset the animation timeline, defining a subsection of the element's full animation attachment range as the active interval. If the element is smaller than the scrollport in the scroll direction, the 30% occurs when the point that is 30% from the animated element's end border edge (which is 70% from its start edge) is 30% of the way from the end edge of the scrollport. The 70% range end occurs when 30% of the element, which is 70% from the end edge, is 70% of the way through that viewport. The positions are different when the animated element is larger than the scrollport. In that case, the 30% animation start is reached when the point that is 30% from the animated element's start end is 30% of the way from the scrollport's start end. The 70% animation end is when the 70% of the animated element has progressed to 70% of the way from the start edge. When the element is the same size as the scrollport in the scroll direction, the start and end of the range occur at the same point, so the animation occurs, but over 0px, so is not visible.
The animation occurs when the element is between these areas, represented by the white of the container in the 50px example and by the overlapping orange areas in the 250px (which is the height of the container) and the 500px examples.
It may be helpful for some to compare and contrast the cover and contain values. We can use the shorthand animation-range property to declare both the animation-range-start and animation-range-end have the same <animation-name-range> value set at 0% and 100% respectively:
#A {
animation-range: contain;
}
#B {
animation-range: cover;
}
We can also use the shorthand to declare offsets:
#A {
animation-range: contain 30% contain 70%;
}
#B {
animation-range: cover 30% cover 70%;
}
Select different radio buttons and scroll the scrollport to see the different effects of cover versus contain on animation timelines with shortened ranges.
Entry and exit
To make the entire animation happen only when the subject is in the process of entering or exiting the viewport, use the entry or exit values, respectively. With these two values, the animation attachment range is based on the size of the animated element, not the size of the viewport.
With entry, the 0% progress occurs the moment the animated element enters the viewport, when the subject's start edge crosses the viewport's end edge. Setting animation-range-start: entry 0% is the same as setting animation-range-start: cover 0%.
If the animated element is smaller than the viewport, the animation attachment range is the size of the element. If the element is larger than the viewport, the animation attachment range is the entire viewport, so the 100% progress occurs when the start edge of the element reaches the start edge of its scroll container.
Setting animation-range-end: entry 100% is equivalent to setting animation-range-end: contain 0%. With entry, the 100% occurs when the subject's end edge crosses the end edge of the viewport or, if the animated element is larger than the viewport in the scroll direction, when the animated element's start edge reaches the start edge of the viewport.
The entire animation occurs as the subject comes into view, ending when it becomes completely visible or when it reaches the start edge; whichever occurs first. In other words, with entry, the animation attachment range is the size of the animated element, abutting the end edge of the scrollport, with a maximum size being the size of the container.
The exit value is the inverse of entry.
With exit, the attachment range starts when the animated element's start edge crosses the viewport's start edge. Setting animation-range-start: exit 0% is equivalent to setting animation-range-start: contain 100%. The 100% progress occurs when the subject's end edge crosses the start edge; setting animation-range-end: exit 100% is equivalent to animation-range-end: cover 100%.
With exit, the entire animation occurs as the subject exits the viewport, only completing when it is completely out of the viewport. As with entry, with exit, the animation attachment range is the size of the animated element, but abutting the start edge of the scrollport rather than the end edge, with the maximum size being the size of the container in the scroll direction.
The position at 0% progress is shown in yellow. The position at 100% progress is displayed in red. In the case, where the animated element is larger than the viewport, these two positions overlap, which is denoted by a striped background.
#A {
animation-range: entry;
}
#B {
animation-range: exit;
}
Scroll the viewbox to see the range of the entry and exit values.
When the subjects are small enough to be fully contained within the viewport, the animation attachment timeline is at the start (entry) or end (exit) of the scrollport and the size of the attachment range is limited to the size of the animated element in the scroll direction. The percent offset, if any, is relative to the subject's size, not the viewport size.
If the animated element is the size of the viewport or larger, the animation doesn't begin until the element fully covers the scrollport in the scroll direction. If your animated element is larger than the scrollport, you may prefer to use entry-crossing and exit-crossing.
Entry- and exit-crossing
If the animated element is smaller than the scrollport, and you want the full animation to occur fully, from beginning to end, as it enters or as it exits the scrollport, use entry or exit, respectively. If your animated element is larger than the viewport, the animation range is contained in the scrollport, while the element is not. The entry value sets the 100% progress to be when the element's start edge reaches the start-edge of the scrollport, and exit only reaches the 0% that is when the element's end-edge reaches the scroll container's end edge; when the animated element has already partially scrolled past the scrollport's start edge.
Entry-crossing
The entry-crossing value represents the range during which the animated element crosses the end edge of the viewport, with 0% progress occurring when the element's start edge aligns with the end edge the viewport and 100% progress occurring when the element's end edge reaches the end edge of the viewport, meaning it has finished fully scrolling into the viewport.
#A {
animation-range: entry;
}
#B {
animation-range: entry-crossing;
}
Note how the effects are similar, except for when the 500px is selected and the animated element is taller than the container. With entry, when the animated element is larger than the viewport, the 100% progress occurs when the element first fully spans the viewport.
The entry-crossing produces the same results as entry when the element is equal to or smaller than the viewport, but when the element is larger than the viewport, the 100% occurs later, occurring only when the end edge has entered the view port. The yellow represents the position of the element when it reaches 0% progress. The red represents the position at 100% progress. The striped area indicates an overlap in these positions.
Exit-crossing
The exit-crossing value represents the range during which the animated element crosses the start edge of the viewport, with 0% progress occurring when the element's start edge aligns with the start edge the viewport and 100% progress occurring when the element's end edge reaches the start edge of the viewport, meaning it starts animating as soon as it covers the viewport, and continues animating until it fully exits the viewport's start edge.
#A {
animation-range: exit;
}
#B {
animation-range: exit-crossing;
}
Like when we compared entry with entry-crossing, the effects are similar, except for when the 500px is selected, which makes the animated element taller than the container. With exit, when the animated element is larger than the viewport, the 0% progress only occurs when the element's end edge crosses the end edge of the viewport. The exit-crossing produces the same results as exit when the element is equal to or smaller than the viewport, but when the element is larger than the viewport, the 0% occurs earlier, occurring as soon as the element's start edge reaches the viewport's start edge, rather than waiting until the element's end edge enters the viewport.