An offer badge should communicate an offer before it communicates an animation. Start with readable text and a normal link, then add a restrained entrance effect whose icon and border share the same duration. The badge should remain understandable if animation is disabled or CSS fails.
This tutorial uses plain HTML and CSS. The downloadable example includes a pause-motion toggle, a reduced-motion rule and a small navigation simulation. It does not make claims about conversion rates or pretend an offer is time-limited.
Build the static badge first
<a class="offer" href="#details">
<span class="offer-icon" aria-hidden="true">✦</span>
<span>Explore the starter package</span>
</a>
<section id="details">
<h2>Package details</h2>
<p>State the actual scope, price and terms here.</p>
</section>Keep decorative icons hidden from assistive technology when the text already conveys the meaning. Do not bake important terms into an image. If the badge is a link, let it remain a link rather than a clickable generic container.
Use tokens for the palette, spacing and timing:
.offer {
--offer-accent: #7ce2c4;
--offer-ink: #071c19;
--offer-duration: 900ms;
display: inline-flex;
align-items: center;
gap: .55rem;
padding: .7rem 1rem;
color: var(--offer-ink);
background: var(--offer-accent);
border: 2px solid #196c59;
border-radius: 999px;
text-decoration: none;
line-height: 1.35;
max-width: 100%;
box-sizing: border-box;
}
.offer:focus-visible {
outline: 3px solid #155eef;
outline-offset: 4px;
}
.offer-icon { flex: 0 0 auto; }Let the label wrap on a small screen. Fixed-width promotional chips often fail precisely when a longer translation or larger text is needed. Check foreground/background contrast independently of any glow.
Use one finite entrance
A subtle border pulse and icon movement can run together from the same element insertion:
.offer {
animation: offer-border var(--offer-duration) ease-out 1;
}
.offer-icon {
animation: offer-icon var(--offer-duration) ease-out 1;
}
@keyframes offer-border {
0% { box-shadow: 0 0 0 0 rgb(25 108 89 / .4); }
100% { box-shadow: 0 0 0 10px rgb(25 108 89 / 0); }
}
@keyframes offer-icon {
0%, 100% { transform: rotate(0); }
40% { transform: rotate(-12deg); }
70% { transform: rotate(8deg); }
}
@media (prefers-reduced-motion: reduce) {
.offer, .offer-icon { animation: none; }
}
[data-motion="off"] .offer,
[data-motion="off"] .offer-icon { animation: none; }A finite effect avoids indefinite visual competition with the page. Transforming the icon does not change surrounding layout. The MDN reduced-motion reference describes the user preference; respond to it without removing the content itself.
For repeating animation, provide a clear way to pause it and review the applicable accessibility requirements. Simply slowing a continuous effect does not necessarily make it comfortable.
Keep navigation from restarting the clock
In React, place a persistent badge in a shared layout if it should survive route changes. Avoid changing its key with the pathname or recreating its component type inside another component. CSS animations restart when their element is replaced.
If the intended behaviour is "animate once per visit", store that decision at the appropriate application level. A boolean inside a component that is unmounted on every route cannot preserve visit-wide state. Conversely, do not add durable storage merely to suppress a harmless finite animation forever.
The fixture's navigation buttons change nearby content without replacing the badge. This makes it easy to compare stable placement with an intentional remount in your own framework.
Verification and tradeoffs
Test with reduced motion enabled before loading the page, then toggle the preference while it remains open. Check the static badge at 200% zoom and keyboard focus. Click the navigation simulation: the entrance should not rerun.
The download includes structural checks for shared duration, finite iteration and reduced-motion coverage. These checks do not measure smoothness on a low-power phone; inspect the final page for frame drops and competing animations.
Use truthful offer copy and an obvious destination. For a cohesive component system rather than an isolated effect, see React web application development, and pair the badge with reachable navigation.
