Search topics
Pulsing buttons, glowing badges and animations that never stop
A call-to-action button is given a pulse to draw the eye, and the pulse is set to repeat without end, so it keeps moving beside everything else on the page for as long as it is open.
animation: pulse 1.5s ease-out infinite; The problem
People with attention difficulties, and people reading beside the button, keep having their eye pulled back to a pulse that never stops, and nothing on the page stops it.
The home page of a project-planning app has one big button, "Start free trial", beside the price and three lines on what the plan includes. To make sure nobody misses it, the button pulses: a ring spreads out from it and fades, every second and a half, from the moment the page loads for as long as it stays open. It is a few lines of CSS, copied from a "pulse button" snippet, and the snippet ends with infinite. The same thing turns up all over the web in other shapes: a "New" badge that glows, an arrow that bounces to say "scroll down", a chat bubble that wobbles, a skeleton placeholder that shimmers after the content has loaded, a decorative icon that spins.
Success criterion 2.2.2 Pause, Stop, Hide applies to moving, blinking or scrolling information that starts by itself, lasts more than five seconds, and is shown alongside other content. The pulse starts as the page loads, never ends, and sits beside the very text the visitor is reading, so the page needs a way to pause, stop or hide it, and it has none. Understanding 2.2.2 counts any content that conveys a sense of motion, animations included, and draws no line between information and decoration: the point is that the movement distracts from the rest of the page, whatever it is for. The criterion's own way out is the five seconds. Movement that stops by itself within five seconds needs no control, and techniques G11 and G152 describe exactly that: blinking that ends in under five seconds, and animated images set to stop after a few cycles. For a decoration, that is the easy fix.
The button is the one thing on the page designed to be looked at, so it sits right in the middle of what people read: the price, the plan, the terms.
2 of 10 groups affected
- No vision (not affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (not affected)
- Reach, strength (not affected)
- Cognitive (affected)
- Seizures (not affected)
- Limited language, cognitive and learning abilities A pulse beside the text keeps pulling the eye back, so people with attention difficulties can't settle on the price and features they came to read, and nothing lets them make it stop.
- Limited vision With a screen magnifier, the pulse pulls the view back each time it fires, and people who read close to the screen see the ring spread across the words beside the button.
The audit finds every running CSS animation, script animation and animated SVG that repeats without end or for more than five seconds, and flags it for a person to check, with its timing and whether a Pause button is near. It can't tell whether the movement is decoration or information, or whether it is essential, so it is a lead, not a finding. It doesn't see an animation a script starts again every few seconds by adding a class, or an animated GIF. AI tools usually wrap the pulse in a reduced-motion media query and stop there, which helps only the people who found that setting, or suggest a Pause button, which is far more than a decoration needs. Many "attention-grabbing button" snippets online end in infinite, so AI-written CSS often brings the failure with it.
Try it
Watch the button for five seconds, then point at it and press Tab.
The readout counts how long the button has been moving. On the broken side it never stops. "Stop demo" is the Learn page’s own button, not the app’s.
Broken Can you fix this?
Moving for 0 s, and it will keep going.
The fix
Let the pulse run a few times and stop within five seconds, keep the button still, or move it only on hover and focus.
- 1Nothing decorative moves by itself for more than five seconds.
- 2Motion that starts by itself is written inside the no-preference query.
- 3Hover and focus start one short animation, never a loop.
<a class="cta" href="/signup">Start free trial</a>Three pulses, then still
<a class="cta" href="/signup">Start free trial</a>Why this fix: Make motion opt-in
infinite makes the ring spread again every 1.5 seconds for as long as the page is open, for every visitor, and nothing on the page stops it.
One value changes: infinite becomes 3. Three cycles of 1.5 seconds are over in 4.5 seconds, so the pulse still draws the eye as the page opens, then the button rests and the visitor can read. It is written inside no-preference, so people who have asked for less motion never see it. This holds only while nothing starts the animation again: a script that re-adds the class every few seconds, or on every scroll, turns it back into movement without end. Keep cycles × duration under five seconds if you change either.
All three pass 2.2.2. Three pulses keep what the design asked for, attention as the page opens, by changing one value, and the stop is built in. The still button costs nothing and distracts nobody, but the design has to do the work with size, colour and space instead. Movement on hover and focus never pulls anyone's eye from across the page, which is its point and its cost: it gives feedback, not attention. Choose three pulses when the motion is part of the brief, the still button when you can change the design, and hover and focus when the motion was only ever meant as feedback.
Count the five seconds from when the movement starts, across all its cycles: animation-iteration-count times animation-duration, plus any animation-delay between repeats that a script adds. A pulse with a two-second delay and three 1.5-second cycles moves for 4.5 seconds, which passes.
The reduced-motion query is no substitute for an end. Many people who are distracted by movement never find that setting, and 2.2.2 asks for the page itself to stop. Writing the motion inside no-preference is still worth doing, because it costs nothing and helps people who have asked; see animations that ignore the reduced-motion setting.
The same fixes apply to every decoration that loops. Each needs an end, or no motion at all.
- A "New" or "Sale" badge that glows or blinks: a few cycles, or a still badge in a stronger colour.
- A "scroll down" arrow that bounces: two or three bounces, then still.
- A skeleton placeholder that shimmers: stop the shimmer when the content arrives, and fall back to a still grey block if loading takes long.
- An SVG icon drawn with
<animateTransform repeatCount="indefinite">: giverepeatCounta number, or use a still icon. - An animated GIF or Lottie file that loops: export it to loop only a few times, or stop it from the player's own setting.
A spinner that shows something is loading is different. Understanding 2.2.2 treats a loading animation as essential while nothing on the page can be used, since it isn't shown alongside other content. A spinner beside content people can read, such as one in a button or a panel, has to stop when the loading does. If it can keep spinning for more than five seconds there, it falls under 2.2.2 like any other movement: after a few seconds, swap it for a still status in words, such as "Still loading…", or a progress bar that moves only as the work does.
Text that moves, such as a ticker or a rotating carousel, can't end within five seconds and still be read, so it needs a Pause button or no movement at all; that has its own guide: tickers, carousels and animations that can't be paused.
Fixing this with an AI coding assistant? Get this guide as Markdown
Verify the fix
5 checks.
Test with: Reduced motion, Keyboard, Mouse, Zoom
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the design, the CSS framework or the animation library changes.
Limits
This is a starting pattern, not a guarantee for every application. It covers movement only: flashing more than three times in any one second is a separate check under 2.3.1, made with a flash analysis tool rather than by eye. Whether an animation is decoration or information, and whether the page is calm enough once it is still, still needs a person to judge.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: pause-stop-hide (opens in a new tab)
- WCAG Technique G152 (opens in a new tab)
- WCAG Technique G11 (opens in a new tab)
- developer.mozilla.org: animation-iteration-count (opens in a new tab)
- developer.mozilla.org: prefers-reduced-motion (opens in a new tab)
- developer.mozilla.org: repeatCount (opens in a new tab)
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.