CSS transitions & animations
CSS @keyframes work on inline SVG elements exactly as they do on HTML elements — because inline SVG is part of the same DOM. You can animate any CSS-settable property: transform, fill, opacity, stroke-width, and more. Assign the animation via a class, and the SVG element runs it automatically.
Because CSS animations are visual and potentially distracting, the prefers-reduced-motion media query is essential. Users who have set "Reduce Motion" in their operating system preferences get a version with no animation. This is not optional polish — it is a baseline accessibility requirement.
Spinning, pulsing, and fading
The stroke-drawing effect
One of the most striking SVG tricks is animating a path so it appears to draw itself. The technique exploits two stroke properties:
stroke-dasharraysets the length of the dashes. If you set it to the total length of the path, the stroke becomes one long dash that covers the whole path.stroke-dashoffsetshifts that dash pattern along the path. Atoffset = path length, the stroke is fully hidden (offset past the end). Atoffset = 0, the stroke is fully visible.
Animating stroke-dashoffset from the path length down to zero makes the stroke appear to "grow" from the start point — the drawing illusion. The total path length can be measured in code with SVGGeometryElement.getTotalLength(), but for short demos you can estimate it visually.
The prefers-reduced-motion block sets stroke-dashoffset: 0 so users who prefer no motion still see the completed path — they just see it fully drawn from the start, which is the meaningful content.
SMIL (<animate> / <animateTransform>)
SMIL (Synchronized Multimedia Integration Language) is a set of SVG-native animation elements that live directly inside the SVG markup. No CSS or JavaScript is needed — the browser parses the animation declaratively alongside the shapes. SMIL has been part of the SVG specification since SVG 1.1.
The key SMIL animation elements are:
<animate>— animates a single attribute (likecx,r,fill) between a list of values.<animateTransform>— animates thetransformattribute, supportingtype="rotate",type="translate", andtype="scale".<set>— sets an attribute to a value after a delay (no interpolation).<animateMotion>— moves an element along a path.
Note on browser support: SMIL was once deprecated in Chrome (a 2015 intention that was never actually removed), and it remains unsupported in Internet Explorer. For new work, prefer CSS animations or the Web Animations API, which are better integrated with the platform and easier to control programmatically. SMIL remains useful when you need self-contained animated SVG files that work inside <img> tags — CSS and JS do not reach into that context.
<animate> — interpolating an attribute
The <animate> elements are children of the <circle>. They target that parent implicitly. The values attribute is a semicolon-separated list of keyframe values; the browser interpolates between them over dur. repeatCount="indefinite" loops forever.
<animateTransform> — rotating a shape
The from and to values for type="rotate" take three numbers: angle cx cy, where cx cy is the center of rotation. Setting that center to (50, 50) keeps the rectangle pivoting around the middle of the viewBox.
Web Animations API (JavaScript)
The Web Animations API (WAAPI) lets you drive animations from JavaScript using the same compositing engine that runs CSS animations. Every DOM element — including SVG elements — exposes an animate() method that accepts a keyframe array and an options object. The return value is an Animation object you can pause, reverse, seek, and listen to with onfinish.
WAAPI is the right choice when animation parameters are determined at runtime: sizes computed from data, sequences triggered by user events, or animations coordinated across multiple elements. Unlike CSS @keyframes, WAAPI animations are created imperatively, which makes them easy to cancel and recreate in response to state changes.
Respecting prefers-reduced-motion in JavaScript requires one extra step — you check the media query before starting the animation:
Driving an SVG element with element.animate()
The animate() call takes two arguments: an array of keyframe objects (same properties as CSS @keyframes steps) and a timing options object. The iterations: Infinity option loops the animation indefinitely. The motionOK check wraps the entire call — if the user prefers reduced motion, the function simply never runs and the hexagon stays static in its default green state.
WAAPI animations run on the compositor thread when possible (like CSS animations), so they do not block the main thread. They also integrate with the document.timeline and support the full Animation lifecycle, making them suitable for sophisticated sequenced animations.
Choosing the right approach
All three techniques animate the same browser rendering pipeline, but each has a distinct niche:
-
CSS
@keyframes— best for declarative, state-driven animation (hover effects, loading spinners, entrance transitions). Zero JavaScript required. Pairs naturally with CSS custom properties for themeable animations. Straightforward to disable withprefers-reduced-motion. -
Web Animations API — best when animation parameters are dynamic (data-driven charts, user-triggered sequences, coordinated multi-element timelines). Gives you fine-grained programmatic control: pause, seek, reverse, listen for completion. The modern replacement for
requestAnimationFrameloops in most cases. -
SMIL — best for self-contained animated SVG files that must work inside
<img>or CSSbackground-image(where CSS and JS cannot reach). Useful for email clients or contexts where scripting is unavailable. Consider it a last resort for new projects — prefer CSS or WAAPI when you control the embedding context.
Regardless of method, always test with prefers-reduced-motion: reduce active (System Preferences → Accessibility → Reduce Motion on macOS/iOS, or Settings → Accessibility → Remove Animations on Android). Visual motion that serves no functional purpose must stop; motion that conveys state change should offer a non-animated alternative.