Exploding like buttonOne-line
DeepSeek V4 Pro 0813
by DeepSeekreasoning defaultClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 7 min 58 s
- Cost
- $0.109
- Output tokens
- 27,390
- Spent thinking
- 20,739 tokens
- Speed
- 57.4 tok/s
- Console errors
- 0
- First token after
- 804 ms
- First visible text after
- 6 min 16 s
- Prompt tokens
- 435
- Code size
- 18.6 KB
- Finish reason
- stop
- Served by
- Cloudflare
- Model id
deepseek/deepseek-v4-pro-0813- Reasoning sent
model default- Max output
- 943,718 tokens
- Generated
- Nov 12, 2026
What it was asked
The prompt
“a "like" button in pure CSS, no JavaScript, that does a crazy animation when you click it”
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task. Output rules: - Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block. - Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task. - Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw. - The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar. - Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction. - Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session. - Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing. - The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message. - The code must run with zero errors in the console of the latest Chrome.
Code run-1.html · 559 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Like Button - Pure CSS Crazy Animation</title>
<style>
:root {
--heart-red: #ff2d55;
--heart-gray: #bbbbbb;
--heart-size: 52px;
--btn-size: 130px;
--card-bg: #ffffff;
--page-gradient-start: #2c3e50;
--page-gradient-mid: #4a569d;
--page-gradient-end: #6a11cb;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, var(--page-gradient-start) 0%, var(--page-gradient-mid) 55%, var(--page-gradient-end) 100%);
padding: 24px;
-webkit-tap-highlight-color: transparent;
user-select: none;
-webkit-user-select: none;
}
/* ============ CARD ============ */
.card {
background: var(--card-bg);
border-radius: 28px;
padding: 48px 40px 40px;
text-align: center;
box-shadow:
0 25px 60px rgba(0, 0, 0, 0.35),
0 8px 20px rgba(0, 0, 0, 0.2),
0 0 0 1px rgba(255, 255, 255, 0.08) inset;
max-width: 380px;
width: 100%;
position: relative;
overflow: visible;
}
/* ============ LIKE AREA ============ */
.like-section {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 18px;
}
/* Hidden checkbox */
.like-checkbox {
position: absolute;
top: 0;
left: 0;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
}
/* ============ LIKE BUTTON ============ */
.like-btn {
display: flex;
align-items: center;
justify-content: center;
width: var(--btn-size);
height: var(--btn-size);
border-radius: 50%;
cursor: pointer;
position: relative;
transition: box-shadow 0.3s ease;
-webkit-tap-highlight-color: transparent;
outline: none;
}
.like-btn:focus-visible {
box-shadow: 0 0 0 4px rgba(255, 45, 85, 0.4);
}
/* Idle pulse when unchecked */
.heart-wrap {
transform: rotate(45deg);
animation: idle-pulse 2.2s ease-in-out infinite;
position: relative;
z-index: 2;
}
.heart {
width: var(--heart-size);
height: var(--heart-size);
background: var(--heart-gray);
position: relative;
transition: background 0.35s ease;
}
.heart::before,
.heart::after {
content: '';
position: absolute;
width: var(--heart-size);
height: var(--heart-size);
border-radius: 50%;
background: inherit;
transition: background 0.35s ease;
}
.heart::before {
top: calc(var(--heart-size) / -2);
left: 0;
}
.heart::after {
top: 0;
left: calc(var(--heart-size) / -2);
}
/* ============ PARTICLES (12 colored dots) ============ */
.particle {
position: absolute;
top: 50%;
left: 50%;
width: var(--size, 10px);
height: var(--size, 10px);
border-radius: 50%;
background: var(--color, #ff4d6d);
opacity: 0;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 1;
}
/* ============ BURST HEARTS (6 mini hearts) ============ */
.burst-heart {
position: absolute;
top: 50%;
left: 50%;
font-size: var(--size, 20px);
color: var(--color, #ff4d6d);
opacity: 0;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 1;
line-height: 1;
}
/* ============ SHOCKWAVE RINGS (2) ============ */
.shockwave {
position: absolute;
top: 50%;
left: 50%;
width: 76px;
height: 76px;
border-radius: 50%;
border: 3px solid var(--heart-red);
opacity: 0;
transform: translate(-50%, -50%) scale(0.3);
pointer-events: none;
z-index: 0;
}
/* ============ SPARKLES (6 twinkling stars) ============ */
.sparkle {
position: absolute;
top: 50%;
left: 50%;
font-size: var(--size, 14px);
color: var(--color, #ffd60a);
opacity: 0;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 3;
line-height: 1;
}
/* ============ HINT TEXT ============ */
.hint {
font-size: 0.95rem;
color: #999;
letter-spacing: 0.04em;
transition: opacity 0.35s ease;
font-weight: 500;
}
/* ============ LIKES COUNTER ============ */
.likes {
font-size: 1.2rem;
font-weight: 700;
color: #333;
letter-spacing: 0.02em;
}
.count-checked {
display: none;
color: var(--heart-red);
}
.count-unchecked {
display: inline;
}
/* ============ KEYFRAMES ============ */
/* Idle gentle pulse */
@keyframes idle-pulse {
0%,
100% {
transform: rotate(45deg) scale(1);
}
50% {
transform: rotate(45deg) scale(1.07);
}
}
/* Heart pop (plays once on like) */
@keyframes heart-pop {
0% {
transform: rotate(45deg) scale(1);
}
12% {
transform: rotate(45deg) scale(1.65) rotate(12deg);
}
28% {
transform: rotate(45deg) scale(0.82) rotate(-8deg);
}
44% {
transform: rotate(45deg) scale(1.28) rotate(5deg);
}
62% {
transform: rotate(45deg) scale(0.94) rotate(-2deg);
}
78% {
transform: rotate(45deg) scale(1.06);
}
100% {
transform: rotate(45deg) scale(1);
}
}
/* Heart rapid beat (continuous while liked) */
@keyframes heart-beat {
0%,
100% {
transform: rotate(45deg) scale(1);
}
18% {
transform: rotate(45deg) scale(1.13);
}
36% {
transform: rotate(45deg) scale(0.98);
}
54% {
transform: rotate(45deg) scale(1.09);
}
72% {
transform: rotate(45deg) scale(1);
}
}
/* Particle burst - fly outward and fade */
@keyframes particle-burst {
0% {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
55% {
opacity: 1;
}
100% {
opacity: 0;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(0.1);
}
}
/* Burst heart fly-out */
@keyframes burst-heart-fly {
0% {
opacity: 1;
transform: translate(-50%, -50%) scale(0.4);
}
45% {
opacity: 1;
transform: translate(calc(-50% + var(--tx, 0px) * 0.55), calc(-50% + var(--ty, 0px) * 0.55)) scale(1.15);
}
100% {
opacity: 0;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(0.1) rotate(20deg);
}
}
/* Shockwave expanding ring */
@keyframes shockwave-expand {
0% {
opacity: 0.9;
transform: translate(-50%, -50%) scale(0.3);
border-width: 3.5px;
}
60% {
opacity: 0.5;
}
100% {
opacity: 0;
transform: translate(-50%, -50%) scale(2.8);
border-width: 0.5px;
}
}
/* Sparkle twinkle */
@keyframes sparkle-twinkle {
0% {
opacity: 0;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(0) rotate(0deg);
}
25% {
opacity: 1;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(1.25) rotate(80deg);
}
50% {
opacity: 1;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(0.85) rotate(160deg);
}
75% {
opacity: 0.6;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(1.05) rotate(240deg);
}
100% {
opacity: 0;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(0) rotate(320deg);
}
}
/* Card shake on like */
@keyframes like-shake {
0%,
100% {
transform: translateX(0) rotate(0);
}
10% {
transform: translateX(-7px) rotate(-0.8deg);
}
20% {
transform: translateX(7px) rotate(0.8deg);
}
30% {
transform: translateX(-5px) rotate(-0.5deg);
}
40% {
transform: translateX(5px) rotate(0.5deg);
}
50% {
transform: translateX(-3px);
}
60% {
transform: translateX(3px);
}
70% {
transform: translateX(-1.5px);
}
80% {
transform: translateX(1.5px);
}
90% {
transform: translateX(0);
}
}
/* Count number bounce */
@keyframes count-bounce {
0% {
transform: scale(0.4);
opacity: 0;
}
45% {
transform: scale(1.35);
opacity: 1;
}
70% {
transform: scale(0.88);
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* ============ CHECKED STATE ============ */
/* Heart turns red and pops/beats */
#like-toggle:checked~.like-btn .heart {
background: var(--heart-red);
}
#like-toggle:checked~.like-btn .heart-wrap {
animation: heart-pop 0.6s cubic-bezier(0.22, 1.28, 0.36, 1) forwards,
heart-beat 1.1s 0.6s ease-in-out infinite;
filter: drop-shadow(0 0 14px rgba(255, 45, 85, 0.65)) drop-shadow(0 0 40px rgba(255, 45, 85, 0.35));
}
/* Shake the whole button */
#like-toggle:checked~.like-btn {
animation: like-shake 0.5s ease-out;
}
/* Particles burst out */
#like-toggle:checked~.like-btn .particle {
animation: particle-burst var(--duration, 0.85s) cubic-bezier(0.1, 0.65, 0.3, 0.9) var(--delay, 0s) forwards;
}
/* Burst hearts fly out */
#like-toggle:checked~.like-btn .burst-heart {
animation: burst-heart-fly var(--duration, 0.9s) cubic-bezier(0.2, 0.7, 0.35, 1) var(--delay, 0.03s) forwards;
}
/* Shockwaves expand */
#like-toggle:checked~.like-btn .shockwave {
animation: shockwave-expand var(--duration, 0.65s) ease-out var(--delay, 0s) forwards;
}
/* Sparkles twinkle */
#like-toggle:checked~.like-btn .sparkle {
animation: sparkle-twinkle var(--duration, 0.8s) ease-in-out var(--delay, 0.1s) forwards;
}
/* Hint fades out */
#like-toggle:checked~.hint {
opacity: 0;
}
/* Count swaps */
#like-toggle:checked~.likes .count-unchecked {
display: none;
}
#like-toggle:checked~.likes .count-checked {
display: inline-block;
animation: count-bounce 0.5s cubic-bezier(0.22, 1.4, 0.36, 1) 0.05s forwards;
}
/* ============ RESPONSIVE ============ */
@media (max-width: 480px) {
:root {
--heart-size: 42px;
--btn-size: 105px;
}
.card {
padding: 32px 20px 28px;
border-radius: 20px;
}
.like-btn {
width: var(--btn-size);
height: var(--btn-size);
}
.shockwave {
width: 60px;
height: 60px;
}
.hint {
font-size: 0.85rem;
}
.likes {
font-size: 1.05rem;
}
}
@media (min-width: 481px) and (max-width: 768px) {
:root {
--heart-size: 48px;
--btn-size: 115px;
}
.card {
padding: 38px 28px 30px;
}
}
</style>
</head>
<body>
<div class="card">
<div class="like-section">
<!-- Hidden checkbox -->
<input type="checkbox" id="like-toggle" class="like-checkbox">
<!-- Like button -->
<label for="like-toggle" class="like-btn" aria-label="Like">
<!-- CSS Heart (rotated square + two circles) -->
<span class="heart-wrap">
<span class="heart"></span>
</span>
<!-- 12 colorful particles -->
<span class="particle" style="--tx:70px; --ty:0px; --delay:0.00s; --duration:0.82s; --color:#ff4d6d; --size:11px;"></span>
<span class="particle" style="--tx:61px; --ty:35px; --delay:0.02s; --duration:0.85s; --color:#ff8c42; --size:9px;"></span>
<span class="particle" style="--tx:35px; --ty:61px; --delay:0.04s; --duration:0.80s; --color:#ffd60a; --size:13px;"></span>
<span class="particle" style="--tx:0px; --ty:70px; --delay:0.06s; --duration:0.87s; --color:#4dd4ac; --size:8px;"></span>
<span class="particle" style="--tx:-35px; --ty:61px; --delay:0.08s; --duration:0.78s; --color:#b56cff; --size:12px;"></span>
<span class="particle" style="--tx:-61px; --ty:35px; --delay:0.10s; --duration:0.90s; --color:#ff6b6b; --size:10px;"></span>
<span class="particle" style="--tx:-70px; --ty:0px; --delay:0.07s; --duration:0.83s; --color:#f72585; --size:11px;"></span>
<span class="particle" style="--tx:-61px; --ty:-35px; --delay:0.05s; --duration:0.88s; --color:#4ecdc4; --size:9px;"></span>
<span class="particle" style="--tx:-35px; --ty:-61px; --delay:0.03s; --duration:0.79s; --color:#ffe66d; --size:14px;"></span>
<span class="particle" style="--tx:0px; --ty:-70px; --delay:0.01s; --duration:0.81s; --color:#ff85a1; --size:8px;"></span>
<span class="particle" style="--tx:35px; --ty:-61px; --delay:0.04s; --duration:0.86s; --color:#a8e6cf; --size:12px;"></span>
<span class="particle" style="--tx:61px; --ty:-35px; --delay:0.06s; --duration:0.84s; --color:#778da9; --size:10px;"></span>
<!-- 6 mini burst hearts -->
<span class="burst-heart" style="--tx:55px; --ty:0px; --delay:0.04s; --duration:0.92s; --color:#ff4d6d; --size:22px;">♥</span>
<span class="burst-heart" style="--tx:28px; --ty:48px; --delay:0.07s; --duration:0.95s; --color:#ff6b6b; --size:18px;">♥</span>
<span class="burst-heart" style="--tx:-28px; --ty:48px; --delay:0.10s; --duration:0.88s; --color:#f72585; --size:20px;">♥</span>
<span class="burst-heart" style="--tx:-55px; --ty:0px; --delay:0.06s; --duration:0.93s; --color:#ff8c42; --size:16px;">♥</span>
<span class="burst-heart" style="--tx:-28px; --ty:-48px; --delay:0.09s; --duration:0.90s; --color:#b56cff; --size:21px;">♥</span>
<span class="burst-heart" style="--tx:28px; --ty:-48px; --delay:0.05s; --duration:0.94s; --color:#4ecdc4; --size:17px;">♥</span>
<!-- 2 shockwave rings -->
<span class="shockwave" style="--delay:0.00s; --duration:0.62s;"></span>
<span class="shockwave" style="--delay:0.10s; --duration:0.68s;"></span>
<!-- 6 sparkles -->
<span class="sparkle" style="--tx:36px; --ty:-52px; --delay:0.10s; --duration:0.82s; --color:#ffd60a; --size:16px;">✦</span>
<span class="sparkle" style="--tx:-44px; --ty:-38px; --delay:0.16s; --duration:0.85s; --color:#fff5b0; --size:13px;">✦</span>
<span class="sparkle" style="--tx:-54px; --ty:22px; --delay:0.13s; --duration:0.78s; --color:#ffd60a; --size:18px;">✦</span>
<span class="sparkle" style="--tx:-14px; --ty:56px; --delay:0.20s; --duration:0.88s; --color:#ffe66d; --size:14px;">✦</span>
<span class="sparkle" style="--tx:32px; --ty:44px; --delay:0.18s; --duration:0.80s; --color:#ffd60a; --size:15px;">✦</span>
<span class="sparkle" style="--tx:52px; --ty:-22px; --delay:0.12s; --duration:0.83s; --color:#fff5b0; --size:17px;">✦</span>
</label>
<!-- Hint -->
<p class="hint">Tap the heart to like!</p>
<!-- Like counter -->
<p class="likes">
<span class="count-unchecked">4.2k</span>
<span class="count-checked">4.3k</span> likes
</p>
</div>
</div>
</body>
</html>Reasoning trace (20,739 tokens)
The user wants a "like" button in pure CSS, no JavaScript, with a crazy animation when clicked. This means I need to use the checkbox hack or similar CSS-only interaction techniques to trigger the animation.
Let me design this:
1. A heart-shaped like button
2. Uses a hidden checkbox to track state
3. When clicked (checked), it triggers a crazy animation
4. Pure CSS - no JavaScript
For the "crazy" animation, I can do:
- The heart explodes with particles
- Color changes
- Multiple hearts fly out
- Screen shake effect
- Confetti burst
- The heart beats/pulses rapidly
- Stars/sparkles fly everywhere
- Maybe the whole button does a 360 spin and bounce
Let me create something impressive:
- A heart button that when clicked:
- The heart itself does a massive bounce/pulse animation
- Multiple hearts burst out from behind it flying in different directions
- Sparkle/star particles shoot out
- A shockwave ring expands
- The background flashes
- Maybe the heart splits into multiple hearts
- Color shifts through rainbow hues
I'll use:
- A label with a hidden checkbox inside
- The heart as an SVG or CSS-drawn shape
- Pseudo-elements for particles
- CSS animations triggered by :checked
Let me design the structure:
```html
<label class="like-btn">
<input type="checkbox" class="like-checkbox">
<div class="heart-container">
<div class="heart">❤</div>
<!-- particles -->
<div class="particle p1"></div>
<div class="particle p2"></div>
... etc
<div class="shockwave"></div>
<div class="burst-heart bh1">❤</div>
...
</div>
</label>
```
Actually, let me think more carefully. I want it to be really impressive. Let me use:
1. A central heart button
2. When checked, the heart does a wild animation (scale up dramatically, rotate, color shift)
3. Particles (small circles/dots) fly outward in all directions
4. Additional hearts burst out
5. A shockwave ring expands
6. Maybe confetti pieces
7. Screen shake on the container
For the heart, I can use a CSS heart shape or an emoji/SVG. Using CSS-drawn heart might be nicer since I can control the parts better. But an SVG heart in a pseudo-element could work too.
Let me use a combination - the main heart can be made with CSS (two circles and a rotated square) or just use the ♥ character with a nice size.
Actually, for maximum visual impact, let me:
- Use a heart shape (CSS or Unicode)
- Have maybe 8-12 particles that fly out
- 3-5 additional mini hearts that burst
- A shockwave ring
- The heart does a dramatic bounce + squash/stretch + color change
- Maybe some star sparkles
Let me code this up:
Structure:
```html
<div class="like-wrapper">
<input type="checkbox" id="like-toggle">
<label for="like-toggle" class="like-btn">
<span class="heart-shape">♥</span>
<span class="particles">
<span class="particle"></span> <!-- many of these -->
</span>
<span class="burst-hearts">
<span class="burst-heart">♥</span> <!-- several -->
</span>
<span class="shockwave"></span>
<span class="sparkles">
<span class="sparkle">✦</span> <!-- several -->
</span>
</label>
</div>
```
Wait, but I need the particles to be visible only when checked. Using the checkbox hack, I can use the :checked selector to trigger animations. The particles can be hidden initially (opacity: 0, transform: scale(0)) and when checked, they animate outward.
Let me think about the animation flow:
- Initial: heart is gray/white, normal size
- On click (checked):
- Heart immediately scales up (like a pop), turns red/pink, does a rapid bounce
- Particles fly out from center in all directions with different delays
- Mini hearts burst outward
- Shockwave ring expands and fades
- Sparkles appear
- Maybe the whole button does a little shake
For the heart color, I can use CSS custom properties or just change color in the :checked state.
Let me design the particles as small circles/stars that fly out radially. I'll need to position them with different angles and distances using CSS transforms.
Actually, for real particles flying out, I can use CSS custom properties to set the angle and distance for each particle, and animate them from center to their target position.
Or simpler: each particle has a fixed position relative to the heart center, and animates from scale(0) at center to scale(1) at their offset position with opacity fade.
Let me use a simpler approach: Each particle is absolutely positioned at the heart's center, and when checked, they animate outward using transform: translate(...) with different values for each particle.
Actually, let me use a cleaner approach with CSS variables:
Each particle has:
- --angle: the direction it flies (in degrees)
- --distance: how far it flies
- --delay: when it starts
And the animation does:
- from: transform: translate(-50%, -50%) scale(0) rotate(0deg); opacity: 1
- to: transform: translate(calc(-50% + cos(angle)*distance), calc(-50% + sin(angle)*distance)) scale(0); opacity: 0
But since CSS can't compute cos/sin, I'll just hardcode the transform values for each particle.
Let me use a simpler method. I'll have maybe 12 particles, each with a custom property for their target X and Y offset. The animation moves them from center to their target position.
Actually, even simpler: I'll use multiple particle elements each with a specific animation that has its own keyframes or uses CSS variables. Let me use CSS custom properties for the translation:
```css
.particle {
--tx: 0px;
--ty: 0px;
animation: fly-out 0.8s ease-out forwards;
}
@keyframes fly-out {
0% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
100% { transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0); opacity: 0; }
}
```
And each particle gets a unique --tx and --ty.
OK let me just write the full thing. I want this to be really impressive.
Let me plan the particles:
- 12 small colored dots flying out in a circle
- 6 mini hearts flying out
- 8 sparkles/stars
- 2-3 shockwave rings
- The main heart does a vibrant bounce and color change
For the color, I want the heart to go from gray to a vibrant red/pink when liked, and the particles can be various colors (red, pink, purple, orange, yellow).
Let me also add a counter that goes from like 4.2k to 4.3k (or just 1 to 2, or some fun number). Actually, maybe just show the heart.
Let me also add a "crazy" element: maybe the whole button shakes, and the heart does a 360 spin, and there are rainbow colors.
Actually, for maximum "crazy", I want:
- Heart explodes into many hearts
- Rainbow color cycling
- Bounce with overshoot
- Particles in every direction
- Maybe the like counter does a flip
Let me structure this properly. Here's my plan:
Container: A circular button area with the heart in the center
- The heart starts gray/silver
- On click: turns animated rainbow/red, pulses hard
- 20+ particles fly out (dots of various colors)
- 5 mini hearts pop out
- Shockwave rings expand
- Sparkle stars appear and disappear
- The button itself does a shake
I'll use a wrapper div with the checkbox and label technique.
Here's the key CSS trick:
```css
#like-toggle:checked ~ .like-btn .heart { ... }
#like-toggle:checked ~ .like-btn .particle { animation: fly 1s ...; }
```
Wait, the structure matters for the sibling selector. The checkbox needs to be before the things it controls, and they need to be siblings.
Structure:
```html
<div class="like-container">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">
<!-- heart and particles go here -->
</label>
</div>
```
Then: `#like-toggle:checked ~ .like-btn .particle { ... }` works because .like-btn is a sibling of #like-toggle.
Inside the label, I'll have:
- .heart-wrapper containing the heart shape
- .particle elements (absolutely positioned)
- .burst-heart elements
- .shockwave elements
- .sparkle elements
All these are inside the label, positioned absolutely relative to it.
Let me write this out now. I'll be generous with particles and effects.
For the heart shape, I'll use a CSS heart made from a rotated square and two circles, OR just use the ♥ character. Unicode ♥ (U+2665) is simplest but might render differently. Let me use a CSS heart for reliability.
Actually for simplicity and beauty, I'll use an SVG heart inside the label. That way I can control the fill color easily.
Wait, but pure CSS - SVG is still fine, it's just markup. Let me use an inline SVG for the heart.
Actually, using the ♥ character with a nice font and size might be simplest. But CSS heart is also clean. Let me use emoji/SVG heart.
Let me use the ♥ character (or ❤️) and style it with CSS. Actually, for consistent rendering, let me use an SVG.
Hmm, let me think about what's simplest and most reliable. The ♥ character (U+2665) with text styling should work fine across browsers. I'll use that with font-size.
Actually, I'll use a CSS heart shape. It's more "pure CSS" and I can animate the parts separately if I want. But it's trickier.
Let me go with a simple approach: use the ♥ character, large, with CSS styling. For the "crazy" part, the heart itself changes color (from gray to rainbow animated), scales dramatically, and all the particles burst out.
OK, let me also think about the unchecked → checked → unchecked cycle. When you click again (uncheck), the heart should go back to its original state. The animations should only play when transitioning to checked. With CSS, I can use the :checked selector to apply animations. When unchecked, the animations stop and elements return to their initial hidden state.
One thing to note: CSS animations replay when the element goes from not having the animation to having it. So when the checkbox is checked, if I apply `animation: fly-out 1s ease-out forwards;` to particles, they'll animate. When unchecked, the animation is removed and they go back to `opacity: 0; transform: scale(0);` state.
But there's a subtlety: with `forwards` fill mode, after the animation ends, elements stay at the end state (opacity: 0). When unchecked, I need them to go back to initial state. Since the animation is removed, they'll go back to their default styles, which should be `opacity: 0; transform: scale(0);`.
For the heart bounce animation, I want it to be a continuous animation while checked (or just a one-time intense bounce). Let me make it continuous while checked with a pulsing beat.
OK, let me start writing the code. I'll make this really elaborate.
Plan:
- Wrapper with a nice background
- The like button centered
- Heart starts gray (#ccc or #999)
- On check:
- Heart turns red with rainbow hue animation
- Heart does a "pop" animation (scale from 1 to 1.4 to 0.9 to 1.1 to 1)
- Then a continuous pulse/beat
- 16-20 particles (small circles) fly out radially
- 6 mini hearts fly out
- 2 shockwave rings expand
- Sparkles twinkle
- Maybe some confetti pieces
- A count increases
Let me count the elements I need:
- 1 heart (main)
- 18 particles (dots)
- 6 burst hearts
- 2 shockwaves
- 8 sparkles
That's a lot of elements but CSS can handle it.
For the particles, I'll assign each a direction and distance. Using CSS custom properties:
Each particle has:
- --tx: target X offset from center
- --ty: target Y offset from center
- --delay: animation delay
- --color: particle color
- --size: particle size
And a base animation that flies them out.
For the burst hearts, similar but with heart shape (using ♥ character).
OK, I realize I also need to make sure the demo looks good on first load. The user said "something meaningful must be visible" - so the like button should be clearly visible and attractive even before clicking.
Let me design the initial state:
- A nice card/background
- The heart button centered, large, gray/silver heart
- Maybe some text like "Like" below it
- A counter showing "4.2k likes"
And when clicked, it goes CRAZY.
Let me also add a subtle idle animation to the heart (gentle pulse) to make it appetizing.
Now let me write the complete HTML file.
Actually, I should think about the particle positions more carefully. For 18 particles in a circular pattern around the heart:
- Angles: 0°, 20°, 40°, ... 340° (18 particles, 20° apart)
- Distance: random-ish between 60-120px
For 6 burst hearts:
- Angles: 0°, 60°, 120°, 180°, 240°, 300°
- Distance: 50-80px
For 8 sparkles:
- Various positions around the heart, twinkling
For 2 shockwaves:
- Expanding rings
Let me calculate positions. The heart is centered in the label. Particles start at center and fly out.
Actually, to keep it simpler and more maintainable, I'll use a smaller set of particles but make them more colorful and varied.
Let me revise:
- 12 particles (dots) at 30° increments, distance ~70px
- 6 hearts at 60° increments, distance ~55px
- 6 sparkles at various angles, distance ~45px
- 2 shockwave rings
- 1 main heart
For the positions, I'll use pre-calculated translate values.
For 12 particles at 30° increments with distance 70px:
- Angle 0°: tx=70, ty=0
- Angle 30°: tx=60.6, ty=35
- Angle 60°: tx=35, ty=60.6
- Angle 90°: tx=0, ty=70
- Angle 120°: tx=-35, ty=60.6
- Angle 150°: tx=-60.6, ty=35
- Angle 180°: tx=-70, ty=0
- Angle 210°: tx=-60.6, ty=-35
- Angle 240°: tx=-35, ty=-60.6
- Angle 270°: tx=0, ty=-70
- Angle 300°: tx=35, ty=-60.6
- Angle 330°: tx=60.6, ty=-35
For 6 hearts at 60° increments with distance 55px:
- 0°: (55, 0)
- 60°: (27.5, 47.6)
- 120°: (-27.5, 47.6)
- 180°: (-55, 0)
- 240°: (-27.5, -47.6)
- 300°: (27.5, -47.6)
This is getting complex but manageable. Actually, I'll just use rough values and not worry about perfect trig.
OK, let me simplify and make it work. I'll use CSS custom properties for each particle's target position. Each particle element will have inline custom properties.
Actually, wait - I realize I should think about the animation more carefully. The particles need to:
1. Start hidden (opacity: 0, scale: 0)
2. When checked, fly out to their target position while fading
3. Stay hidden after the animation
With CSS animation + `forwards` fill mode, they can stay at the final state (opacity: 0). But the animation needs to be triggered by the checkbox.
The key technique:
```css
/* Initial state - hidden */
.particle {
opacity: 0;
transform: translate(-50%, -50%) scale(0);
}
/* Checked state - animate */
#like-toggle:checked ~ .like-btn .particle {
animation: particle-fly 0.8s cubic-bezier(0.17, 0.67, 0.83, 0.67) var(--delay) forwards;
}
@keyframes particle-fly {
0% {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
100% {
opacity: 0;
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0);
}
}
```
This should work. When checked, the animation plays and the particle flies out. When unchecked, the animation is removed and the particle returns to the hidden state.
One issue: with `forwards`, after the animation ends the particle stays at opacity: 0. But when the checkbox is unchecked, we need the particle to snap back to hidden (which is opacity: 0 anyway). And when re-checked, the animation should replay.
Actually, there's a CSS quirk: if the animation is removed and then re-added (by unchecking and rechecking), the animation will restart. And `forwards` will keep the end state. This should work.
Let me also consider: the heart's main bounce animation. For the heart, I want:
- Initial: gray, normal size
- Checked: red/rainbow, does a big pop bounce
- Maybe a continuous gentle pulse while checked
I'll use multiple animations on the heart when checked.
For the color, I can use a hue-rotate animation or just change the color to red and maybe add a rainbow animation on the border/filter.
Let me make the heart color transition to a nice red/pink gradient and add a rainbow glow.
Actually, for "crazy", let me add a rainbow hue cycle on the heart itself. Using `filter: hue-rotate()` on a red heart would create rainbow cycling.
OK, I think I've planned enough. Let me write the code.
One more thing: the label needs to be clickable, and the checkbox needs to be visually hidden but still functional. I'll use:
```css
.like-checkbox {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
```
Or use `appearance: none` with zero size.
Actually, let me use `display: none` on the checkbox - does that break the label? No, clicking the label still toggles the checkbox even if it's display: none. Wait, actually in some browsers `display: none` might break the label association. Let me use `position: absolute; opacity: 0; pointer-events: none;` or just `appearance: none; opacity: 0; width: 0; height: 0;`.
Hmm, actually `display: none` on an input with a label does work in modern browsers for toggling. But to be safe, I'll use the visually hidden approach.
Let me also think about what the overall page looks like. I want a nice background (maybe a gradient), the like button in the center of a card, with some text. The page should look clean and inviting.
Here's my layout:
- Full viewport background: soft gradient
- Center: a card with the like button, some text, and maybe a counter
- The card has a subtle shadow
Let me write the whole thing now.
For the heart shape, I'll use the ♥ character (U+2665). At a large font size (like 80px), it looks good. I can style it with color, text-shadow, etc.
Actually, for better control, let me use an SVG heart. That way I can have a fill, stroke, and animate the fill color. But the ♥ character is simpler and looks fine. Let me go with the character for simplicity, and use CSS `color` for styling.
Wait, actually, I want to use a CSS-drawn heart shape because:
1. It's more "pure CSS"
2. I can control it better
3. It looks more professional
A CSS heart is made from a rotated square and two circles:
```css
.heart {
width: 50px;
height: 50px;
background: red;
transform: rotate(45deg);
position: relative;
}
.heart::before,
.heart::after {
content: '';
width: 50px;
height: 50px;
background: red;
border-radius: 50%;
position: absolute;
}
.heart::before {
top: -25px;
left: 0;
}
.heart::after {
left: -25px;
top: 0;
}
```
This is clean and works well. But animating the color change requires changing the background of three elements (the heart and its two pseudo-elements). This is doable with CSS.
Actually, let me use the simpler approach with the ♥ character. It's cleaner to animate with just `color` property, and the heart shape is instantly recognizable. I'll use a large font-size and maybe add `text-shadow` for glow effects.
OK, decision: use ♥ character with large font size. Add text-shadow for glow. Use `color` for the heart's color. Use `animation` on the whole span for bounces.
Actually, I just realized I should also think about what "crazy" means. Let me really go wild:
- The heart explodes with 20+ particles
- The heart itself does multiple rapid bounces and a full 360° spin
- Rainbow color cycling
- Confetti dots flying in every direction
- Shockwave rings
- Maybe the whole page shakes slightly
- Stars sparkle around
- The heart briefly splits into multiple hearts that fly away and come back
For the spin: the heart can do a quick 360° rotation during the initial pop. That's crazy.
For rainbow: use `filter: hue-rotate()` animation on the checked state, cycling through all colors.
OK, I'm going to write the code now. Let me structure it well.
I'll keep the particle count manageable but impressive: 16 colored dots, 6 mini hearts, 6 sparkles, 2 shockwaves.
For the heart's checked animation sequence:
1. Initial pop (scale up fast, slightly overshoot, then back)
2. Continuous beat (gentle scale pulsing)
3. The color cycles through rainbow via hue-rotate
For particles: stagger the delays slightly so they burst out in a wave.
Let me also add a "Like" counter that goes from "4.2k" to "4.3k" when checked, with a little bounce.
Let me also consider the animation timing. When the checkbox is checked:
- t=0ms: heart starts popping, shockwave starts expanding
- t=0-100ms: particles start flying out (staggered delays)
- t=0-150ms: burst hearts fly out
- t=100-300ms: sparkles appear
- t=0-500ms: shockwave rings expand and fade
The overall effect should be a burst of energy.
For the initial state (before clicking):
- Heart is gray, with a subtle hint of color
- Gentle idle pulse
- Counter shows "4.2k likes"
- Maybe a finger pointer icon or "Click me" hint
OK, I need to write this. Let me also make sure the page looks good at 1280x800 and scales down to 375px (mobile). The like button should be centered and responsive.
Let me also add a small floating animation on the heart so it's clearly interactive.
Now, about the background: I want something nice. A gradient from dark blue/purple to pink, or a light warm gradient. Since we're doing a "like" button, a light, warm, social-media-inspired background feels right. Maybe a subtle gradient from white to light pink/purple.
Or maybe darker for more dramatic effect with the colorful particles. Let me go with a dark-ish background so the colorful particles really pop. A dark purple/navy gradient.
Actually, keep it clean. Light background with the heart button being the focal point. The particles will still be visible with bright colors.
Let me make it a pleasant, modern design. Light background, the like button in a card.
Here's the final structure I'm going with:
```html
<!DOCTYPE html>
<html>
<head>
<title>Like Button - Pure CSS</title>
<style>
/* ... */
</style>
</head>
<body>
<div class="page">
<div class="card">
<p class="caption">Tap the heart</p>
<div class="like-container">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">
<span class="heart">♥</span>
<!-- Particles -->
<span class="particle p1"></span>
<span class="particle p2"></span>
<!-- ... more particles ... -->
<!-- Burst hearts -->
<span class="burst-heart bh1">♥</span>
<!-- ... more burst hearts ... -->
<!-- Shockwaves -->
<span class="shockwave sw1"></span>
<span class="shockwave sw2"></span>
<!-- Sparkles -->
<span class="sparkle s1">✦</span>
<!-- ... more sparkles ... -->
</label>
</div>
<p class="like-count"><span class="count">4.2k</span> likes</p>
</div>
</div>
</body>
</html>
```
Now, for the particle positions, I'll use CSS custom properties. Each particle will have inline `--tx` and `--ty` values for its target position.
Let me define 16 particles in a circle. I'll use angles: 0°, 22.5°, 45°, 67.5°, 90°, ... 337.5° (16 particles).
Distance: I'll vary the distance for each particle (some fly further, some closer) for a more organic feel.
Actually, for simplicity, I'll use a consistent distance and just 12 particles (30° apart) at distance 65-70px.
But wait, I want it to be "crazy". More particles = more crazy. Let me do 18 particles (20° apart) at varying distances.
For 18 particles at 20° increments, distances between 55-100px:
Angles in degrees: 0, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340
I'll compute approximate positions. Using simple trigonometry:
- cos(θ) and sin(θ) for each angle
Angle 0°: (1.00, 0.00) → (100, 0) at d=100
Angle 20°: (0.94, 0.34) → (94, 34)
Angle 40°: (0.77, 0.64) → (77, 64)
Angle 60°: (0.50, 0.87) → (50, 87)
Angle 80°: (0.17, 0.98) → (17, 98)
Angle 100°: (-0.17, 0.98) → (-17, 98)
Angle 120°: (-0.50, 0.87) → (-50, 87)
... etc.
I'll vary the distance for each: some at 60px, some at 80px, some at 100px.
OK, this is getting very detailed. Let me just pick numbers that look reasonable and write them out. I don't need perfect trig - just a nice distribution.
For the 18 particles, I'll use these (tx, ty) values (in pixels), with some variation in distance:
(80, 0), (70, 30), (50, 55), (25, 75), (-10, 70), (-40, 60), (-65, 40), (-75, 10), (-70, -25), (-55, -50), (-30, -70), (0, -80), (30, -70), (55, -50), (70, -30), (75, 0), (60, 35), (35, 60)
That's 18 values, roughly circular. Some distances are shorter, some longer.
For burst hearts (6):
(60, 0), (30, 52), (-30, 52), (-60, 0), (-30, -52), (30, -52)
For sparkles (6):
(40, -40), (-45, -35), (-50, 30), (-10, 55), (35, 35), (15, -55)
Actually, let me just go and write it. I'll assign them inline.
For the colors:
- Particles: mix of pink (#ff4d6d), orange (#ff8c42), yellow (#ffd60a), purple (#b56cff), teal (#4dd4ac), coral (#ff6b6b)
- Burst hearts: pink/red shades
- Sparkles: gold/white
The heart color when checked:
- Via hue-rotate animation on a base red color
- Or use different shades
OK, I think I've planned enough. Let me write the complete file now.
Actually, one more thought: the .like-btn label needs to be big enough to be easily clickable. I'll make it about 100-120px in size. The heart character at 70-80px font-size fits well.
Also, for the shake effect: I can animate the .like-btn container with a quick shake when checked. This adds to the "crazy" feel. I'll apply a shake animation to the label or the entire card.
Actually, let me add the shake to the like-container (the wrapper around the button).
Wait, the shake might be too much with all the particles. Let me make it subtle - a quick 2-3 frame shake right at the start.
OK let me now write the complete code. I'll aim for a polished, impressive result.
One final consideration: the "forwards" fill mode on animations when the checkbox is checked. If the checkbox stays checked, the particles will be at their end state (hidden). The heart will have its continuous animation. When unchecked, everything resets. This is correct behavior.
But what about the heart's initial pop animation? I want it to play once when checked, then transition to the continuous pulse. I can use two animations on the heart:
When checked:
- `heart-pop` - plays once, 0.6s duration
- `heart-beat` - continuous pulse, starts after the pop
Or I can have a single animation that does the pop then settles into a gentle pulse. A keyframe animation with a complex curve:
```css
@keyframes heart-pop-beat {
0% { transform: scale(1); }
15% { transform: scale(1.5) rotate(10deg); }
30% { transform: scale(0.9) rotate(-5deg); }
45% { transform: scale(1.2) rotate(3deg); }
60% { transform: scale(1); }
70% { transform: scale(1.1); }
80% { transform: scale(1); }
90% { transform: scale(1.05); }
100% { transform: scale(1); }
}
```
This would be a one-time animation that does the pop and settles. For continued beating, I'd need a separate infinite animation. Let me use two animations with delay:
```css
#like-toggle:checked ~ .like-btn .heart {
animation: heart-pop 0.6s ease-out forwards, heart-beat 1.2s 0.6s ease-in-out infinite;
}
```
This way, the pop plays first (0.6s), then the beat starts and goes forever.
For the hue-rotate rainbow, another animation:
```css
animation: heart-pop 0.6s ease-out forwards, heart-beat 1.2s 0.6s ease-in-out infinite, hue-cycle 3s 0.6s linear infinite;
```
But `filter: hue-rotate()` cycles through rainbows. On a red heart, hue-rotate(0) is red, hue-rotate(60) is yellow-green, etc. This would make the heart cycle through different colors - crazy but maybe a bit weird. Let me instead use a slight color shift, or keep the heart red and just add a rainbow glow.
Actually, for "crazy", let me cycle the hue but keep it subtle. Or just keep the heart red/pink and make the particles rainbow-colored.
I think a solid red heart looks best for a "like" button. The particles can be rainbow.
Let me finalize the design and write it.
Here's my complete plan:
1. Centered card on a nice background
2. Like button with heart (♥) character
3. When clicked:
- Heart pops (scale bounce) and turns from gray to red
- Heart does a continuous gentle beat after the pop
- 18 colorful particles burst out radially
- 6 mini hearts fly out
- 2 shockwave rings expand
- 6 sparkles twinkle
- The card does a quick shake
- The count updates with a flip
4. When clicked again:
- Everything resets smoothly
- Heart goes back to gray
- All animations stop
Let me write the code now. I'll include all the details.
For the shake, I'll apply it to the .card or .like-container. Actually, make it the like-container (the button area). A quick horizontal shake.
For the count update, I have "4.2k" that changes to "4.3k". This requires two separate elements or a trick. Let me use two spans, one visible when unchecked, one when checked. Or simpler: show "+1" next to the count when checked.
Actually, let me keep the count simple. I'll have the number "4.2k" and when checked, it changes to "4.3k" with a little bounce animation. I can do this by having two spans and toggling visibility, or use the `content` property with pseudo-elements.
Simpler approach: use one span with the number, and a second span "+1" that appears when checked with a little animation.
OK, let me just write the code now. I've been planning way too long.
I think I'll do 12-14 particles, not 18, to keep the code cleaner. Still plenty crazy.
Let me finalize particle positions for 14 particles:
1. (85, 0)
2. (70, 45)
3. (35, 70)
4. (-15, 80)
5. (-55, 55)
6. (-75, 20)
7. (-70, -30)
8. (-45, -65)
9. (0, -85)
10. (40, -70)
11. (65, -45)
12. (75, -10)
13. (30, 75)
14. (-30, -75)
These are roughly distributed but not perfect. Let me craft them more carefully for a nice circular burst:
Actually, I realize I'm overcomplicating this. Let me use 12 particles at perfect 30° intervals with a distance of 70px:
- 0°: (70, 0)
- 30°: (60.6, 35) ≈ (61, 35)
- 60°: (35, 60.6) ≈ (35, 61)
- 90°: (0, 70)
- 120°: (-35, 60.6) ≈ (-35, 61)
- 150°: (-60.6, 35) ≈ (-61, 35)
- 180°: (-70, 0)
- 210°: (-60.6, -35) ≈ (-61, -35)
- 240°: (-35, -60.6) ≈ (-35, -61)
- 270°: (0, -70)
- 300°: (35, -60.6) ≈ (35, -61)
- 330°: (60.6, -35) ≈ (61, -35)
These are clean values. I'll use these.
For the burst hearts (6 at 60° intervals, distance 50):
- 0°: (50, 0)
- 60°: (25, 43.3) ≈ (25, 43)
- 120°: (-25, 43.3) ≈ (-25, 43)
- 180°: (-50, 0)
- 240°: (-25, -43.3) ≈ (-25, -43)
- 300°: (25, -43.3) ≈ (25, -43)
For sparkles (6), I'll vary their positions:
- (35, -50)
- (-40, -40)
- (-55, 20)
- (-20, 55)
- (30, 45)
- (50, -25)
OK now I'll actually write the complete HTML file. Let me make the code comprehensive and well-organized.
For the particles, I'll use small circular spans with background colors. Their size will be 8-14px varied.
For the burst hearts, they'll be ♥ characters at smaller font sizes (16-22px).
For the sparkles, ✦ or ★ characters in gold/white.
For the shockwaves, bordered circles that expand.
Let me also think about the delays. Particles should have a slight stagger so they burst out in sequence. I'll use delays from 0ms to 150ms.
Color palette for particles:
- #ff4d6d (pink-red)
- #ff8fa3 (light pink)
- #ffb3c6 (paler pink)
- #ffd60a (yellow)
- #ff9e00 (orange)
- #b56cff (purple)
- #4dd4ac (teal)
- #ff6b6b (coral)
- #f72585 (magenta)
- #7209b7 (deep purple)
- #ff85a1 (rose)
- #ffd166 (gold)
These colors will make the burst look vibrant and colorful.
For the heart's gray → red transition, I'll use `transition` on the color property, but also the animation will apply transforms.
Hmm, combining transitions and animations on the same element can be tricky. Let me use a transition for the color, and animations for the transforms.
Actually, let me use `transition: color 0.3s` on the heart for the gray→red change, and animations for the bounce/pulse. The transition will handle the color smoothly.
Wait, but if I also have a hue-rotate animation, it might conflict. Let me keep the color as a simple transition (gray → red) and not do hue-rotate on the heart. The rainbow vibe will come from the particles.
OK, final plan for the heart:
- Unchecked: gray (#b0b0b0), normal size
- Checked: red (#ff2d55), with pop animation then gentle beat
- Use `transition: color 0.2s` for the color change
- Use `animation` for transforms (pop + beat)
- Add a nice glow (text-shadow) when checked
Now, let me write the code. This is going to be a substantial file.
I also need a gradient background for the page. Something modern.
For the count, I'll show "4.2k" and when checked, flip it to "4.3k" with a little bounce. Actually, let me keep the count static or just show a "+1" that appears. Simpler and less error-prone.
Actually, I realize the count thing adds another layer. Let me keep it simple - just have "4.2k likes" text and the heart does the talking.
Or: have "4.2k" that changes to "4.3k". But this requires the text to change on checked state. I can use the `:checked` selector to show/hide different spans:
```html
<span class="count-unchecked">4.2k</span>
<span class="count-checked">4.3k</span>
```
With CSS:
- Unchecked: show .count-unchecked, hide .count-checked
- Checked: hide .count-unchecked, show .count-checked
And the .count-checked can have a bounce animation.
OK, let me include this. It adds a nice touch.
Now I'll start writing the actual HTML/CSS. Let me be really thorough.
Also, I want the whole damn thing to be visually stunning. Let me add a nice gradient background, a card with shadow, maybe some floating shapes in the background (decorative circles), and the like button centered.
For the page:
- Background: linear-gradient from a soft lavender to a warm pink/beige
- Card: white, rounded, shadow, centered
- Heart button: large, prominent, inviting
For the "crazy" animation on click, I want:
- 12 colorful dots bursting out
- 6 mini hearts flying out
- 2 shockwave rings
- 6 sparkles twinkling
- Heart pops and turns red
- Card shakes briefly
- Counter bumps up
Let me also add some decorative elements around the button that are always visible - maybe some floating dots that become the particles when clicked. Or just keep the particles hidden until clicked.
OK, I'll keep particles hidden until clicked. That's the big reveal.
Alright, writing the code now for real.
One more thing: the shockwave rings. These are basically circular borders that expand and fade. They should be absolutely positioned, centered on the heart, and when checked, they animate from scale(0) to scale(1.5-2.5) with opacity fade.
For the ring to be visible, it needs a border (2-3px solid) and be a perfect circle. The heart button is maybe 100-120px, so the rings should start from around the heart and expand outward to 200-300px.
I'll position the rings to be the same size as the heart initially, then expand.
OK here's my code. I'm writing it now.
Hmm, also, I need to think about how the heart element is positioned. The label needs to be `position: relative` so all children can be absolutely positioned relative to it. The heart is centered in the label.
Label dimensions: let's say 120px × 120px. The heart is centered inside.
Heart: ♥ character, font-size maybe 70px, centered using flexbox or absolute positioning with transform.
Actually, let me use flexbox on the label to center the heart, and absolutely position the particles to the center of the label.
For the particles, they need to start at the center of the label (which is where the heart is). So they should be positioned at `top: 50%; left: 50%;` with `transform: translate(-50%, -50%)` initially, then animate outward.
For the burst hearts, same thing - start at center, fly outward.
For shockwaves, start at center, expand outward.
For sparkles, they can be at various positions around the heart, maybe slightly offset, and just twinkle (appear, scale, fade).
OK, let me also think about the tap target size. The label should be at least 44×44px (accessibility), but for this demo, bigger is better. I'll make it about 110-130px.
Actually, maybe I should make the clickable area encompass the heart plus some surrounding space so it's easy to click.
Let me set the label to be 120px × 120px with the heart centered. The particles overlay within this area and fly beyond it.
For particles that fly beyond the label boundary, I need `overflow: visible` on the label (which is the default). The particles can extend beyond the label's rectangular area using transforms.
OK, I'm confident I have everything planned. Let me write the complete HTML file.
I'll keep the code organized with sections:
1. Page styles
2. Card styles
3. Like button styles (label, checkbox, heart)
4. Particle styles
5. Burst heart styles
6. Shockwave styles
7. Sparkle styles
8. Animation keyframes
9. Checked state styles
Here goes:
One thing I want to make sure: the label should have `cursor: pointer` so it's obviously clickable. I'll also add a subtle glow/hover effect.
For the initial heart pulse (before clicking), I want a gentle beating animation so it's clearly alive and inviting. This will be an infinite animation on the unchecked heart.
Then when checked, the animation changes to the pop + beat sequence.
Actually, for the unchecked state, let me use a gentle pulse that's slower than the checked beat. This gives a nice contrast.
Let me also think about the transition from unchecked to checked. The heart color should transition smoothly from gray to red. Using `transition: color 0.3s` on the heart.
When unchecked, the heart has a gentle pulse. When checked, it has the pop animation followed by the faster beat.
But wait - if I change the animation when the checkbox goes from unchecked to checked, the new animation starts from the beginning. That's what I want for the pop. And when unchecked, the gentle pulse animation resumes (from the beginning, but since it's infinite, it doesn't matter).
For the continuous beat when checked, I'll use a separate animation that starts after the pop (with a delay).
OK, let me finalize the keyframes:
Gentle pulse (unchecked):
```css
@keyframes idle-pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.08); }
}
```
Pop (checked, plays once):
```css
@keyframes pop {
0% { transform: scale(1); }
20% { transform: scale(1.6) rotate(15deg); }
40% { transform: scale(0.85) rotate(-10deg); }
60% { transform: scale(1.25) rotate(5deg); }
80% { transform: scale(0.95); }
100% { transform: scale(1); }
}
```
Beat (checked, continuous after pop):
```css
@keyframes beat {
0%, 100% { transform: scale(1); }
20% { transform: scale(1.12); }
40% { transform: scale(1); }
60% { transform: scale(1.08); }
80% { transform: scale(1); }
}
```
For the particles, each one has:
```css
@keyframes particle-burst {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 1;
}
70% {
opacity: 1;
}
100% {
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0.2);
opacity: 0;
}
}
```
The particle starts at center, flies outward, fades near the end. Duration maybe 0.7-0.9s depending on distance.
For shockwaves:
```css
@keyframes shockwave-expand {
0% {
transform: translate(-50%, -50%) scale(0.3);
opacity: 0.9;
border-width: 3px;
}
100% {
transform: translate(-50%, -50%) scale(2.2);
opacity: 0;
border-width: 1px;
}
}
```
Shockwave duration maybe 0.6s.
For sparkles (twinkle):
```css
@keyframes sparkle-twinkle {
0% {
transform: translate(-50%, -50%) scale(0) rotate(0deg);
opacity: 0;
}
30% {
opacity: 1;
}
60% {
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(1.5) rotate(180deg);
opacity: 0;
}
}
```
Sparkles have delays so they twinkle one after another. Duration about 0.8s.
For the card shake:
```css
@keyframes card-shake {
0%, 100% { transform: translateX(0); }
10% { transform: translateX(-6px) rotate(-1deg); }
20% { transform: translateX(6px) rotate(1deg); }
30% { transform: translateX(-4px); }
40% { transform: translateX(4px); }
50% { transform: translateX(-2px); }
60% { transform: translateX(2px); }
70% { transform: translateX(0); }
}
```
Duration about 0.5s.
OK, I think I'm fully ready now. Let me write the complete HTML file.
For the count update, I'll have:
- Unchecked: "4.2k" shown
- Checked: "4.3k" shown with a bounce
The bounce animation for the checked count:
```css
@keyframes count-bounce {
0% { transform: scale(0.5); opacity: 0; }
40% { transform: scale(1.3); opacity: 1; }
70% { transform: scale(0.9); }
100% { transform: scale(1); }
}
```
OK, now writing the complete code. I need to be very careful with all the selectors and animations.
The structure will use the checkbox hack:
```
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">
<span class="heart">♥</span>
<span class="particle p1"></span>
<span class="particle p2"></span>
... etc
</label>
```
CSS selectors:
- `#like-toggle:checked ~ .like-btn .heart` → checked heart styles
- `#like-toggle:checked ~ .like-btn .particle` → checked particle animations
- etc.
The particles, burst hearts, shockwaves, and sparkles are all inside the label. They're siblings of the heart inside the label.
For the card shake, the checkbox needs to be a sibling of the card or like-container. Let me restructure:
```html
<div class="like-container">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">
<span class="heart">♥</span>
<!-- particles, etc. -->
</label>
</div>
```
Then `#like-toggle:checked ~ .like-btn` works for the label. For the container shake, I need the checkbox to be a sibling of the container:
```html
<div class="like-area">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<div class="like-container">
<label for="like-toggle" class="like-btn">
<span class="heart">♥</span>
<!-- ... -->
</label>
</div>
</div>
```
Then `#like-toggle:checked ~ .like-container` would apply the shake to the container. But this changes the sibling relationship. Let me think.
Actually, the shake should apply to the like-btn (label) or like-container (parent of the label).
If I use:
```html
<div class="like-area">
<input type="checkbox" id="like-toggle">
<label for="like-toggle" class="like-btn">...</label>
</div>
```
Then `#like-toggle:checked ~ .like-btn` works. And I can apply the shake to `#like-toggle:checked ~ .like-btn` directly. But the shake would affect the label, which also has the particles. The shake might look weird combined with the particle burst.
Actually, the shake could add to the "crazy" feel. The whole button shakes while particles fly out. That could look great!
But I also want the particles to be positioned relative to the label. If the label shakes, the particles shake too, which might be fine or might be weird.
Let me put the shake on a wrapper that contains just the button (not the particles). Actually, all elements are within the label. Shaking the label would shake everything.
Hmm, let me reconsider the structure. What if the particles are siblings of the label, not children?
```html
<div class="like-area">
<input type="checkbox" id="like-toggle">
<label for="like-toggle" class="like-btn">
<span class="heart">♥</span>
</label>
<!-- Particles as siblings of the label, positioned relative to .like-area -->
<span class="particle p1"></span>
<span class="particle p2"></span>
<!-- ... -->
</div>
```
This way:
- `.like-area` is `position: relative`
- The heart is centered in the label
- Particles are positioned absolutely relative to .like-area
- The label shake won't affect the particles
Selectors:
- `#like-toggle:checked ~ .particle` → particle animations
- `#like-toggle:checked ~ .like-btn` → label shake and heart animation
This is cleaner for the shake effect. But the particles need to start from the center of the heart, which is the center of the label. Since the label is centered in .like-area, the center of .like-area should match the center of the label (if the label is the only child that defines the size).
This requires .like-area to be the same size as the label, or the particles to be positioned at the center of .like-area (which should match the center of the heart).
Let me set:
- .like-area: 130px × 130px, position: relative
- .like-btn (label): 130px × 130px, display: block (or flex), centered
- Heart: centered in the label
- Particles: absolutely positioned with `top: 50%; left: 50%; transform: translate(-50%, -50%)` initially, then animating outward
Since the label is the same size as .like-area, the centers align. This works.
For the shake, apply it to .like-btn (label) when checked. The particles (siblings) won't be affected by the shake.
But wait, will the shake look right if only the label shakes and not the particles? The heart shakes while particles burst out statically. That could look a bit disjointed.
Actually, for maximum crazy, let me apply the shake to .like-area (the container of both the label and particles). That way everything shakes together:
```css
#like-toggle:checked ~ .like-btn { animation: card-shake 0.5s ...; }
```
But if the shake is on the label and particles are siblings of the label, the particles won't shake. Let me apply the shake to a parent of both.
With the structure above, both .like-btn and .particle elements are siblings inside .like-area. To shake both, I can shake .like-area itself:
```css
#like-toggle:checked ~ * { } /* Too generic */
```
Actually, I can use `:has()` for this, but that's a newer CSS feature. Or I can put the shake on the .like-area using a different approach.
Alternatively, let me just not do a card shake. The particle burst and heart pop are already crazy enough. The shake might be overkill.
Let me reconsider. The heart pop + particle burst + shockwaves + sparkles is already a lot. The shake would add even more. I'll include a subtle shake for extra flair if I can structure it cleanly.
Actually, let me just keep all elements inside the label and apply the shake to the label. The particles will shake along with the heart, which could actually look cool - like the whole button is vibrating with energy as particles burst out. This is fine.
New plan: everything inside the label. Shake the label. Particles are children of the label. When the label shakes, everything shakes together. This is actually MORE crazy and cohesive.
OK, final structure:
```html
<div class="like-wrapper">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">
<span class="heart">♥</span>
<span class="particle p1"></span>
<span class="particle p2"></span>
<!-- ... more particles ... -->
<span class="burst-heart bh1">♥</span>
<!-- ... more burst hearts ... -->
<span class="shockwave sw1"></span>
<span class="shockwave sw2"></span>
<span class="sparkle sp1">✦</span>
<!-- ... more sparkles ... -->
</label>
</div>
```
And:
```css
#like-toggle:checked ~ .like-btn {
animation: like-shake 0.5s ease-out;
}
```
This shakes the entire label (heart + particles + everything). Particles fly out while the whole thing shakes. This is the "crazy" effect.
Wait, but if the particles are children of the label and the label is shaking, the particles' animations (which use transform) will be combined with the label's transform. The particle animations use `animation` with their own `transform` values, while the label also has an `animation` with its own `transform`. These composit - the child's transform is relative to the parent's transformed coordinate space.
This should work fine. The particles will fly out from their starting positions (center of the label) while the label shakes. The shake is a small translation/rotation, so the particles will have a slight wobble as they fly out, but this could look natural (like the burst is shaking the button).
OK, I'm going with this approach. Let me now actually write the code.
Shake animation on the label:
```css
@keyframes like-shake {
0%, 100% { transform: translateX(0) rotate(0); }
10% { transform: translateX(-4px) rotate(-0.5deg); }
20% { transform: translateX(4px) rotate(0.5deg); }
30% { transform: translateX(-3px) rotate(-0.3deg); }
40% { transform: translateX(3px) rotate(0.3deg); }
50% { transform: translateX(-2px); }
60% { transform: translateX(2px); }
70% { transform: translateX(-1px); }
80% { transform: translateX(1px); }
90% { transform: translateX(0); }
}
```
Duration: 0.4s (quick shake at the moment of click).
OK, I'm finally writing the complete code. Let me be comprehensive.
Actually, I also need to handle the case where the page is being viewed at mobile sizes. The like button should scale down appropriately. I'll use relative units or media queries.
For simplicity, I'll use fixed pixel sizes that work well at 375px width:
- Card: max-width 400px, with padding
- Heart: 70px font size
- Like button area: 120px
- Particles: fly out to 50-80px
These should all fit on mobile.
Let me also think about the background more. I want:
- A warm, inviting gradient
- Maybe some subtle floating shapes
- The card centered with a nice shadow
Page background: `linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)` or something more colorful like `linear-gradient(135deg, #667eea 0%, #764ba2 100%)`. Since we want the like button to pop, maybe a darker background.
Or, for a social media feel: light background with a subtle pink tint.
Let me go with a soft, elegant background: `linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%)` - warm beige/white.
Or: a modern dark gradient: `linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%)` with the card being white and the like button colorful. This would make the colorful particles really pop against the dark background.
I think a light background is more appropriate for a "like" button demo. Let me use a soft gradient with light pink/purple tones.
Background: `linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%)` - warm sunset gradient? That's nice.
Or `linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%)` - soft purple to blue.
Let me use something modern and soft:
`background: linear-gradient(135deg, #f5f7fa 0%, #e3eeff 50%, #fef3e2 100%);`
Actually, for drama, I want the background to not compete with the animation. Let me use a slightly darker background so the colorful burst stands out:
`background: linear-gradient(135deg, #2c3e50 0%, #4a569d 50%, #6a11cb 100%);`
With white card on top. This will make the white card pop and the colorful particles visible against the white card.
Hmm, but the particles will be positioned over the white card (since they're inside the label which is in the card). The particles will be visible against the white card surface. Colorful particles on white = great contrast.
OK let me use the dark-ish background with a white card.
Actually, for a "like" button, lighter is more typical (Instagram/Facebook style). Let me use:
- Page: soft gradient
- Card: white with shadow
- Heart: gray initially, red when liked
- Particles: colorful
This is clean and the particles will pop against the white card. The dark gradient behind the white card will also make the card stand out.
OK let me settle: dark-ish gradient page, white card, colorful particles. This is beautiful.
Alright, I need to stop overthinking and WRITE THE CODE. Here goes.
I'll include:
- 12 particles with unique positions and colors
- 6 burst hearts
- 2 shockwave rings
- 6 sparkles
- Heart with idle pulse → pop → beat
- Card shake on click
- Count update (4.2k → 4.3k)
- "Click me" hint that fades after first click
Let me also add the "Click me" text that's visible initially. When checked, it disappears. This gives a nice touch.
For the hint, I can use the `:checked` state to hide it:
```css
#like-toggle:checked ~ .hint { opacity: 0; }
```
But the hint needs to be a sibling of the checkbox. Let me include it in the structure.
OK here's the complete structure:
```html
<div class="page">
<div class="card">
<div class="like-wrap">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">
<span class="heart">♥</span>
<!-- 12 particles -->
<span class="particle p1"></span>
<!-- ... -->
<!-- 6 burst hearts -->
<span class="burst-heart b1">♥</span>
<!-- ... -->
<!-- 2 shockwaves -->
<span class="shockwave sw1"></span>
<span class="shockwave sw2"></span>
<!-- 6 sparkles -->
<span class="sparkle sp1">✦</span>
<!-- ... -->
</label>
</div>
<div class="hint">Tap the heart!</div>
<div class="likes">
<span class="count-unchecked">4.2k</span>
<span class="count-checked">4.3k</span> likes
</div>
</div>
</div>
```
Wait, for the hint to be controlled by the checkbox, the hint needs to be a sibling of the checkbox. Let me restructure:
```html
<div class="like-wrap">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">...</label>
<div class="hint">Tap the heart!</div>
</div>
```
Then `#like-toggle:checked ~ .hint { opacity: 0; }` works.
And the count:
```html
<div class="likes">
<span class="count-unchecked">4.2k</span>
<span class="count-checked">4.3k</span> likes
</div>
```
With:
```css
.count-checked { display: none; }
#like-toggle:checked ~ .count-checked { display: inline; animation: count-bounce 0.4s; }
#like-toggle:checked ~ .count-unchecked { display: none; }
```
But wait, the count is not a sibling of the checkbox. Let me restructure so everything is within the same parent:
```html
<div class="like-section">
<input type="checkbox" id="like-toggle" class="like-checkbox">
<label for="like-toggle" class="like-btn">...</label>
<div class="hint">Tap the heart!</div>
<div class="likes">
<span class="count-unchecked">4.2k</span>
<span class="count-checked">4.3k</span> likes
</div>
</div>
```
All siblings of the checkbox. The checkbox is first. This works for all selectors.
Now I'm really writing the code for real. Let me make it comprehensive and beautiful.
One last thing: the badge/number animation. When checked, the counter goes from 4.2k to 4.3k. I want the 4.3k to bounce in. This is a nice touch.
Also, I should consider what happens when unchecked again. The hint should come back, the counter goes back to 4.2k, the heart goes back to gray. This is all handled by the CSS selectors reverting when the checkbox is no longer checked.
Alright, writing the code NOW. No more planning.
I realize I need to also think about what the particles look like in the initial state. They need to be completely hidden (opacity: 0, scale: 0) so they're not visible before clicking. When checked, the animation runs and they burst out, then fade away.
For the burst hearts and particles, I'll use the same pattern: hidden initially, animate when checked.
Here's the pattern for a particle:
```css
.particle {
position: absolute;
top: 50%;
left: 50%;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--color, #ff4d6d);
opacity: 0;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
}
#like-toggle:checked ~ .like-btn .particle {
animation: particle-burst var(--duration, 0.8s) cubic-bezier(0.17, 0.67, 0.83, 0.67) var(--delay, 0s) forwards;
}
```
And in the keyframes:
```css
@keyframes particle-burst {
0% {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
70% {
opacity: 1;
}
100% {
opacity: 0;
transform: translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px))) scale(0);
}
}
```
Each particle gets `--tx`, `--ty`, `--delay`, `--color`, and optionally `--duration` and `--size` via inline styles.
Let me assign colors to particles:
- p1: #ff4d6d (pink-red)
- p2: #ff8c42 (orange)
- p3: #ffd60a (yellow)
- p4: #4dd4ac (teal)
- p5: #b56cff (purple)
- p6: #ff6b6b (coral)
- p7: #f72585 (magenta)
- p8: #4ecdc4 (cyan)
- p9: #ffe66d (light yellow)
- p10: #ff85a1 (rose)
- p11: #a8e6cf (mint)
- p12: #d4a5f5 (lavender)
These are vibrant, playful colors.
For particle sizes, I'll vary them: some 8px, some 12px, some 15px. Using `width` and `height` inline or via CSS classes.
Actually, to simplify, I'll set the size using CSS custom properties and set them inline:
```html
<span class="particle" style="--tx:70px; --ty:0px; --delay:0s; --color:#ff4d6d; --size:12px;"></span>
```
And in CSS:
```css
.particle {
width: var(--size, 10px);
height: var(--size, 10px);
background: var(--color, #ff4d6d);
}
```
This is clean. Each particle is self-contained with its inline styles.
For the burst hearts, similar:
```html
<span class="burst-heart" style="--tx:50px; --ty:0px; --delay:0.05s;">♥</span>
```
With:
```css
.burst-heart {
position: absolute;
top: 50%;
left: 50%;
font-size: 18px;
color: #ff4d6d;
opacity: 0;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
}
```
And the animation is similar to particles.
For shockwaves:
```html
<span class="shockwave" style="--delay:0s;"></span>
<span class="shockwave" style="--delay:0.1s;"></span>
```
With:
```css
.shockwave {
position: absolute;
top: 50%;
left: 50%;
width: 80px;
height: 80px;
border-radius: 50%;
border: 3px solid #ff4d6d;
opacity: 0;
transform: translate(-50%, -50%) scale(0.3);
pointer-events: none;
}
```
And animation:
```css
@keyframes shockwave-expand {
0% {
opacity: 0.8;
transform: translate(-50%, -50%) scale(0.3);
border-width: 3px;
}
100% {
opacity: 0;
transform: translate(-50%, -50%) scale(2.5);
border-width: 1px;
}
}
```
For sparkles:
```html
<span class="sparkle" style="--tx:35px; --ty:-50px; --delay:0.15s; --size:14px;">✦</span>
```
With animation for twinkling.
OK, this is becoming very long. Let me write the final code now and make sure it all works together.
I'll estimate the HTML size - it's going to be substantial (200+ lines). That's fine for a demo.
Let me also make sure the checkbox is properly hidden but still clickable via the label. I'll use:
```css
.like-checkbox {
position: absolute;
opacity: 0;
width: 0.1px;
height: 0.1px;
pointer-events: none;
}
```
The label will be clickable and will toggle the checkbox.
Also, I should note that the focus state for accessibility. I'll add a visible focus ring on the label when the checkbox is focused using `:focus-visible`.
OK, HERE WE GO. Writing the complete HTML file now. I'll make sure everything is inline, no external resources.
One more thing: for the heart's color when checked, I want it to be a vibrant red (#ff2d55 or #ff4757). When unchecked, gray (#b0b0b0). I'll use a transition for the color change, plus the animation for transforms.
Wait, I also want the heart to have a nice glow when checked. I'll use `text-shadow` with a red/pink glow. The text-shadow can also transition.
Alright, I'm done planning. The code is going to be comprehensive. Let me write it all out now.
I'll also include the hint "Tap the heart!" which fades away when the heart is liked (checked). And the count "4.2k" / "4.3k".
For the overall card design:
- Rounded corners (16px radius)
- White background
- Box shadow
- Padding
- Centered text
- The like button is prominent
The page will have:
- A gradient background
- The card centered
- Some decorative floating circles in the background (pure CSS, optional)
I'll skip the decorative background circles to keep the code focused on the like button animation.
Let me also add a subtle hover effect on the like button:
```css
.like-btn:hover .heart {
transform: scale(1.05);
}
```
Wait, but the heart has animations. The hover would interfere. Let me apply hover to the label:
```css
.like-btn:hover {
transform: scale(1.03);
}
```
But the label also has a shake animation when checked. The hover transform and shake animation could conflict. I'll use `transition` for the hover and the animation will override it momentarily.
Actually, for simplicity, I'll use a subtle glow on hover via box-shadow on the label or just skip the hover effect. The heart's idle pulse is already inviting.
Alright, I'm writing the full code now. For real this time. I'll be thorough and make it beautiful.
I need to think about one more thing: when the checkbox is toggled off and on again quickly, do the animations replay? Yes, because the `animation` property is added/removed based on the checked state. When unchecked, animations are removed and elements return to their initial state. When re-checked, animations are re-applied and restart.
However, there's a subtlety with `forwards` fill mode: after the animation ends (with forwards), the element stays at the end state. When the checkbox is unchecked, the animation is removed and the element returns to its default state (opacity: 0, scale: 0 for particles). When re-checked, the animation replays. This is correct.
For the heart's continuous beat (infinite animation), when unchecked the animation stops. The heart has a `transition` for the color, so it smoothly transitions back to gray.
One issue: the heart's checked state has TWO animations (pop + beat). When unchecked, both are removed. The heart's `transform` returns to the default (scale(1) from the idle pulse or no transform). This should be fine.
For the idle pulse (unchecked), I'll apply it to the heart:
```css
.heart {
animation: idle-pulse 2s ease-in-out infinite;
}
```
When checked:
```css
#like-toggle:checked ~ .like-btn .heart {
animation: heart-pop 0.6s ease-out forwards, heart-beat 1s 0.6s ease-in-out infinite;
color: #ff2d55;
text-shadow: 0 0 25px rgba(255, 45, 85, 0.5), 0 0 50px rgba(255, 45, 85, 0.3);
}
```
The `animation` property overrides the idle pulse when checked. When unchecked, the idle pulse resumes (it was already there in the base .heart style).
Yes, this works. The checked state overrides the animation, and when unchecked, the base animation resumes.
OK, here's the final code. I'm writing it all out now.
Wait, one thing: for the sparkles, should they be at different positions around the heart (not starting from center but just appearing at their positions)? Yes, sparkles should appear at their positions around the heart. So they don't fly out from center - they just twinkle at their assigned positions.
So sparkles are different from particles:
- Particles: start at center, fly outward
- Sparkles: appear at fixed positions around the heart, twinkle in place
For sparkles, the animation should just scale them from 0 to 1 (with some rotation) and then fade out, without changing their position (or with a slight drift).
For the sparkle animation:
```css
@keyframes sparkle-twinkle {
0% {
opacity: 0;
transform: translate(-50%, -50%) scale(0) rotate(0deg);
}
30% {
opacity: 1;
transform: translate(-50%, -50%) scale(1) rotate(90deg);
}
60% {
opacity: 1;
transform: translate(-50%, -50%) scale(0.8) rotate(180deg);
}
100% {
opacity: 0;
transform: translate(-50%, -50%) scale(0) rotate(270deg);
}
}
```
Each sparkle has a different `--tx` and `--ty` for its position.
But wait, the sparkle's transform includes both the position (translate) and the scale/rotate. If I use `translate(-50%, -50%)` as the base and then want to offset to a specific position, I need to combine them:
```css
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0);
```
This positions the sparkle at (--tx, --ty) relative to the center.
OK, let me make the sparkles have their respective positions in the keyframes. Each sparkle has a unique --tx and --ty.
For the sparkle animation:
```css
@keyframes sparkle-twinkle {
0% {
opacity: 0;
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0) rotate(0deg);
}
25% {
opacity: 1;
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(1.2) rotate(90deg);
}
50% {
opacity: 1;
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0.9) rotate(180deg);
}
75% {
opacity: 0.7;
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(1) rotate(270deg);
}
100% {
opacity: 0;
transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(0) rotate(360deg);
}
}
```
This is clean. Each sparkle twinkles at its position.
OK, I am now truly writing the code. This will be a long HTML file with extensive CSS. I'll organize it carefully.
Let me also reconsider the particle count. 12 particles is good. 6 burst hearts. 6 sparkles. 2 shockwaves. That's 26 child elements + the heart = 27 elements inside the label. That's a lot but manageable.
Actually, for brevity and clarity, I could reduce sparkles to 4. But 6 is fine. 12 particles + 6 hearts + 6 sparkles + 2 shockwaves = 26 decorative elements. Each one needs a line of HTML + inline styles. That's about 26 lines of HTML just for the decorative elements. Plus the CSS for each type of element.
I can reduce this by using CSS to style some elements rather than inline styles. For example, I can create CSS classes for each particle position instead of inline styles. But inline styles are actually cleaner for this use case since each element has unique values.
Actually, I can use nth-child selectors to assign positions. But that gets complicated. Inline styles are simpler and more readable for this.
OK, for the HTML I'll use inline styles for the particles, keeping each on one line.
Alright, here's the code. Let me write it all out. I'll aim for clean, well-organized code.
I'll also use smaller particle counts if the code gets too long. But 12 particles is a good number.
Hmm, actually, I realize using inline styles for the particles might make the HTML verbose. But it's the simplest approach. Let me do it.
For the 12 particles, I'll keep them in order around a circle with 30° spacing.
Here they are (angle: tx, ty at distance ~70px):
1. 0°: (70, 0) - delay 0.02s
2. 30°: (61, 35) - delay 0.04s
3. 60°: (35, 61) - delay 0.06s
4. 90°: (0, 70) - delay 0.08s
5. 120°: (-35, 61) - delay 0.10s
6. 150°: (-61, 35) - delay 0.12s
7. 180°: (-70, 0) - delay 0.14s
8. 210°: (-61, -35) - delay 0.16s
9. 240°: (-35, -61) - delay 0.18s
10. 270°: (0, -70) - delay 0.20s
11. 300°: (35, -61) - delay 0.22s
12. 330°: (61, -35) - delay 0.24s
Wait, delays from 0.02 to 0.24 might be too long. The total animation duration is 0.8s. Having delays up to 0.24s is fine - the particles burst in a wave.
Actually, for a more dramatic "burst" effect, I want most particles to fly out simultaneously with just a slight stagger. Let me use delays from 0s to 0.12s.
Adjusting delays (shorter):
1. 0s
2. 0.02s
3. 0.04s
4. 0.06s
5. 0.08s
6. 0.10s
7. 0.12s
8. 0.14s
9. 0.08s
10. 0.06s
11. 0.04s
12. 0.02s
This gives a nice wave effect - particles burst in order around the circle.
OK, I'm writing the code now. For real. Let me produce the complete file.
I'll also set the color of each particle to a different vibrant color:
1. #ff4d6d (coral pink)
2. #ff8c42 (orange)
3. #ffd60a (yellow)
4. #4dd4ac (teal)
5. #b56cff (purple)
6. #ff6b6b (light coral)
7. #f72585 (magenta)
8. #4ecdc4 (cyan)
9. #ffe66d (light yellow)
10. #ff85a1 (rose)
11. #a8e6cf (mint)
12. #778da9 (slate)
And for the burst hearts, color them all a warm pink/red. Font size around 18-20px.
For the sparkles, gold/white color, font size 14-18px.
OK, code time!
I should also note that I'm writing this as a self-contained HTML file. Everything inline. No external resources.
Let me be careful about one thing: the `calc(-50% + var(--tx))` syntax in the keyframes. In CSS, the `var()` function can be used inside `calc()`. Each particle has its own `--tx` and `--ty` values, so the keyframe will use those values. This should work in all modern browsers.
Actually, there might be a subtle issue: the keyframes are defined globally, but they reference `var(--tx)` and `var(--ty)`. These variables need to be defined on the element that's animating. Since each particle has its own inline styles with these variables, the animation will pick up the correct values from the element it's applied to.
This is a standard CSS custom properties technique and should work fine.
Let me write the code. I need to be very thorough to ensure everything is correct.
One more consideration: the heart's font. Using a system font or a generic serif for the ♥ character. The ♥ character (U+2665 BLACK HEART SUIT) might look a bit plain. The ❤️ (U+2764 with variation selector) is the emoji, which varies by platform. The ♥ (U+2665) is the text heart, which is more consistent.
Actually, I want the heart to look good. Let me use the ♥ character (U+2665) with a specific font family that renders it nicely. Most system fonts have this character. It'll render as a filled heart shape in most cases. But on some platforms it might be an outline.
Actually, let me use the ❤ character (U+2764, HEAVY BLACK HEART) without the emoji variation selector. This renders as a solid heart in most modern browsers. Or I can use an SVG heart for maximum control.
You know what, let me use an SVG heart. It's the most reliable and I can control the fill color perfectly. And it's still markup (not JavaScript), so it qualifies as "pure CSS/HTML".
Actually, the user said "pure CSS, no JavaScript". An SVG is fine since it's markup, not JavaScript. But maybe "pure CSS" means they want it done with CSS techniques. The checkbox hack is the key CSS technique here. The heart shape itself can be made with CSS or SVG or unicode.
Let me use a CSS-drawn heart. It's the most "pure CSS" approach. Two circles + a rotated square. All styled with CSS.
But animating a CSS heart's color requires changing the background of three elements (the square and two pseudo-elements). This is manageable:
```css
.heart,
.heart::before,
.heart::after {
background: #b0b0b0;
transition: background 0.3s;
}
#like-toggle:checked ~ .like-btn .heart,
#like-toggle:checked ~ .like-btn .heart::before,
#like-toggle:checked ~ .like-btn .heart::after {
background: #ff2d55;
}
```
The heart shape:
```css
.heart {
width: 56px;
height: 56px;
background: #b0b0b0;
transform: rotate(45deg);
position: relative;
margin: 0 auto;
}
.heart::before,
.heart::after {
content: '';
width: 56px;
height: 56px;
border-radius: 50%;
background: inherit;
position: absolute;
}
.heart::before {
top: -28px;
left: 0;
}
.heart::after {
top: 0;
left: -28px;
}
```
Wait, the heart shape uses `transform: rotate(45deg)`. If I also want to animate it with scale and bounce, I need to combine the rotation with the scale animations. This can be tricky since `transform` can only have one value at a time (well, multiple transform functions on the same property).
For a 45° rotated square, the overall heart shape is rotated 45°. The scale animation needs to be applied on top of this rotation. I can do:
```css
#like-toggle:checked ~ .like-btn .heart {
animation: heart-pop 0.6s ease-out forwards, heart-beat 1s 0.6s ease-in-out infinite;
}
```
And in the keyframes:
```css
@keyframes heart-pop {
0% { transform: rotate(45deg) scale(1); }
20% { transform: rotate(45deg) scale(1.6); }
...
100% { transform: rotate(45deg) scale(1); }
}
```
But the base heart has `transform: rotate(45deg)`. If the animation overrides transform, it needs to include the rotation in every keyframe. This is doable but tedious.
Alternatively, I can apply the rotation to a parent element and the scale to the heart:
```html
<div class="heart-rotate">
<div class="heart"></div>
</div>
```
The .heart-rotate handles the 45° rotation, and .heart handles the scale animations. This separates concerns and makes the animations cleaner.
But this adds another wrapper element. Let me do this if I use the CSS heart shape.
Actually, maybe simpler: use the ♥ character (U+2665) with a large font-size. It renders as a solid heart in most browsers (especially in modern Chrome). And I can animate it freely with transforms and color changes. The lack of total control over the shape is offset by the much simpler CSS.
I'll use the ♥ character. If it renders as an outline on some platforms, it still looksfine. And I can use `color` to control the fill.
Hmm, but ♥ (U+2665) usually renders as a text character, not a filled shape. In many fonts, it's the suit symbol (black heart). In some fonts it might be an outline.
For guaranteed filled heart, I can use ❤ (U+2764) or the emoji ❤️. But the emoji rendering varies.
OK, let me just use ❤ (U+2764, no variation selector). In Chrome, this renders as a filled red heart shape even without the emoji variation selector. I can then color it with CSS `color` property, and it should respect the color.
Actually, I just realized that the color property works on ❤ (U+2764) in most browsers. If I set `color: #b0b0b0`, the heart should be gray, and `color: #ff2d55` should make it red.
Hmm, but some browser/font combinations render ❤ as an emoji regardless of the variation selector, and emoji don't change color with the `color` property. They use the emoji skin tone mechanism instead.
To be safe, I'll use the CSS heart shape approach. It's more reliable. The wrapper for rotation is fine.
OR - I can just use the ♥ (U+2665) character, which is a text symbol and definitely respects the `color` property. It might look slightly different across platforms but it'll work. And I can set the font to something like Arial or Helvetica where ♥ renders reasonably.
You know what, let me just use the CSS heart. It's the most impressive and reliable. I'll add the rotation wrapper.
Structure:
```html
<label for="like-toggle" class="like-btn">
<span class="heart-wrapper">
<span class="heart"></span>
</span>
<!-- particles, etc. -->
</label>
```
.heart-wrapper is rotated 45°.
.heart is the square that, with its pseudo-element circles, forms the heart shape.
.heart gets the scale/pop/beat animations.
.heart-wrapper gets the 45° rotation.
Wait, the heart shape in CSS is: a square rotated 45° with two circles on top. The square is the rotated element. If I separate rotation from scaling, I have:
.heart-wrapper (no rotation): contains .heart and its pseudo-elements
.heart: square with two pseudo-element circles, rotated 45°
Actually the standard CSS heart is:
- One element rotated 45° (the square part)
- Two pseudo-elements as circles
The rotation is inherent to the heart shape. If I want to scale the heart without messing up the rotation, I need the scale on a parent, not on the heart itself.
So:
- .heart-rotate (parent): rotate(45deg)
- .heart (child): the square, no transform, but with the two circle pseudo-elements
- Scale animations apply to .heart-rotate or to a wrapper
Wait, I'm confusing myself. Let me think clearly:
Standard CSS heart:
```css
.heart {
width: 50px;
height: 50px;
background: red;
transform: rotate(45deg);
position: relative;
}
.heart::before,
.heart::after {
content: '';
position: absolute;
width: 50px;
height: 50px;
border-radius: 50%;
background: inherit;
}
.heart::before { top: -25px; left: 0; }
.heart::after { top: 0; left: -25px; }
```
The .heart element is rotated 45°, forming the pointy bottom of the heart. The two circles sit on top, forming the lobes. The end result is a heart shape.
If I want to scale this heart, I need to scale the entire assembled shape. But the rotation is on .heart itself. If I apply a scale animation to .heart, it would combine with the rotation:
```css
animation: pop 0.6s forwards;
@keyframes pop {
0% { transform: rotate(45deg) scale(1); }
50% { transform: rotate(45deg) scale(1.5); }
100% { transform: rotate(45deg) scale(1); }
}
```
This is fine. I just need to include the rotation in every keyframe. Not a big deal.
Actually, there's a simpler approach: use a wrapper for the rotation, and apply the scale animations to the wrapper:
```html
<span class="heart-wrap">
<span class="heart"></span>
</span>
```
```css
.heart-wrap {
transform: rotate(45deg);
}
.heart {
width: 50px;
height: 50px;
background: #b0b0b0;
position: relative;
}
.heart::before,
.heart::after {
content: '';
position: absolute;
width: 50px;
height: 50px;
border-radius: 50%;
background: inherit;
}
.heart::before { top: -25px; left: 0; }
.heart::after { top: 0; left: -25px; }
```
Then the scale animations apply to .heart-wrap (or .heart, but I need to be careful).
Hmm, but if the animation is on .heart-wrap, then .heart and its pseudo-elements scale together. The rotation is also on .heart-wrap. So:
```css
#like-toggle:checked ~ .like-btn .heart-wrap {
animation: heart-pop 0.6s ease-out forwards;
}
@keyframes heart-pop {
0% { transform: rotate(45deg) scale(1); }
20% { transform: rotate(45deg) scale(1.6); }
40% { transform: rotate(45deg) scale(0.85); }
60% { transform: rotate(45deg) scale(1.25); }
80% { transform: rotate(45deg) scale(0.95); }
100% { transform: rotate(45deg) scale(1); }
}
```
This works! The heart-wrap handles both rotation and scaling.
For the continuous beat:
```css
@keyframes heart-beat {
0%, 100% { transform: rotate(45deg) scale(1); }
15% { transform: rotate(45deg) scale(1.08); }
30% { transform: rotate(45deg) scale(1); }
45% { transform: rotate(45deg) scale(1.12); }
60% { transform: rotate(45deg) scale(1); }
}
```
And the idle pulse (unchecked):
```css
.heart-wrap {
animation: idle-pulse 2s ease-in-out infinite;
}
@keyframes idle-pulse {
0%, 100% { transform: rotate(45deg) scale(1); }
50% { transform: rotate(45deg) scale(1.08); }
}
```
This is all clean. The keyframes all include rotate(45deg), but that's only 5-6 keyframes per animation, and I have 3 animations. Totally manageable.
OK, I'll go with the CSS heart approach. It's more "pure CSS" and looks great.
The heart is centered in the label. The label is 120×120px. The heart is about 50×50px (roughly 70px diagonal). The particles start from the center of the label.
Actually, the heart-wrap is rotated 45°. The heart inside it is 50×50px, so the diagonal of the heart (from top lobe to bottom point) is about 70px. This is the visual size of the heart.
For centering, the heart-wrap needs to be positioned so the heart is centered in the label. Since the heart-wrap is rotated, the heart inside is 50×50px but visually rotated, so the bounding box is about 70×70px.
Let me set:
- Label: 120×120px, flexbox to center the heart
- Heart-wrap: 50×50px (rotated)
- The rotated heart has a visual bounding box of about 70×70px
I'll use flexbox on the label to center the heart-wrap, and the heart-wrap has fixed dimensions.
OK, I'm going to write the complete code now. This is the final version. No more planning.
Actually wait, one thing I need to figure out: the heart shape color. The .heart element has a background color. The pseudo-elements (.heart::before and .heart::after) use `background: inherit` to get the same color. When I change the color in the checked state, I need to change it on .heart, and the pseudo-elements will inherit.
So:
```css
.heart {
background: #b0b0b0;
transition: background 0.3s;
}
#like-toggle:checked ~ .like-btn .heart {
background: #ff2d55;
}
```
The pseudo-elements will inherit the new color automatically since they use `background: inherit`.
For the glow effect (text-shadow equivalent for a shaped element), I can use `box-shadow` on the .heart element. But box-shadow follows the box, not the shape. For a heart-shaped glow, I can use a drop-shadow filter on the heart-wrap:
```css
#like-toggle:checked ~ .like-btn .heart-wrap {
filter: drop-shadow(0 0 15px rgba(255, 45, 85, 0.6)) drop-shadow(0 0 30px rgba(255, 45, 85, 0.3));
}
```
This applies a glow to the heart shape. Combined with the animation, this would look great.
OK, final code time. Let me write it all.
Actually, I also want to add a small badge/number animation. When the count changes from 4.2k to 4.3k, the new number should bounce in. I'll use a separate span for each count state.
For the hint text, I want it to fade out when the heart is liked. It's positioned below the heart (or above?) and says "Tap the heart!".
Alright, here's the complete HTML. I'll organize it nicely.