CSS Keyframe Engine¶
This page is a practical guide to using the Keyframe engine. Read Engines Overview when you want side-by-side comparisons and tradeoffs.
This Engine builds native browser CSS @keyframes animations. The browser handles all rendering, providing excellent performance.
Example¶
A dot in the middle of the screen that pulses by scaling and fading in and out - looping forever and alternating direction on each iteration.
View Example
View Source Code
module Animation.Keyframe.PulsingDot.Main exposing (main)
import Anim.Builder exposing (AnimBuilder)
import Anim.Engine.Keyframe as Keyframe
import Anim.Property.Opacity as Opacity
import Anim.Property.Scale as Scale
import Browser
import Html exposing (Html, div)
import Html.Attributes exposing (class, style)
import Motion.Easing exposing (Easing(..))
-- MAIN
main : Program () Model msg
main =
Browser.element
{ init = \_ -> init
, view = view
, update = \_ model -> ( model, Cmd.none )
, subscriptions = always Sub.none
}
-- MODEL
type alias Model =
{ animState : Keyframe.AnimState }
init : ( Model, Cmd msg )
init =
let
animState =
Keyframe.init
[ Scale.init groupName 1
, Opacity.init groupName 1
]
in
( { animState =
Keyframe.animate animState <|
Keyframe.for groupName
>> pulse
}
, Cmd.none
)
-- ANIMATION
-- Avoid typos from hardcoding strings in multiple places
groupName : String
groupName =
"pulsingDot"
pulse : Keyframe.EngineBuilder -> Keyframe.EngineBuilder
pulse =
Keyframe.loopForever
>> Keyframe.alternate
>> Keyframe.duration 1000
>> Keyframe.easing EaseInOut
>> Scale.begin
>> Scale.to 0.4
>> Scale.end
>> Opacity.begin
>> Opacity.to 0.3
>> Opacity.end
-- VIEW
view : Model -> Html msg
view model =
div
[ class "example-stage" ]
[ Keyframe.styleNode model.animState
, div
(Keyframe.attributes groupName model.animState
++ [ style "width" "80px"
, style "height" "80px"
, style "border-radius" "50%"
, style "background-color" "#e53935"
]
)
[]
]
Quick Walkthrough¶
Here's a general workflow to get up an running quickly.
1. Build¶
View Source Code
2. Initialize¶
View Source Code
3. Render¶
Render the generated @keyframes style node, element attributes and event listeners.
View Source Code
4. Trigger with animate¶
Call animate to apply the animation config to the current AnimState.
View Source Code
5. React¶
Use update for incoming Keyframe events.
View Source Code
type Msg
= TriggerFadeIn
| GotAnimMsg Keyframe.AnimMsg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
GotAnimMsg animMsg ->
let
( animState, maybeEvent ) =
Keyframe.update animMsg model.animState
in
handleAnimEvent maybeEvent { model | animState = animState }
_ ->
( model, Cmd.none )
handleAnimEvent : Maybe AnimEvent -> Model -> (Model, Cmd Msg)
handleAnimEvent maybeEvent model =
case maybeEvent of
Just (Ended _ _ "card") ->
(model, Cmd.none)
_ ->
(model, Cmd.none)
In Detail¶
Initialize¶
Pass a list of property initializers to init. Each registers an animation group name and sets the element's starting inline style from the first render.
View Source Code
📖 See Initialize for more info.
Trigger¶
Call animate to apply an animation to the current AnimState.
View Source Code
📖 See Triggering Animations for more info.
Mid-Flight Interruptions¶
Mid-flight values are not available for @keyframes animations, which means triggering with animate while an animation is running cancels the current animation and replaces it with the new one. This will cause a jump in state from wherever the animation was, to the start state of the new animation.
📖 See Interrupting Animations for more info.
OnLoad Animations¶
For on-load animations, trigger animate when the page initializes, the animation runs immediately.
Update¶
Use update to process incoming keyframe messages. It returns the updated AnimState and the corresponding AnimEvent.
View Source Code
Events¶
update returns a single AnimEvent per call.
DOM events (Started, Ended, Cancelled, Iteration) carry three values:
- the
id(if one exists) of the element that fired the event (CurrentTargetId), - the
id(if one exists) of the element that owns the listener (TargetId), and, - the animation group name.
Iteration carries an additional iteration count (Int).
In most cases only the group name is needed. CurrentTargetId and TargetId may or may not be the same depending on whether the event has bubbled up.
Synthetic events (Paused, Resumed, Restarted) are generated by the engine when the corresponding control functions are called, and carry only the animation group name.
View Source Code
| Event | Fires when... |
|---|---|
Started |
Animation begins playing |
Ended |
Animation completes |
Cancelled |
Animation is interrupted by something outside the engine's control. |
Iteration |
Each iteration completes (looping or alternating) |
Paused |
pause is called on a running animation |
Resumed |
resume is called on a paused animation |
Restarted |
restart is called |
📖 See React for more info.
View¶
Apply attributes to the animated element and include styleNode in your view to inject the generated @keyframes CSS rules.
View Source Code
Use styleNodeFor to inject rules for a single group.
Positioning the style node
Keyframe animations restart whenever the browser re-renders their <style> node.
Place styleNode in a stable part of your DOM — ideally near the root, outside any conditionally-rendered elements or frequently-updating regions.
Event Listeners¶
Apply events alongside attributes to attach the DOM animation event listeners that drive update.
View Source Code
Use eventsStopPropagation to prevent events from bubbling to parent elements.
📖 See Render for more info.
Responsive Strategy¶
Use relative CSS units whenever the motion can be defined in layout-relative terms and the Browser does the work.
For measured pixel targets, Keyframe has no proportional remap API for resize updates because mid-flight values are not available. Therefore:
- On resize, recompute pixel targets and re-anchor with
retarget. retargetsnaps immediately to the new correct position and stops.- Use this when a layout change makes the old target wrong.
📖 See Responsive Animations for more info.
Playback¶
Set iterations, loopForever, and alternate in the animation builder.
View Source Code
📖 See Playback for the full looping, iterations, and alternate API with live examples.
Timing¶
Set the default duration, speed, and delay. Inherited by every property that doesn't override them.
duration— animation length in milliseconds.speed— alternative toduration; set a rate in property units per second.delay— wait before the animation begins, in milliseconds.
View Source Code
📖 See Timing for more info.
Easing¶
Keyframe animations support the full Easing library, including bounce and elastic. Complex curves are sampled into densely-spaced @keyframes stops, and the browser interpolates linearly between them — visually faithful to the source curve.
Set the default easing for all properties that don't override it:
View Source Code
📖 See Easing for all available easing functions.
Spring¶
Keyframe animations support springs. The spring's motion is pre-baked into densely-spaced @keyframes stops, and the browser interpolates linearly between them — visually faithful to the analytic solution.
The motion ends when each value has settled at the target according to the spring settings — there is no explicit duration, therefore any duration or speed settings on the builder are ignored.
Set the default spring for all properties that don't override it:
View Source Code
📖 See Spring for the full preset list and tuning guidance.
Controls¶
| Function | Returns | Notes |
|---|---|---|
stop |
AnimState |
Jump to end state |
reset |
AnimState |
Jump to start state |
restart |
( AnimState, Cmd msg ) |
Reset and play again; Cmd fires Restarted |
pause |
( AnimState, Cmd msg ) |
Freeze at current position; Cmd fires Paused |
resume |
( AnimState, Cmd msg ) |
Continue from paused position; Cmd fires Resumed |
View Source Code
Stop ->
( { model | animState = Keyframe.stop "card" model.animState }, Cmd.none )
Reset ->
( { model | animState = Keyframe.reset "card" model.animState }, Cmd.none )
Restart ->
let
( animState, cmd ) =
Keyframe.restart "card" GotAnimMsg model.animState
in
( { model | animState = animState }, cmd )
Pause ->
let
( animState, cmd ) =
Keyframe.pause "card" GotAnimMsg model.animState
in
( { model | animState = animState }, cmd )
Resume ->
let
( animState, cmd ) =
Keyframe.resume "card" GotAnimMsg model.animState
in
( { model | animState = animState }, cmd )
📖 See Controlling Animations for more info.
Discrete Properties¶
The Keyframe engine bakes discrete properties into the generated @keyframes rule. discreteEntry values are emitted on every step, and discreteExit values emit the entry value on all steps, then flip to the exit value on the final frame. No additional view setup is needed.
📖 See Discrete Properties for the full API, live examples, and source code.
Transform Order¶
Use transformOrder to set the order in which transform properties are applied.
Call it after for to set the current animation group's order.
Call it before selecting a group to set the global default for groups that do not override it.
View Source Code
📖 See Transform Order for full details.
State Queries¶
Query animation state at any time without waiting for events.
View Source Code
Nothing is returned when no animation exists for the given group.
Property Queries¶
CSS keyframes don't provide access to mid-flight values, so only start and end values are tracked.
All query functions follow the same pattern:
get[Property]Start, and returnMaybe [PropertyValue].get[Property]End, and returnMaybe [PropertyValue]
View Source Code
For mid-flight current values, use the Sub or WAAPI engine.
Nothing is returned when no animation exists for the given group.
When to Choose This Engine¶
Choose Keyframe when you want browser-native keyframes with state-tracked lifecycle and playback controls.
- Best for: on-load animations, loops, and timelines that benefit from pause/resume/restart.
- Avoid when: you need true mid-flight value access or smooth redirection from current playhead position.
API Quick Reference¶
Types¶
| Type | Description |
|---|---|
AnimState |
Tracks animations and their states |
AnimBuilder eng |
Carries all animation configurations |
AnimMsg |
Internal engine messages |
AnimEvent |
Events received during a keyframe animation's lifecycle |
AnimGroupName |
String type alias for the animation group name |
CurrentTargetId |
String type alias for the element that fired the event |
TargetId |
String type alias for the element that owns the listener |
TransformProperty |
Custom transform ordering |
Initialize¶
| Function | Type | Description |
|---|---|---|
init |
List (AnimBuilder eng -> AnimBuilder eng) -> AnimState |
Create initial animation state |
Trigger¶
| Function | Type | Description |
|---|---|---|
animate |
AnimState -> (AnimBuilder eng -> AnimBuilder eng) -> AnimState |
Apply an animation to the current state |
Events¶
| Event | Fires when... |
|---|---|
Started CurrentTargetId TargetId AnimGroupName |
Animation begins playing |
Ended CurrentTargetId TargetId AnimGroupName |
Animation completes |
Cancelled CurrentTargetId TargetId AnimGroupName |
Animation is cancelled before completion by an external DOM/CSS change |
Iteration CurrentTargetId TargetId AnimGroupName Int |
Each cycle completes |
Paused AnimGroupName |
pause is called (synthetic) |
Resumed AnimGroupName |
resume is called (synthetic) |
Restarted AnimGroupName |
restart is called (synthetic) |
Update¶
| Function | Type | Description |
|---|---|---|
update |
AnimMsg -> AnimState -> (AnimState, Maybe AnimEvent) |
Process keyframe messages |
View¶
| Function | Type | Description |
|---|---|---|
attributes |
AnimGroupName -> AnimState -> List (Html.Attribute msg) |
Get animation attributes for an element |
styleNode |
AnimState -> Html msg |
Generate @keyframes rules for all groups |
styleNodeFor |
AnimGroupName -> AnimState -> Html msg |
Generate @keyframes rules for a specific group |
maybeString |
AnimGroupName -> AnimState -> Maybe String |
Get the raw @keyframes CSS as a string |
Event Listeners¶
| Function | Type | Description |
|---|---|---|
events |
(AnimMsg -> msg) -> List (Html.Attribute msg) |
Attach all animation event listeners |
eventsStopPropagation |
(AnimMsg -> msg) -> List (Html.Attribute msg) |
Attach all listeners, stops propagation |
Playback¶
| Function | Type | Description |
|---|---|---|
iterations |
Int -> AnimBuilder eng -> AnimBuilder eng |
Set number of iterations |
loopForever |
AnimBuilder eng -> AnimBuilder eng |
Loop animation infinitely |
alternate |
AnimBuilder eng -> AnimBuilder eng |
Reverse direction on each iteration |
Timing¶
| Function | Type | Description |
|---|---|---|
duration |
Int -> AnimBuilder eng -> AnimBuilder eng |
Set duration (ms) |
speed |
Float -> AnimBuilder eng -> AnimBuilder eng |
Set speed (property units/sec) |
delay |
Int -> AnimBuilder eng -> AnimBuilder eng |
Set delay before animation starts (ms) |
Easing¶
| Function | Type | Description |
|---|---|---|
easing |
Easing -> AnimBuilder eng -> AnimBuilder eng |
Set easing function |
Spring¶
| Function | Type | Description |
|---|---|---|
spring |
Spring -> AnimBuilder eng -> AnimBuilder eng |
Set spring physics |
Controls¶
| Function | Type | Description |
|---|---|---|
stop |
AnimGroupName -> AnimState -> AnimState |
Jump to end state and stop |
reset |
AnimGroupName -> AnimState -> AnimState |
Jump to start state and stop |
restart |
AnimGroupName -> (AnimMsg -> msg) -> AnimState -> ( AnimState, Cmd msg ) |
Reset and begin playing again |
pause |
AnimGroupName -> (AnimMsg -> msg) -> AnimState -> ( AnimState, Cmd msg ) |
Freeze at current position |
resume |
AnimGroupName -> (AnimMsg -> msg) -> AnimState -> ( AnimState, Cmd msg ) |
Continue from paused position |
Discrete Properties¶
| Function | Type | Description |
|---|---|---|
discreteEntry |
String -> String -> AnimBuilder eng -> AnimBuilder eng |
Set a CSS property value when the animation starts |
discreteExit |
String -> String -> String -> AnimBuilder eng -> AnimBuilder eng |
Set a CSS property value during and after the animation |
Transform Order¶
| Function | Type | Description |
|---|---|---|
transformOrder |
List TransformProperty -> AnimBuilder eng -> AnimBuilder eng |
Set custom transform order |
State Queries¶
| Function | Type | Description |
|---|---|---|
anyRunning |
AnimState -> Maybe Bool |
Check if any animation is running |
isRunning |
AnimGroupName -> AnimState -> Maybe Bool |
Check if a specific group is animating |
allComplete |
AnimState -> Maybe Bool |
Check if all animations are complete |
isComplete |
AnimGroupName -> AnimState -> Maybe Bool |
Check if a specific group's animation is complete |
isCancelled |
AnimGroupName -> AnimState -> Maybe Bool |
Check if a specific group's animation was cancelled |
Property Queries¶
CSS keyframes track only start and end values.
| Function | Type | Description |
|---|---|---|
getOpacityStart |
AnimGroupName -> AnimState -> Maybe Float |
Get start opacity |
getOpacityEnd |
AnimGroupName -> AnimState -> Maybe Float |
Get end opacity |
getTranslateStart |
AnimGroupName -> AnimState -> Maybe { x, y, z } |
Get start translate |
getTranslateEnd |
AnimGroupName -> AnimState -> Maybe { x, y, z } |
Get end translate |
getRotateStart |
AnimGroupName -> AnimState -> Maybe { x, y, z } |
Get start rotate |
getRotateEnd |
AnimGroupName -> AnimState -> Maybe { x, y, z } |
Get end rotate |
getScaleStart |
AnimGroupName -> AnimState -> Maybe { x, y, z } |
Get start scale |
getScaleEnd |
AnimGroupName -> AnimState -> Maybe { x, y, z } |
Get end scale |
getSizeStart |
AnimGroupName -> AnimState -> Maybe { width, height } |
Get start size |
getSizeEnd |
AnimGroupName -> AnimState -> Maybe { width, height } |
Get end size |
getSkewStart |
AnimGroupName -> AnimState -> Maybe { x, y } |
Get start skew |
getSkewEnd |
AnimGroupName -> AnimState -> Maybe { x, y } |
Get end skew |
getPropertyStart |
AnimGroupName -> String -> AnimState -> Maybe Float |
Get start value for a custom numeric property |
getPropertyEnd |
AnimGroupName -> String -> AnimState -> Maybe Float |
Get end value for a custom numeric property |
getColorPropertyStart |
AnimGroupName -> String -> AnimState -> Maybe Color |
Get start value for a custom color property |
getColorPropertyEnd |
AnimGroupName -> String -> AnimState -> Maybe Color |
Get end value for a custom color property |
Nothing is returned when no animation exists for the given group.
For complete API details, see the Anim.Engine.Keyframe documentation.
Next Steps¶
The Sub Engine which provides a few more features than you get with keyframes.