16 CSS Bounce Animations16 / 16
Tailwind animate-bounce Customization
The 3-tier Tailwind animate-bounce customization reference — default utility, arbitrary-value tweak, and custom keyframes registered in tailwind.config.js. Three side-by-side badges bouncing at different heights and durations show how each tier controls the effect. The Stack Overflow / dev.to search intent "how do I customize Tailwind's animate-bounce" solved with a copy-paste reference. Tailwind teal palette + JetBrains Mono code snippets so users see exactly what to write.
Published
The code
<div class="bn-16">
<p class="bn-16__label">Tailwind <code>animate-bounce</code> — 3 customization tiers</p>
<div class="bn-16__grid">
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t1" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
</span>
<p class="bn-16__tier-title">Tier 1 · Default</p>
<code class="bn-16__code">animate-bounce</code>
<p class="bn-16__tier-note">Built-in utility<br/><code>-25%</code> translateY, 1s cycle</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t2" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
</span>
<p class="bn-16__tier-title">Tier 2 · Arbitrary</p>
<code class="bn-16__code">animate-[bounce_2s_ease-in-out_infinite]</code>
<p class="bn-16__tier-note">Override per-instance<br/>No config edit</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t3" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2" stroke="#0f766e" stroke-width="2.5" fill="none" stroke-linecap="round"/></svg>
</span>
<p class="bn-16__tier-title">Tier 3 · Custom keyframes</p>
<code class="bn-16__code">animate-customBounce</code>
<p class="bn-16__tier-note">Full pixel control<br/>Registered in config</p>
</div>
</div>
</div><div class="bn-16">
<p class="bn-16__label">Tailwind <code>animate-bounce</code> — 3 customization tiers</p>
<div class="bn-16__grid">
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t1" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
</span>
<p class="bn-16__tier-title">Tier 1 · Default</p>
<code class="bn-16__code">animate-bounce</code>
<p class="bn-16__tier-note">Built-in utility<br/><code>-25%</code> translateY, 1s cycle</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t2" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
</span>
<p class="bn-16__tier-title">Tier 2 · Arbitrary</p>
<code class="bn-16__code">animate-[bounce_2s_ease-in-out_infinite]</code>
<p class="bn-16__tier-note">Override per-instance<br/>No config edit</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t3" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2" stroke="#0f766e" stroke-width="2.5" fill="none" stroke-linecap="round"/></svg>
</span>
<p class="bn-16__tier-title">Tier 3 · Custom keyframes</p>
<code class="bn-16__code">animate-customBounce</code>
<p class="bn-16__tier-note">Full pixel control<br/>Registered in config</p>
</div>
</div>
</div>.bn-16 {
--page-bg: #f0fdfa;
--surface: #ffffff;
--ink: #134e4a;
--sub: #0f766e;
--teal: #14b8a6;
--teal-hi: #5eead4;
--line: #ccfbf1;
--code-bg: #134e4a;
--code-ink: #ccfbf1;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 32px;
padding: 32px;
background: radial-gradient(70% 60% at 50% 20%, rgba(20,184,166,.12) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-16 *,
.bn-16 *::before,
.bn-16 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-16 ::selection {
background: var(--teal);
color: #fff;
}
.bn-16__label {
font-size: .95rem;
font-weight: 700;
color: var(--sub);
letter-spacing: .02em;
}
.bn-16__label code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .85rem;
padding: 1px 6px;
border-radius: 5px;
background: var(--code-bg);
color: var(--code-ink);
}
.bn-16__grid {
display: grid;
grid-template-columns: repeat(3,minmax(0,220px));
gap: 20px;
width: 100%;
justify-content: center;
}
@media (max-width: 720px) {
.bn-16__grid {
grid-template-columns: 1fr;
}
}
.bn-16__col {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 22px 16px;
border: 1px solid var(--line);
border-radius: 12px;
background: var(--surface);
box-shadow: 0 8px 20px -8px rgba(20,184,166,.14);
}
.bn-16__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
border-radius: 16px;
color: #fff;
will-change: transform;
}
.bn-16__badge--t1 {
background: linear-gradient(135deg,#5eead4,#14b8a6);
animation: bn-16-t1 1s ease-in-out infinite;
}
.bn-16__badge--t2 {
background: linear-gradient(135deg,#fb923c,#ea580c);
animation: bn-16-t2 2s ease-in-out infinite;
}
.bn-16__badge--t3 {
background: linear-gradient(135deg,#a78bfa,#7c3aed);
animation: bn-16-t3 1.8s ease-in-out infinite;
}
/* Tier 1 — Tailwind's default animate-bounce keyframe */
@keyframes bn-16-t1 {
0%,
100% {
transform: translateY(-25%);
animation-timing-function: cubic-bezier(.8,0,1,1);
}
50% {
transform: translateY(0);
animation-timing-function: cubic-bezier(0,0,.2,1);
}
}
/* Tier 2 — same keyframe, slower duration + ease-in-out */
@keyframes bn-16-t2 {
0%,
100% {
transform: translateY(-25%);
}
50% {
transform: translateY(0);
}
}
/* Tier 3 — custom pixel-based keyframes for exact control */
@keyframes bn-16-t3 {
0%,
100% {
transform: translateY(0);
}
30% {
transform: translateY(-30px);
}
60% {
transform: translateY(-10px);
}
}
.bn-16__tier-title {
font-size: .85rem;
font-weight: 800;
letter-spacing: -.005em;
color: var(--ink);
margin-top: 2px;
}
.bn-16__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 600;
padding: 5px 9px;
border-radius: 6px;
background: var(--code-bg);
color: var(--code-ink);
word-break: break-all;
max-width: 100%;
}
.bn-16__tier-note {
font-size: .72rem;
color: var(--sub);
line-height: 1.5;
}
.bn-16__tier-note code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .68rem;
padding: 1px 4px;
border-radius: 3px;
background: var(--line);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-16__badge {
animation: none!important;
transform: none!important;
}
}.bn-16 {
--page-bg: #f0fdfa;
--surface: #ffffff;
--ink: #134e4a;
--sub: #0f766e;
--teal: #14b8a6;
--teal-hi: #5eead4;
--line: #ccfbf1;
--code-bg: #134e4a;
--code-ink: #ccfbf1;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 32px;
padding: 32px;
background: radial-gradient(70% 60% at 50% 20%, rgba(20,184,166,.12) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-16 *,
.bn-16 *::before,
.bn-16 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-16 ::selection {
background: var(--teal);
color: #fff;
}
.bn-16__label {
font-size: .95rem;
font-weight: 700;
color: var(--sub);
letter-spacing: .02em;
}
.bn-16__label code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .85rem;
padding: 1px 6px;
border-radius: 5px;
background: var(--code-bg);
color: var(--code-ink);
}
.bn-16__grid {
display: grid;
grid-template-columns: repeat(3,minmax(0,220px));
gap: 20px;
width: 100%;
justify-content: center;
}
@media (max-width: 720px) {
.bn-16__grid {
grid-template-columns: 1fr;
}
}
.bn-16__col {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 22px 16px;
border: 1px solid var(--line);
border-radius: 12px;
background: var(--surface);
box-shadow: 0 8px 20px -8px rgba(20,184,166,.14);
}
.bn-16__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
border-radius: 16px;
color: #fff;
will-change: transform;
}
.bn-16__badge--t1 {
background: linear-gradient(135deg,#5eead4,#14b8a6);
animation: bn-16-t1 1s ease-in-out infinite;
}
.bn-16__badge--t2 {
background: linear-gradient(135deg,#fb923c,#ea580c);
animation: bn-16-t2 2s ease-in-out infinite;
}
.bn-16__badge--t3 {
background: linear-gradient(135deg,#a78bfa,#7c3aed);
animation: bn-16-t3 1.8s ease-in-out infinite;
}
/* Tier 1 — Tailwind's default animate-bounce keyframe */
@keyframes bn-16-t1 {
0%,
100% {
transform: translateY(-25%);
animation-timing-function: cubic-bezier(.8,0,1,1);
}
50% {
transform: translateY(0);
animation-timing-function: cubic-bezier(0,0,.2,1);
}
}
/* Tier 2 — same keyframe, slower duration + ease-in-out */
@keyframes bn-16-t2 {
0%,
100% {
transform: translateY(-25%);
}
50% {
transform: translateY(0);
}
}
/* Tier 3 — custom pixel-based keyframes for exact control */
@keyframes bn-16-t3 {
0%,
100% {
transform: translateY(0);
}
30% {
transform: translateY(-30px);
}
60% {
transform: translateY(-10px);
}
}
.bn-16__tier-title {
font-size: .85rem;
font-weight: 800;
letter-spacing: -.005em;
color: var(--ink);
margin-top: 2px;
}
.bn-16__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 600;
padding: 5px 9px;
border-radius: 6px;
background: var(--code-bg);
color: var(--code-ink);
word-break: break-all;
max-width: 100%;
}
.bn-16__tier-note {
font-size: .72rem;
color: var(--sub);
line-height: 1.5;
}
.bn-16__tier-note code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .68rem;
padding: 1px 4px;
border-radius: 3px;
background: var(--line);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-16__badge {
animation: none!important;
transform: none!important;
}
}Here's a working CSS Bounce Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind animate-bounce Customization
Source: https://codefronts.com/motion/css-bounce-animation/tailwind-animate-bounce-customization/
The 3-tier Tailwind animate-bounce customization reference — default utility, arbitrary-value tweak, and custom keyframes registered in tailwind.config.js. Three side-by-side badges bouncing at different heights and durations show how each tier controls the effect. The Stack Overflow / dev.to search intent "how do I customize Tailwind's animate-bounce" solved with a copy-paste reference. Tailwind teal palette + JetBrains Mono code snippets so users see exactly what to write.
## HTML
```html
<div class="bn-16">
<p class="bn-16__label">Tailwind <code>animate-bounce</code> — 3 customization tiers</p>
<div class="bn-16__grid">
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t1" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
</span>
<p class="bn-16__tier-title">Tier 1 · Default</p>
<code class="bn-16__code">animate-bounce</code>
<p class="bn-16__tier-note">Built-in utility<br/><code>-25%</code> translateY, 1s cycle</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t2" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
</span>
<p class="bn-16__tier-title">Tier 2 · Arbitrary</p>
<code class="bn-16__code">animate-[bounce_2s_ease-in-out_infinite]</code>
<p class="bn-16__tier-note">Override per-instance<br/>No config edit</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t3" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2" stroke="#0f766e" stroke-width="2.5" fill="none" stroke-linecap="round"/></svg>
</span>
<p class="bn-16__tier-title">Tier 3 · Custom keyframes</p>
<code class="bn-16__code">animate-customBounce</code>
<p class="bn-16__tier-note">Full pixel control<br/>Registered in config</p>
</div>
</div>
</div>
```
## CSS
```css
.bn-16 {
--page-bg: #f0fdfa;
--surface: #ffffff;
--ink: #134e4a;
--sub: #0f766e;
--teal: #14b8a6;
--teal-hi: #5eead4;
--line: #ccfbf1;
--code-bg: #134e4a;
--code-ink: #ccfbf1;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 32px;
padding: 32px;
background: radial-gradient(70% 60% at 50% 20%, rgba(20,184,166,.12) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-16 *,
.bn-16 *::before,
.bn-16 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-16 ::selection {
background: var(--teal);
color: #fff;
}
.bn-16__label {
font-size: .95rem;
font-weight: 700;
color: var(--sub);
letter-spacing: .02em;
}
.bn-16__label code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .85rem;
padding: 1px 6px;
border-radius: 5px;
background: var(--code-bg);
color: var(--code-ink);
}
.bn-16__grid {
display: grid;
grid-template-columns: repeat(3,minmax(0,220px));
gap: 20px;
width: 100%;
justify-content: center;
}
@media (max-width: 720px) {
.bn-16__grid {
grid-template-columns: 1fr;
}
}
.bn-16__col {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 22px 16px;
border: 1px solid var(--line);
border-radius: 12px;
background: var(--surface);
box-shadow: 0 8px 20px -8px rgba(20,184,166,.14);
}
.bn-16__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
border-radius: 16px;
color: #fff;
will-change: transform;
}
.bn-16__badge--t1 {
background: linear-gradient(135deg,#5eead4,#14b8a6);
animation: bn-16-t1 1s ease-in-out infinite;
}
.bn-16__badge--t2 {
background: linear-gradient(135deg,#fb923c,#ea580c);
animation: bn-16-t2 2s ease-in-out infinite;
}
.bn-16__badge--t3 {
background: linear-gradient(135deg,#a78bfa,#7c3aed);
animation: bn-16-t3 1.8s ease-in-out infinite;
}
/* Tier 1 — Tailwind's default animate-bounce keyframe */
@keyframes bn-16-t1 {
0%,
100% {
transform: translateY(-25%);
animation-timing-function: cubic-bezier(.8,0,1,1);
}
50% {
transform: translateY(0);
animation-timing-function: cubic-bezier(0,0,.2,1);
}
}
/* Tier 2 — same keyframe, slower duration + ease-in-out */
@keyframes bn-16-t2 {
0%,
100% {
transform: translateY(-25%);
}
50% {
transform: translateY(0);
}
}
/* Tier 3 — custom pixel-based keyframes for exact control */
@keyframes bn-16-t3 {
0%,
100% {
transform: translateY(0);
}
30% {
transform: translateY(-30px);
}
60% {
transform: translateY(-10px);
}
}
.bn-16__tier-title {
font-size: .85rem;
font-weight: 800;
letter-spacing: -.005em;
color: var(--ink);
margin-top: 2px;
}
.bn-16__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 600;
padding: 5px 9px;
border-radius: 6px;
background: var(--code-bg);
color: var(--code-ink);
word-break: break-all;
max-width: 100%;
}
.bn-16__tier-note {
font-size: .72rem;
color: var(--sub);
line-height: 1.5;
}
.bn-16__tier-note code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .68rem;
padding: 1px 4px;
border-radius: 3px;
background: var(--line);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-16__badge {
animation: none!important;
transform: none!important;
}
}
```Here's a working CSS Bounce Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind animate-bounce Customization
Source: https://codefronts.com/motion/css-bounce-animation/tailwind-animate-bounce-customization/
The 3-tier Tailwind animate-bounce customization reference — default utility, arbitrary-value tweak, and custom keyframes registered in tailwind.config.js. Three side-by-side badges bouncing at different heights and durations show how each tier controls the effect. The Stack Overflow / dev.to search intent "how do I customize Tailwind's animate-bounce" solved with a copy-paste reference. Tailwind teal palette + JetBrains Mono code snippets so users see exactly what to write.
## HTML
```html
<div class="bn-16">
<p class="bn-16__label">Tailwind <code>animate-bounce</code> — 3 customization tiers</p>
<div class="bn-16__grid">
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t1" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
</span>
<p class="bn-16__tier-title">Tier 1 · Default</p>
<code class="bn-16__code">animate-bounce</code>
<p class="bn-16__tier-note">Built-in utility<br/><code>-25%</code> translateY, 1s cycle</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t2" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
</span>
<p class="bn-16__tier-title">Tier 2 · Arbitrary</p>
<code class="bn-16__code">animate-[bounce_2s_ease-in-out_infinite]</code>
<p class="bn-16__tier-note">Override per-instance<br/>No config edit</p>
</div>
<div class="bn-16__col">
<span class="bn-16__badge bn-16__badge--t3" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2" stroke="#0f766e" stroke-width="2.5" fill="none" stroke-linecap="round"/></svg>
</span>
<p class="bn-16__tier-title">Tier 3 · Custom keyframes</p>
<code class="bn-16__code">animate-customBounce</code>
<p class="bn-16__tier-note">Full pixel control<br/>Registered in config</p>
</div>
</div>
</div>
```
## CSS
```css
.bn-16 {
--page-bg: #f0fdfa;
--surface: #ffffff;
--ink: #134e4a;
--sub: #0f766e;
--teal: #14b8a6;
--teal-hi: #5eead4;
--line: #ccfbf1;
--code-bg: #134e4a;
--code-ink: #ccfbf1;
font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
width: 100%;
min-height: 100vh;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 32px;
padding: 32px;
background: radial-gradient(70% 60% at 50% 20%, rgba(20,184,166,.12) 0%, transparent 60%), var(--page-bg);
color: var(--ink);
text-align: center;
}
.bn-16 *,
.bn-16 *::before,
.bn-16 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.bn-16 ::selection {
background: var(--teal);
color: #fff;
}
.bn-16__label {
font-size: .95rem;
font-weight: 700;
color: var(--sub);
letter-spacing: .02em;
}
.bn-16__label code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .85rem;
padding: 1px 6px;
border-radius: 5px;
background: var(--code-bg);
color: var(--code-ink);
}
.bn-16__grid {
display: grid;
grid-template-columns: repeat(3,minmax(0,220px));
gap: 20px;
width: 100%;
justify-content: center;
}
@media (max-width: 720px) {
.bn-16__grid {
grid-template-columns: 1fr;
}
}
.bn-16__col {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 22px 16px;
border: 1px solid var(--line);
border-radius: 12px;
background: var(--surface);
box-shadow: 0 8px 20px -8px rgba(20,184,166,.14);
}
.bn-16__badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;
border-radius: 16px;
color: #fff;
will-change: transform;
}
.bn-16__badge--t1 {
background: linear-gradient(135deg,#5eead4,#14b8a6);
animation: bn-16-t1 1s ease-in-out infinite;
}
.bn-16__badge--t2 {
background: linear-gradient(135deg,#fb923c,#ea580c);
animation: bn-16-t2 2s ease-in-out infinite;
}
.bn-16__badge--t3 {
background: linear-gradient(135deg,#a78bfa,#7c3aed);
animation: bn-16-t3 1.8s ease-in-out infinite;
}
/* Tier 1 — Tailwind's default animate-bounce keyframe */
@keyframes bn-16-t1 {
0%,
100% {
transform: translateY(-25%);
animation-timing-function: cubic-bezier(.8,0,1,1);
}
50% {
transform: translateY(0);
animation-timing-function: cubic-bezier(0,0,.2,1);
}
}
/* Tier 2 — same keyframe, slower duration + ease-in-out */
@keyframes bn-16-t2 {
0%,
100% {
transform: translateY(-25%);
}
50% {
transform: translateY(0);
}
}
/* Tier 3 — custom pixel-based keyframes for exact control */
@keyframes bn-16-t3 {
0%,
100% {
transform: translateY(0);
}
30% {
transform: translateY(-30px);
}
60% {
transform: translateY(-10px);
}
}
.bn-16__tier-title {
font-size: .85rem;
font-weight: 800;
letter-spacing: -.005em;
color: var(--ink);
margin-top: 2px;
}
.bn-16__code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .72rem;
font-weight: 600;
padding: 5px 9px;
border-radius: 6px;
background: var(--code-bg);
color: var(--code-ink);
word-break: break-all;
max-width: 100%;
}
.bn-16__tier-note {
font-size: .72rem;
color: var(--sub);
line-height: 1.5;
}
.bn-16__tier-note code {
font-family: 'JetBrains Mono',ui-monospace,monospace;
font-size: .68rem;
padding: 1px 4px;
border-radius: 3px;
background: var(--line);
color: var(--sub);
}
@media (prefers-reduced-motion: reduce) {
.bn-16__badge {
animation: none!important;
transform: none!important;
}
}
```How this works
Three badges demonstrate the customization tiers. Badge 1 (Default) uses Tailwind's shipped animate-bounce — an @keyframes bounce that translateY(0 → -25% → 0) over 1s. The effect is subtle because -25% is percent of the element's own height, not pixels.
Badge 2 (Arbitrary value) uses Tailwind's arbitrary syntax to override without touching tailwind.config.js: animate-[bounce_2s_ease-in-out_infinite]. Same keyframe as default, but slower duration (2s) and smoother timing. This is the fastest way to tweak per-instance — no config file changes.
Badge 3 (Custom keyframes) uses a custom animation registered in tailwind.config.js: theme.extend.keyframes.customBounce = { '0%, 100%': { transform: 'translateY(0)' }, '30%': { transform: 'translateY(-30px)' }, '60%': { transform: 'translateY(-10px)' } } paired with theme.extend.animation.customBounce = 'customBounce 1.8s ease-in-out infinite'. Then use animate-customBounce in JSX. Pixel-based translateY gives you exact control over bounce height.
The demo shows three badges labeled with their tier + technique. On prefers-reduced-motion, all three disable simultaneously so the reduced-motion story stays consistent across tiers.
Make it yours
- For a one-off page with a single custom bounce, use arbitrary values (Tier 2) — no config file edit needed. For a reusable pattern across many components, use custom keyframes (Tier 3) — DRY.
- For the FASTEST bounce customization, use Tailwind's
animate-bouncewith the built-in shortcutsanimation-durationarbitrary value like[animation-duration:.5s]— combines with the default utility. - For color-cycling with the bounce, add
animate-pulsealongsideanimate-bounce— Tailwind supports composing multiple animation utilities. Or combine into a single custom animation with both transform and opacity/color keyframe stops. - For directional bounces (bounceInDown / bounceInLeft), Tailwind doesn't ship these — you MUST register custom keyframes. The 5-stop animate.css bounceIn recipe from Demo #2 is the reference.
- For Tailwind v4 (2025+), the config format changed to CSS-first with
@themeblock. The customization pattern is the same —@theme { --animate-custom-bounce: customBounce 1.8s ease-in-out infinite; @keyframes customBounce { ... } }. Semantics identical to v3.
Gotchas — read before shipping
- Tailwind v3's default
animate-bounceusestranslateY(-25%)— a PERCENTAGE of the element's own height. On a 40px element that's -10px; on a 200px element that's -50px. If your bounce needs consistent visual height regardless of element size, register custom pixel-based keyframes (Tier 3). - Arbitrary values (Tier 2) work in Tailwind v3+ only. Older versions (v2, v1) require the config file edit. Check your
package.jsonTailwind version. - For JIT (just-in-time) compilation to pick up arbitrary values, they MUST appear in your source code as complete strings — dynamic string concatenation like
animate-[bounce_\`${duration}s\`]doesn't work because the JIT scanner sees the template literal, not the resolved value. Use CSS variables ([--duration:2s]) + a full arbitrary utility that reads it. - For custom keyframes registered in
tailwind.config.js, remember to reference them EXACTLY by name intheme.extend.animation— typos silently fail to compile the animation. VS Code's Tailwind CSS IntelliSense extension catches most typos. - Custom animation utilities won't purge correctly if you compose them dynamically. Always use the full class name (
animate-customBounce) as a static string in your JSX/HTML. - For dark mode + bounce, don't animate
colororbackground-colorinside the keyframe — those trigger paint work. Keep transforms in the animation; use Tailwind'sdark:variant for color changes. - For
prefers-reduced-motion, wrap all three bounces inmotion-safe:animate-*variants (Tailwind's built-in prefix). This automatically disables the animation for users who prefer reduced motion — no manual@mediaquery needed.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 45+ | 10+ | 40+ | 45+ |
transform + @keyframes. Universal, GPU-accelerated. Works in Tailwind v2+.