Exploding like buttonOne-line
DeepSeek V4.1 Flash 1017
by DeepSeekReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 3 min 4 s
- Cost
- $0.011
- Output tokens
- 21,661
- Spent thinking
- 13,769 tokens
- Speed
- 117.8 tok/s
- Console errors
- 0
- First token after
- 315 ms
- First visible text after
- 2 min 7 s
- Prompt tokens
- 382
- Code size
- 19.7 KB
- Finish reason
- stop
- Served by
- Cohere
- Model id
deepseek/deepseek-v4.1-flash-1017- Reasoning sent
{"effort":"low"}- Max output
- 64,000 tokens
- Generated
- Nov 10, 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 · 626 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Like Button — Pure CSS</title>
<style>
:root{
--pink:#ff2d6f;
--pink2:#ff6b9d;
--gold:#ffd166;
--cyan:#5ee7ff;
--violet:#a78bfa;
--size: clamp(118px, 23vmin, 172px);
}
*{ box-sizing:border-box; }
html,body{ height:100%; }
body{
margin:0;
min-height:100vh;
background:#07040e;
color:#fff;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-font-smoothing:antialiased;
display:grid;
place-items:center;
overflow:hidden;
}
/* ---------- hidden checkbox driving everything ---------- */
.like-input{
position:fixed;
top:0; left:0;
width:1px; height:1px;
opacity:0;
margin:0;
pointer-events:none;
z-index:-1;
}
.stage{
position:fixed;
inset:0;
display:grid;
place-items:center;
overflow:hidden;
isolation:isolate;
}
/* ---------- ambient aurora ---------- */
.aurora{
position:absolute;
inset:-25%;
z-index:0;
filter: blur(70px);
opacity:.6;
pointer-events:none;
}
.aurora span{
position:absolute;
border-radius:50%;
display:block;
}
.aurora .b1{
width:48vmax; height:48vmax; left:2%; top:0%;
background: radial-gradient(circle, #ff2d6f 0%, rgba(255,45,111,0) 68%);
animation: drift1 19s ease-in-out infinite;
}
.aurora .b2{
width:44vmax; height:44vmax; right:0%; top:8%;
background: radial-gradient(circle, #7c3aed 0%, rgba(124,58,237,0) 68%);
animation: drift2 23s ease-in-out infinite;
}
.aurora .b3{
width:40vmax; height:40vmax; left:22%; bottom:-8%;
background: radial-gradient(circle, #06b6d4 0%, rgba(6,182,212,0) 68%);
animation: drift3 27s ease-in-out infinite;
}
@keyframes drift1{
0%,100%{ transform: translate(0,0) scale(1); }
50%{ transform: translate(9vmax,6vmax) scale(1.15); }
}
@keyframes drift2{
0%,100%{ transform: translate(0,0) scale(1.1); }
50%{ transform: translate(-8vmax,7vmax) scale(.92); }
}
@keyframes drift3{
0%,100%{ transform: translate(0,0) scale(.95); }
50%{ transform: translate(6vmax,-8vmax) scale(1.18); }
}
/* ---------- floating hearts ---------- */
.floaters{
position:absolute;
inset:0;
z-index:1;
pointer-events:none;
overflow:hidden;
}
.floaters span{
position:absolute;
left: var(--x);
bottom:-12vh;
font-size: var(--fs, 18px);
color: rgba(255,130,180,.55);
text-shadow: 0 0 18px rgba(255,45,111,.55);
opacity:0;
animation: floatUp var(--d) linear var(--dl) infinite;
will-change: transform, opacity;
}
@keyframes floatUp{
0% { opacity:0; transform: translate3d(0,0,0) scale(.55) rotate(0deg); }
12% { opacity:.8; }
85% { opacity:.15; }
100% { opacity:0; transform: translate3d(24px,-125vh,0) scale(1.15) rotate(45deg); }
}
/* ---------- card ---------- */
.card{
position:relative;
z-index:3;
display:flex;
flex-direction:column;
align-items:center;
gap: clamp(12px, 2.4vmin, 22px);
padding: clamp(20px, 3.6vmin, 36px) clamp(26px, 5.5vmin, 52px) clamp(18px, 3.2vmin, 32px);
border-radius: 34px;
background: linear-gradient(160deg, rgba(255,255,255,.10), rgba(255,255,255,.025));
border: 1px solid rgba(255,255,255,.13);
box-shadow:
0 34px 90px -34px rgba(0,0,0,.95),
inset 0 1px 0 rgba(255,255,255,.16);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
transition: box-shadow .45s ease, border-color .45s ease;
}
.tag{
font-size: clamp(9px, 1.35vmin, 11px);
letter-spacing: .22em;
font-weight: 700;
color: rgba(255,255,255,.45);
text-transform: uppercase;
padding: 5px 12px;
border-radius: 999px;
border: 1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.04);
white-space: nowrap;
}
/* ---------- the button ---------- */
.btn-wrap{
position:relative;
display:grid;
place-items:center;
}
.like-btn{
position:relative;
display:grid;
place-items:center;
width: var(--size);
height: var(--size);
border-radius:50%;
cursor:pointer;
background:
radial-gradient(circle at 32% 26%, rgba(255,255,255,.17), rgba(255,255,255,0) 62%),
rgba(255,255,255,.055);
border: 1.5px solid rgba(255,255,255,.18);
box-shadow:
0 20px 48px -20px rgba(255,45,111,.55),
inset 0 1px 0 rgba(255,255,255,.22);
transition:
transform .3s cubic-bezier(.34,1.56,.64,1),
background .45s ease,
border-color .45s ease,
box-shadow .45s ease;
-webkit-tap-highlight-color: transparent;
user-select:none;
touch-action: manipulation;
}
.like-btn:hover{ transform: scale(1.055); }
.like-btn:active{ transform: scale(.93); }
/* focus ring for keyboard users */
#like:focus-visible ~ .stage .like-btn{
box-shadow:
0 0 0 4px rgba(255,255,255,.4),
0 20px 48px -20px rgba(255,45,111,.55);
}
/* halo */
.halo{
position:absolute;
inset:-30%;
border-radius:50%;
background: radial-gradient(circle, rgba(255,45,111,.6), rgba(255,45,111,0) 66%);
filter: blur(20px);
opacity:.35;
pointer-events:none;
animation: haloPulse 3.4s ease-in-out infinite;
transition: opacity .45s ease;
}
@keyframes haloPulse{
0%,100%{ transform: scale(.94); }
50% { transform: scale(1.1); }
}
/* rings */
.rings{
position:absolute;
inset:0;
pointer-events:none;
}
.rings i{
position:absolute;
inset:0;
border-radius:50%;
border:3px solid var(--pink2);
opacity:0;
will-change: transform, opacity;
}
/* burst particles */
.burst{
position:absolute;
inset:0;
pointer-events:none;
}
.burst i{
position:absolute;
inset:0;
margin:auto;
width: var(--s, 9px);
height: var(--s, 9px);
border-radius:50%;
background: var(--c, #fff);
box-shadow: 0 0 12px var(--c, #fff);
opacity:0;
will-change: transform, opacity;
}
.burst i.sq{ border-radius:2px; }
/* heart */
.heart-holder{
position:relative;
width:52%;
height:52%;
display:grid;
place-items:center;
animation: idleBeat 3.4s ease-in-out infinite;
filter: drop-shadow(0 6px 18px rgba(255,45,111,.3));
transition: filter .45s ease;
pointer-events:none;
}
.heart{
width:100%;
height:100%;
display:block;
overflow:visible;
}
.heart path{
fill: url(#hg);
stroke: rgba(255,255,255,.42);
stroke-width:.55;
stroke-linejoin: round;
transition: stroke .45s ease;
}
.s1{ stop-color:#8b88a3; transition: stop-color .45s ease; }
.s2{ stop-color:#4b4863; transition: stop-color .45s ease; }
@keyframes idleBeat{
0%, 68%, 100% { transform: scale(1); }
76% { transform: scale(1.09); }
84% { transform: scale(1); }
91% { transform: scale(1.05); }
}
/* ---------- counter ---------- */
.count{
display:flex;
flex-direction:column;
align-items:center;
gap:2px;
line-height:1;
}
.num{
display:inline-grid;
overflow:hidden;
height:1.06em;
line-height:1.06;
font-size: clamp(26px, 5.2vmin, 42px);
font-weight:800;
letter-spacing:-.02em;
font-variant-numeric: tabular-nums;
color:#fff;
transition: color .4s ease, text-shadow .4s ease;
}
.num > span{
grid-area: 1 / 1;
transition: transform .5s cubic-bezier(.34,1.56,.64,1), opacity .3s ease;
}
.num .n1{ transform: translateY(115%); opacity:0; }
.clabel{
font-size: clamp(9px, 1.4vmin, 11px);
letter-spacing:.26em;
text-transform:uppercase;
font-weight:700;
color: rgba(255,255,255,.4);
transition: color .4s ease;
}
/* ---------- hint ---------- */
.hint{
position:relative;
display:grid;
place-items:center;
height:1.5em;
width:100%;
font-size: clamp(11px, 1.7vmin, 13px);
font-weight:600;
letter-spacing:.03em;
}
.hint .h{
grid-area:1 / 1;
white-space:nowrap;
transition: opacity .3s ease, transform .3s ease;
}
.hint .h0{ color: rgba(255,255,255,.55); }
.hint .h1{ color: var(--pink2); opacity:0; transform: translateY(8px); }
/* ---------- confetti + flash ---------- */
.confetti{
position:fixed;
inset:0;
z-index:8;
pointer-events:none;
overflow:hidden;
}
.confetti i{
position:absolute;
top:-10vh;
left: var(--x);
width:9px;
height:14px;
border-radius:2px;
background: var(--c);
opacity:0;
will-change: transform, opacity;
}
.flash{
position:absolute;
inset:0;
z-index:9;
pointer-events:none;
opacity:0;
background: radial-gradient(circle at 50% 50%,
rgba(255,255,255,.85) 0%,
rgba(255,120,170,.45) 26%,
rgba(255,45,111,0) 60%);
}
/* =========================================================
CHECKED STATE — the crazy part
========================================================= */
#like:checked ~ .stage .card{
border-color: rgba(255,90,150,.45);
box-shadow:
0 34px 100px -30px rgba(255,45,111,.85),
inset 0 1px 0 rgba(255,255,255,.22);
animation: cardShake .78s cubic-bezier(.36,.07,.19,.97);
}
@keyframes cardShake{
0% { transform: translate3d(0,0,0) rotate(0deg); }
12% { transform: translate3d(-9px,5px,0) rotate(-1.1deg); }
24% { transform: translate3d(8px,-6px,0) rotate(1deg); }
36% { transform: translate3d(-7px,-4px,0) rotate(-.8deg); }
48% { transform: translate3d(6px,5px,0) rotate(.7deg); }
62% { transform: translate3d(-4px,3px,0) rotate(-.45deg); }
78% { transform: translate3d(3px,-2px,0) rotate(.3deg); }
100% { transform: translate3d(0,0,0) rotate(0deg); }
}
#like:checked ~ .stage .like-btn{
background:
radial-gradient(circle at 32% 26%, rgba(255,255,255,.28), rgba(255,255,255,0) 62%),
radial-gradient(circle at 50% 60%, rgba(255,45,111,.55), rgba(255,45,111,.12) 70%);
border-color: rgba(255,140,180,.75);
box-shadow:
0 26px 64px -18px rgba(255,45,111,.95),
0 0 0 6px rgba(255,45,111,.09),
inset 0 1px 0 rgba(255,255,255,.35);
}
#like:checked ~ .stage .halo{
opacity:1;
animation: haloBoom .9s cubic-bezier(.2,.8,.3,1), haloPulse 3s 0.9s ease-in-out infinite;
}
@keyframes haloBoom{
0% { transform: scale(.5); opacity:.2; }
35% { transform: scale(1.9); opacity:1; }
100% { transform: scale(1); opacity:.9; }
}
#like:checked ~ .stage .heart-holder{
animation:
heartPop .88s cubic-bezier(.22,.9,.28,1) both,
heartbeat 1.7s 1s ease-in-out infinite;
filter: drop-shadow(0 10px 30px rgba(255,45,111,.9));
}
@keyframes heartPop{
0% { transform: scale(1) rotate(0deg); }
14% { transform: scale(.5) rotate(-10deg); }
34% { transform: scale(1.5) rotate(8deg); }
50% { transform: scale(.9) rotate(-5deg); }
66% { transform: scale(1.24) rotate(4deg); }
82% { transform: scale(.97) rotate(-1deg); }
100% { transform: scale(1) rotate(0deg); }
}
@keyframes heartbeat{
0%, 100% { transform: scale(1); }
12% { transform: scale(1.13); }
26% { transform: scale(1); }
40% { transform: scale(1.08); }
56% { transform: scale(1); }
}
#like:checked ~ .stage .s1{ stop-color:#ff9ab8; }
#like:checked ~ .stage .s2{ stop-color:#ff1a48; }
#like:checked ~ .stage .heart path{ stroke: rgba(255,220,232,.85); }
#like:checked ~ .stage .rings i{
animation: ringOut .95s cubic-bezier(.15,.7,.3,1) forwards;
}
#like:checked ~ .stage .rings i:nth-child(2){ animation-delay:.11s; }
#like:checked ~ .stage .rings i:nth-child(3){ animation-delay:.22s; }
@keyframes ringOut{
0% { transform: scale(.35); opacity:.95; border-width:6px; }
70% { opacity:.45; }
100% { transform: scale(2.9); opacity:0; border-width:0px; }
}
#like:checked ~ .stage .burst i{
animation: burst .95s cubic-bezier(.12,.72,.3,1) forwards;
}
@keyframes burst{
0% { opacity:0; transform: rotate(var(--a)) translateX(0) scale(.15); }
6% { opacity:1; }
52% { opacity:1; transform: rotate(var(--a)) translateX(calc(var(--size) * var(--k))) scale(1); }
100% { opacity:0; transform: rotate(var(--a)) translateX(calc(var(--size) * var(--k) * 1.55)) scale(.05); }
}
#like:checked ~ .stage .num .n0{ transform: translateY(-115%); opacity:0; }
#like:checked ~ .stage .num .n1{ transform: translateY(0); opacity:1; }
#like:checked ~ .stage .num{
color:#ff7aa8;
text-shadow: 0 0 26px rgba(255,45,111,.7);
}
#like:checked ~ .stage .clabel{ color: rgba(255,140,180,.85); }
#like:checked ~ .stage .count{
animation: countPop .62s cubic-bezier(.34,1.56,.64,1);
}
@keyframes countPop{
0% { transform: scale(1); }
30% { transform: scale(1.14); }
100% { transform: scale(1); }
}
#like:checked ~ .stage .hint .h0{ opacity:0; transform: translateY(-8px); }
#like:checked ~ .stage .hint .h1{ opacity:1; transform: translateY(0); }
#like:checked ~ .stage .flash{
animation: flashOut .6s ease-out;
}
@keyframes flashOut{
0% { opacity:0; }
10% { opacity:.8; }
100% { opacity:0; }
}
#like:checked ~ .stage .confetti i{
animation: fall var(--d) cubic-bezier(.3,.05,.65,1) var(--dl) forwards;
}
@keyframes fall{
0% { opacity:0; transform: translate3d(0,-12vh,0) rotate(0deg) scale(.5); }
12% { opacity:1; }
85% { opacity:1; }
100% { opacity:0; transform: translate3d(var(--dx), 116vh, 0) rotate(var(--r)) scale(1); }
}
@media (max-width: 420px){
.card{ border-radius:26px; }
.tag{ display:none; }
}
</style>
</head>
<body>
<input type="checkbox" id="like" class="like-input" aria-label="Like this">
<div class="stage">
<div class="aurora" aria-hidden="true">
<span class="b1"></span>
<span class="b2"></span>
<span class="b3"></span>
</div>
<div class="floaters" aria-hidden="true">
<span style="--x:6%; --d:16s; --dl:-2s; --fs:20px">♥</span>
<span style="--x:17%; --d:22s; --dl:-11s; --fs:14px">♥</span>
<span style="--x:28%; --d:19s; --dl:-16s; --fs:26px">♥</span>
<span style="--x:39%; --d:25s; --dl:-6s; --fs:16px">♥</span>
<span style="--x:50%; --d:17s; --dl:-19s; --fs:22px">♥</span>
<span style="--x:62%; --d:21s; --dl:-9s; --fs:13px">♥</span>
<span style="--x:73%; --d:24s; --dl:-14s; --fs:28px">♥</span>
<span style="--x:85%; --d:18s; --dl:-4s; --fs:18px">♥</span>
<span style="--x:93%; --d:23s; --dl:-21s; --fs:15px">♥</span>
<span style="--x:34%; --d:20s; --dl:-13s; --fs:12px">♥</span>
<span style="--x:67%; --d:26s; --dl:-1s; --fs:20px">♥</span>
<span style="--x:11%; --d:27s; --dl:-17s; --fs:17px">♥</span>
</div>
<div class="card">
<div class="tag">Pure CSS · No JavaScript</div>
<div class="btn-wrap">
<label class="like-btn" for="like">
<span class="halo"></span>
<span class="rings" aria-hidden="true">
<i></i><i></i><i></i>
</span>
<span class="burst" aria-hidden="true">
<i style="--a:0deg; --k:.62; --c:#ff2d6f; --s:11px"></i>
<i class="sq" style="--a:18deg; --k:.85; --c:#ffd166; --s:8px"></i>
<i style="--a:36deg; --k:.55; --c:#5ee7ff; --s:10px"></i>
<i class="sq" style="--a:54deg; --k:.92; --c:#ff6b9d; --s:7px"></i>
<i style="--a:72deg; --k:.68; --c:#a78bfa; --s:12px"></i>
<i style="--a:90deg; --k:.80; --c:#ff2d6f; --s:9px"></i>
<i class="sq" style="--a:108deg; --k:.58; --c:#ffd166; --s:11px"></i>
<i style="--a:126deg; --k:.88; --c:#5ee7ff; --s:7px"></i>
<i class="sq" style="--a:144deg; --k:.64; --c:#ff6b9d; --s:10px"></i>
<i style="--a:162deg; --k:.95; --c:#a78bfa; --s:8px"></i>
<i style="--a:180deg; --k:.60; --c:#ff2d6f; --s:12px"></i>
<i class="sq" style="--a:198deg; --k:.82; --c:#ffd166; --s:7px"></i>
<i style="--a:216deg; --k:.57; --c:#5ee7ff; --s:10px"></i>
<i class="sq" style="--a:234deg; --k:.90; --c:#ff6b9d; --s:9px"></i>
<i style="--a:252deg; --k:.70; --c:#a78bfa; --s:11px"></i>
<i class="sq" style="--a:270deg; --k:.86; --c:#ff2d6f; --s:8px"></i>
<i style="--a:288deg; --k:.56; --c:#ffd166; --s:12px"></i>
<i class="sq" style="--a:306deg; --k:.84; --c:#5ee7ff; --s:7px"></i>
<i style="--a:324deg; --k:.66; --c:#ff6b9d; --s:10px"></i>
<i class="sq" style="--a:342deg; --k:.94; --c:#a78bfa; --s:9px"></i>
</span>
<span class="heart-holder">
<svg class="heart" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<defs>
<linearGradient id="hg" x1="0" y1="0" x2="0.35" y2="1">
<stop class="s1" offset="0%"/>
<stop class="s2" offset="100%"/>
</linearGradient>
</defs>
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
</svg>
</span>
</label>
</div>
<div class="count">
<div class="num">
<span class="n0">12,408</span>
<span class="n1">12,409</span>
</div>
<div class="clabel">likes</div>
</div>
<div class="hint">
<span class="h h0">Click the heart</span>
<span class="h h1">Liked! Click again</span>
</div>
</div>
<div class="confetti" aria-hidden="true">
<i style="--x:3%; --c:#ff2d6f; --d:1.5s; --dl:.02s; --r:520deg; --dx:40px"></i>
<i style="--x:9%; --c:#ffd166; --d:1.9s; --dl:.14s; --r:-460deg; --dx:-30px"></i>
<i style="--x:15%; --c:#5ee7ff; --d:1.6s; --dl:.06s; --r:600deg; --dx:55px"></i>
<i style="--x:21%; --c:#a78bfa; --d:2.1s; --dl:.22s; --r:-380deg; --dx:-45px"></i>
<i style="--x:27%; --c:#ff6b9d; --d:1.7s; --dl:.1s; --r:480deg; --dx:20px"></i>
<i style="--x:33%; --c:#ff2d6f; --d:2.0s; --dl:.18s; --r:-560deg; --dx:-60px"></i>
<i style="--x:39%; --c:#ffd166; --d:1.55s;--dl:.04s; --r:440deg; --dx:35px"></i>
<i style="--x:45%; --c:#5ee7ff; --d:1.85s;--dl:.12s; --r:-500deg; --dx:-25px"></i>
<i style="--x:51%; --c:#a78bfa; --d:1.65s;--dl:.08s; --r:640deg; --dx:50px"></i>
<i style="--x:57%; --c:#ff6b9d; --d:2.05s;--dl:.2s; --r:-420deg; --dx:-40px"></i>
<i style="--x:63%; --c:#ff2d6f; --d:1.75s;--dl:.03s; --r:560deg; --dx:30px"></i>
<i style="--x:69%; --c:#ffd166; --d:1.95s;--dl:.16s; --r:-600deg; --dx:-55px"></i>
<i style="--x:75%; --c:#5ee7ff; --d:1.6s; --dl:.09s; --r:460deg; --dx:45px"></i>
<i style="--x:81%; --c:#a78bfa; --d:2.15s;--dl:.24s; --r:-480deg; --dx:-35px"></i>
<i style="--x:87%; --c:#ff6b9d; --d:1.7s; --dl:.05s; --r:580deg; --dx:25px"></i>
<i style="--x:93%; --c:#ff2d6f; --d:1.9s; --dl:.15s; --r:-520deg; --dx:-50px"></i>
<i style="--x:6%; --c:#5ee7ff; --d:2.2s; --dl:.28s; --r:400deg; --dx:60px"></i>
<i style="--x:18%; --c:#ffd166; --d:1.5s; --dl:.11s; --r:-640deg; --dx:-20px"></i>
<i style="--x:30%; --c:#ff2d6f; --d:1.8s; --dl:.19s; --r:500deg; --dx:38px"></i>
<i style="--x:42%; --c:#a78bfa; --d:2.0s; --dl:.07s; --r:-440deg; --dx:-48px"></i>
<i style="--x:54%; --c:#ff6b9d; --d:1.65s;--dl:.25s; --r:620deg; --dx:28px"></i>
<i style="--x:66%; --c:#5ee7ff; --d:1.85s;--dl:.13s; --r:-560deg; --dx:-32px"></i>
<i style="--x:78%; --c:#ffd166; --d:2.1s; --dl:.01s; --r:480deg; --dx:52px"></i>
<i style="--x:90%; --c:#ff2d6f; --d:1.7s; --dl:.21s; --r:-500deg; --dx:-42px"></i>
</div>
<div class="flash" aria-hidden="true"></div>
</div>
</body>
</html>Reasoning trace (13,769 tokens)
The task: a "like" button in pure CSS, no JavaScript, with a crazy animation on click. But we need it to be interesting, visible on first paint, and ideally self-demoing? The instruction about games doesn't apply. But we could add a CSS-only auto-demo? Pure CSS no JS. We can use a checkbox/radio hack for the toggle.
Let's design something impressive: a big heart button. On click (checkbox checked), the heart pops with a burst of particles, confetti, rings, floating hearts, screen shake, sparkles, text "LIKED!" appears, counter increments.
Pure CSS constraints: we can use a checkbox input with label. Use :checked ~ sibling selectors.
Ideas for "crazy":
- Heart scales up with elastic bounce
- Radial burst of ~24 particles (using ::before/::after on many divs, each rotated with transform)
- Ripple rings
- Confetti pieces falling
- Screen shake
- Background gradient hue rotate
- Counter increments 0 → 1 (or a fake counter going up using steps animation with content? Can't change text easily... can use CSS counters with a hidden element? Actually we can display numbers using multiple spans toggled by :checked).
Let's do: like count shows "1,234" normally, and on checked shows "1,235" with a pop. Use two spans stacked, opacity toggled.
Also a "crazy animation" — maybe make it re-triggerable: when unchecking, no animation. But a checkbox only animates once per toggle... Actually animation on :checked will re-run when re-checked since the animation restarts? When unchecking, animation is removed, so re-checking restarts it. Yes, that works — animations defined on `:checked` state run each time it's checked.
Better: use radio buttons with a "reset" hidden? Nah, checkbox is fine. Add a hint "click again to unlike".
Let's make it flashy:
Layout: dark background with animated gradient. Centered card. Big heart in a circle button. Around it, particles.
Let me write the structure:
```html
<div class="scene">
<input type="checkbox" id="like" class="like-input">
<label for="like" class="like-btn">
<span class="heart-wrap">
<svg class="heart" viewBox="0 0 32 29">...</svg>
</span>
<span class="rings">...</span>
<span class="particles">... 24 spans ...</span>
<span class="label">LIKE</span>
</label>
...
</div>
```
Wait — the input must be a sibling preceding the elements it controls. Use `#like:checked ~ .scene` etc. Let's put input at top level, then the whole UI as siblings.
Structure:
```html
<input type="checkbox" id="like" class="like-input">
<main class="stage">
<div class="card">
<div class="counter">...</div>
<div class="button-area">
<label for="like" class="like-btn">...</label>
... burst stuff inside label? particles need to be positioned relative to button. Put them inside the label as absolutely positioned children — that's fine, they're descendants of label and controlled by `#like:checked ~ .stage .particle`.
</div>
<div class="hint">Click the heart</div>
</div>
</main>
<div class="confetti">... pieces ...</div>
```
Selectors: `#like:checked ~ .stage .heart { animation: ... }`.
Let's make particles: 20 spans each with `--a: angle` inline style via CSS nth-child or inline style attributes. Inline style with custom property is allowed (it's inline CSS, not external). Use `style="--i:0"` and compute angle via calc: `transform: rotate(calc(var(--i) * 15deg)) translateY(...)`. Hmm, for the burst we want each particle to fly outward in its direction.
Approach: each particle is absolutely positioned at center, with `transform: rotate(var(--a)) translateX(0)` at rest, and animation moves translateX to a distance. Using keyframes with `rotate(var(--a)) translateX(...)` works since var is resolved per element? Custom properties in keyframes are resolved against the element, yes it works in Chrome.
But keyframes with var() — the var is looked up on the element the animation applies to. Yes, that works.
So:
```css
@keyframes burst {
0% { transform: rotate(var(--a)) translateX(0) scale(0); opacity: 0; }
10% { opacity: 1; }
60% { transform: rotate(var(--a)) translateX(var(--d)) scale(1); opacity: 1; }
100% { transform: rotate(var(--a)) translateX(calc(var(--d) * 1.3)) scale(0.2); opacity: 0; }
}
```
Particles of different shapes: circles, squares, little hearts. Use background colors via --c.
Also rings: two/three spans that scale up and fade.
Heart itself: SVG with gradient fill, on check it does a squash-stretch pop with a "thump" and then a heartbeat pulse loop? "Crazy animation" — let's do:
- heart scale bounce with cubic-bezier overshoot
- then continuous pulse (heartbeat) while liked
- the button circle fills with pink gradient
- shake the whole card
- background hue rotation
- floating "LIKED!" text pop up
- counter increments
- confetti falls from top of screen
Let me also add a big flash/white burst.
Also handle un-check: return to normal (transitions, not animations, so uncheck is smooth).
Since the demo needs to be visible on first paint — a big pretty heart button on a nice background, with a hint. But nothing animates until clicked. That's a bit dull for the screenshot at 3 seconds. Hmm. "Something meaningful must be visible on first paint" — a nice-looking like button is meaningful. But to make it better, we could add an ambient floating hearts background always animating, plus the button has a subtle idle pulse.
Could we auto-demo? Without JS, we could use CSS animation to... no, can't check a checkbox. Unless we use a `<label>` with `autofocus`? No.
Alternative: We could add an always-running demo of the burst in a separate "preview" area? That could be confusing. Better: make the idle state alive — background gradient animating, floating hearts drifting up, the heart gently beating, glow pulsing. That's plenty for first paint.
Hmm, but the "wow" is on click. The screenshot at 3s won't show the click. Maybe add a "PREVIEW" self-running ghost demo... Actually there's a trick: use CSS animation with a very long delay on the demo layer that runs the burst animation once at ~1s, independent of the checkbox, as a "teaser". But then it wouldn't be triggered by click.
Alternative neat trick: use `:checked` for real interaction AND an infinite ambient version of the burst that runs on a loop at low opacity? Meh.
I think I could add an auto-playing "demo" that repeatedly shows a mini version of the burst behind, but that would look weird when clicked.
Let me instead consider: the burst animation is defined via a class `.burst` that gets `animation` on `:checked`. I can additionally give the whole page an "attract mode" where a small loop animation of the heart thumping plays, plus decorative floating hearts. Good enough. And the instruction says "Games must start in a self-playing demo mode" — this isn't a game. Fine.
Hmm, but comparing side by side with other models — the static screenshot matters a lot. So make the idle state gorgeous: dark purple/indigo gradient with animated aurora blobs, glowing heart with pulsing halo, floating hearts, and a nice label.
Actually — here's a thought: I can make the demo auto-trigger using a CSS-only "timer" via animation-delay on a hidden element that... no, CSS can't change checkbox state.
OK. Let's also add keyboard support: the checkbox is focusable; pressing Space/Enter toggles it (native). Add a focus ring style. And a "Click or press space" hint — though keyboard events go to the iframe only after click, which is fine.
Now let's build it carefully.
HTML:
```html
<input type="checkbox" id="like" class="like-input" aria-label="Like">
<div class="stage">
<div class="aurora"></div> (3 blobs)
<div class="floaters"> (a few heart spans) </div>
<div class="card">
<div class="counter">
<span class="num num-a">12,408</span>
<span class="num num-b">12,409</span>
<span class="lbl">likes</span>
</div>
<div class="btn-wrap">
<label for="like" class="like-btn">
<span class="halo"></span>
<span class="rings">3 spans</span>
<span class="burst"> 18 spans </span>
<span class="heart-holder">
<svg .../>
</span>
</label>
<div class="shockwave"></div> (maybe inside label)
</div>
<div class="hint">
<span class="hint-on">Click the heart</span>
<span class="hint-off">Liked! Click to undo</span>
</div>
</div>
<div class="confetti"> pieces </div>
<div class="flash"></div>
</div>
```
Wait: the confetti should fall over the whole screen. Put it in .stage with position fixed/absolute covering viewport.
The counter: two numbers stacked; on check, num-a slides up and fades, num-b comes up. Or simpler: both absolutely positioned, translateY.
Let's make the counter big and prominent, since it's the "like" thing. Actually let's put the counter below the heart.
Design:
- Card: rounded rect, glassy, with border, backdrop blur. Inside: heart button, counter, hint.
- Background: deep navy/indigo with aurora blobs.
Let me write CSS.
```css
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
background: #0b0716;
color: #fff;
overflow: hidden; /* no scrollbars; but content should fit */
display: grid;
place-items: center;
}
```
Hmm, overflow hidden on body could clip on small screens. Use `overflow-x: hidden; overflow-y: auto;` Actually with place-items center and small height, better: `min-height: 100vh; display: grid; place-items: center; overflow: hidden;` and make the card scale down with clamp. Let's use responsive sizes with clamp/vmin.
Let me size the heart button: `width: clamp(120px, 26vmin, 190px)`.
Stage: fixed inset 0, overflow hidden.
Aurora blobs: big radial-gradient circles with filter blur, animated with slow drift + hue rotation.
Floating hearts: absolutely positioned spans with heart shape (using CSS clip-path or ::before/::after rotated squares — simpler: use a unicode "♥"? Text is fine, no external font. Use ♥ with color). Floating up with opacity fade, various delays. Good, cheap.
Now the button.
```css
.like-btn {
position: relative;
width: var(--size);
aspect-ratio: 1;
border-radius: 50%;
display: grid;
place-items: center;
cursor: pointer;
background: radial-gradient(circle at 30% 25%, rgba(255,255,255,.14), rgba(255,255,255,.03) 60%), rgba(255,255,255,.05);
border: 1.5px solid rgba(255,255,255,.16);
box-shadow: 0 20px 50px -20px rgba(255,60,120,.5), inset 0 1px 0 rgba(255,255,255,.2);
transition: transform .25s cubic-bezier(.34,1.56,.64,1), background .35s, border-color .35s, box-shadow .35s;
-webkit-tap-highlight-color: transparent;
user-select: none;
}
.like-btn:hover { transform: scale(1.05); }
.like-btn:active { transform: scale(.94); }
```
Careful: `.like-btn:hover` transform vs animation on the wrap. Animations on the label itself would conflict with hover transform. Better: apply the pop animation to an inner element, keep label transform for hover.
Actually, put the shake/pop animation on `.btn-wrap` or on `.heart-holder`. Let's animate `.heart-holder` (contains svg) with the heart pop, and animate `.card` with a shake.
Heart SVG: path for a heart. Use viewBox "0 0 24 24" with path "M12 21s-7.5-4.7-9.5-9.2C1 8.2 3.2 5 6.5 5c2 0 3.6 1.1 4.5 2.7C11.9 6.1 13.5 5 15.5 5c3.3 0 5.5 3.2 4 6.8C19.5 16.3 12 21 12 21z" — hmm let me use a standard heart path.
Standard Material heart: "M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" — that's the classic. Good, viewBox 0 0 24 24.
Idle heart: stroke white / fill rgba(255,255,255,0.12)? Let's make it a filled heart with a subtle gradient that's greyish when unliked, and vivid pink/red gradient when liked. Use two paths stacked: base (outline) and fill (opacity 0 → 1). Simpler: one path with fill transition.
Use `fill: url(#grad)` — gradient defined in the SVG with stops that we can't easily change from CSS... Actually we can: `stop { stop-color: ... }` via CSS? CSS can target `stop` elements? SVG stops accept `stop-color` as a presentation attribute → CSS property. Yes, `stop-color` is a CSS property. So `.grad-stop-1 { stop-color: #6b7280; transition: stop-color .4s; }` and `#like:checked ~ .stage .grad-stop-1 { stop-color: #ff2d6f; }`. Hmm, but the SVG is inside the label inside .stage — descendant selector works.
Alternatively simpler: heart path fill with a CSS gradient via `background` on a div with clip-path. Let's do the clip-path heart approach? Nah, SVG is cleaner and crisp.
Actually simplest robust: two SVG paths — an outline path (stroke) and a fill path. Or one path with `fill: currentColor` and color transitions. Let's do: `.heart { fill: rgba(255,255,255,.1); stroke: rgba(255,255,255,.55); stroke-width: 1.4; transition: ... }` and checked → `fill: #ff2b62; stroke: #ff7aa2;`. Then add a drop-shadow glow filter. Clean and reliable. Maybe add an inner highlight via a second small ellipse with white opacity.
Hmm, a gradient fill looks much nicer. Let's try the SVG gradient with CSS-controlled stops. Risk: low. Chrome supports `stop-color` in CSS. I'll do it but with a fallback fill color too.
Actually, let me simplify: use `fill: url(#heartGrad)` where the gradient goes from #ff4d8d to #ff1e56 when checked, and both stops are `#ffffff` with opacity when unchecked? Can't change opacity of stop easily... `stop-opacity` is also CSS. OK:
```css
.heart-grad .s1 { stop-color: #ff9a9e; } /* unchecked: muted */
```
Unchecked: stops #6d6a86 → #8a86a8 (grayish purple), checked: #ff5f9e → #ff1f4b. With transition on stop-color .45s. Should work in Chrome. Add fallback: if it fails, fill defaults... `fill: url(#g)` failing would render black. Hmm. Let's add `fill: #ff3b6b` first then `fill: url(#heartGrad)` — if the url fails, the fill stays? No, the later declaration wins regardless. If the gradient is broken, fill becomes none/black. But the gradient won't be broken — it's static SVG. Only stop colors change. Fine.
Let's just do it.
Now the burst particles.
Inside `.like-btn`, add `<span class="burst">` containing 18 `<i>` elements. Position `.burst` at center with `position:absolute; inset:0; display:grid; place-items:center; pointer-events:none;`.
Each `<i>`: `position:absolute; width:10px; height:10px; border-radius:50%; background: var(--c); opacity:0;` and transform origin center.
Keyframes:
```css
@keyframes burst {
0% { transform: rotate(var(--a)) translateX(0) scale(.3); opacity: 0; }
8% { opacity: 1; }
55% { transform: rotate(var(--a)) translateX(var(--d)) scale(1); opacity: 1; }
100% { transform: rotate(var(--a)) translateX(calc(var(--d) * 1.45)) scale(.1); opacity: 0; }
}
```
With `--d` in px relative to button size. Use `--d: calc(var(--size) * .62)` etc. But `var(--size)` defined on .like-btn — children inherit. Good. Vary per particle with nth-child or inline style.
I'll write inline styles: `style="--a:0deg; --d:70px; --c:#ff3b6b"`. Hmm, `--d` should be relative to button size for responsiveness. Use `--k` multiplier: `--d: calc(var(--size) * var(--k))` where `--k` is like .55. Set `--k` inline per particle.
Let's generate 20 particles with angles 0..360 step 18, with varied distances and colors and shapes.
I'll write them out manually. 20 items:
```html
<i style="--a:0deg;--k:.62;--c:#ff3b6b"></i>
<i style="--a:18deg;--k:.48;--c:#ffd166"></i>
...
```
Shapes: alternate border-radius 50% / 2px / and heart-ish. Add classes: `.p-dot`, `.p-sq`, `.p-star`. Keep simple: circles and squares and a couple of tiny hearts using ♥ text? Let's just use circles and squares with varied sizes.
Also add "rings": 3 spans that expand from center and fade.
```css
@keyframes ring {
0% { transform: scale(.2); opacity: .9; border-width: 6px; }
100% { transform: scale(2.6); opacity: 0; border-width: 0px; }
}
```
Rings positioned absolutely with inset 0, border-radius 50%, border: 3px solid #ff6b9d.
Now the heart pop:
```css
@keyframes heartPop {
0% { transform: scale(1); }
15% { transform: scale(.55) rotate(-8deg); }
35% { transform: scale(1.45) rotate(6deg); }
50% { transform: scale(.92) rotate(-4deg); }
65% { transform: scale(1.2) rotate(3deg); }
80% { transform: scale(.98); }
100% { transform: scale(1); }
}
```
Then a continuous heartbeat after: `.heart-holder` animation: heartPop .9s cubic-bezier(.2,.8,.3,1), then heartbeat infinite 1.4s .9s? Multiple animations with delays — fine. But on uncheck the animation is removed, so heart returns to normal. Good.
Actually continuous heartbeat while liked might be nice. Let's do:
`animation: heartPop .85s cubic-bezier(.25,.9,.3,1) both, heartbeat 1.6s 1s infinite;`
heartbeat keyframes: 0%,100% scale(1); 14% scale(1.12); 28% scale(1); 42% scale(1.08); 56% scale(1).
Hmm but `both` fill on first animation would hold final scale(1) — fine.
Card shake:
```css
@keyframes shake {
0% { transform: translate(0,0) rotate(0); }
20% { transform: translate(-6px, 4px) rotate(-1deg); }
...
}
```
Careful: card shake with `transform` may conflict with nothing else. OK.
Screen flash: a full-screen radial gradient white/pink that flashes 0 → .55 → 0 opacity quickly.
Confetti: 30 pieces falling from top with rotation. Only visible briefly after click. Use `position:fixed; inset:0; pointer-events:none; overflow:hidden;`.
Each piece: `position:absolute; top:-10vh; left: var(--x); width:8px; height:14px; background: var(--c); animation: fall 1.6s ...` with rotation.
```css
@keyframes fall {
0% { transform: translateY(-20vh) rotate(0deg); opacity: 1; }
100% { transform: translateY(110vh) rotate(720deg); opacity: .9; }
}
```
Use animation-duration varying per piece and delay small.
Hmm, confetti falls from top which may take time to appear. Better: burst confetti from the button outward and then fall. Simplify: confetti spawns at center-ish and explodes outward with gravity... complexity. Let's just do top-falling confetti with staggered delays 0–0.4s and durations 1.2–2s. Should be visible quickly.
Alternatively, add a second confetti group that bursts from the button. Keep it simple — the particle burst already covers that.
Counter animation:
```html
<div class="count">
<span class="n n0">12,408</span>
<span class="n n1">12,409</span>
</div>
```
CSS: `.count { position: relative; height: 1em; overflow: hidden; }` — hmm, with two absolutely positioned spans stacked. `.n1 { transform: translateY(100%); opacity: 0; }` checked → `.n0 { translateY(-100%); opacity: 0 }`, `.n1 { translateY(0); opacity: 1 }` with transitions.
Font: use tabular numbers, big bold.
Also "LIKED!" text appearing? The hint text switches. Good enough.
Let's also add a "like" word under the heart inside the button? No, keep heart only.
Let me also add an idle heartbeat to the heart when unchecked — a gentle pulse every 3s. That gives motion at first paint. Yes!
```css
.heart-holder { animation: idleBeat 3.2s ease-in-out infinite; }
@keyframes idleBeat { 0%,70%,100% { transform: scale(1);} 78% { transform: scale(1.09);} 86% { transform: scale(1);} 92% { transform: scale(1.05);} }
```
And when checked, the checked rule overrides `animation` with the pop+heartbeat. Since checked rule has higher specificity (id + class), it wins. Good.
Halo: a blurred radial glow behind the button, pulsing.
Let's write it all out now.
Colors palette:
- bg: #0a0614 → #150b24
- pink: #ff2d6f, #ff6b9d, #ffb3c9
- accent: #ffd166 (gold), #6ee7ff (cyan), #a78bfa (purple), #4ade80?
Aurora blobs: pink #ff2d6f, purple #7c3aed, cyan #06b6d4, low opacity, blurred 80px, animated drift.
Let me write the HTML.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Like Button — Pure CSS</title>
<style>...</style>
</head>
<body>
<input type="checkbox" id="like" class="like-input">
<div class="stage">
<div class="aurora"><span class="b b1"></span><span class="b b2"></span><span class="b b3"></span></div>
<div class="floaters" aria-hidden="true">
<span>♥</span> ... x8
</div>
<div class="card">
<div class="btn-wrap">
<label class="like-btn" for="like">
<span class="halo"></span>
<span class="rings"><i></i><i></i><i></i></span>
<span class="burst">
<i class="p" style="..."></i> x20
</span>
<span class="heart-holder">
<svg viewBox="0 0 24 24" class="heart" aria-hidden="true">
<defs>
<linearGradient id="hg" x1="0" y1="0" x2="0" y2="1">
<stop class="s1" offset="0%"/>
<stop class="s2" offset="100%"/>
</linearGradient>
</defs>
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
</svg>
</span>
</label>
</div>
<div class="count">
<span class="n n0">12,408</span>
<span class="n n1">12,409</span>
<span class="clabel">likes</span>
</div>
<div class="hint">
<span class="h h0">Click the heart</span>
<span class="h h1">Liked! Click again</span>
</div>
</div>
<div class="confetti" aria-hidden="true"> ... </div>
<div class="flash"></div>
</div>
</body>
</html>
```
Note: `.card` needs to be a sibling descendant of `.stage` so `#like:checked ~ .stage ...` works. Yes.
Wait: input is before .stage, so `#like:checked ~ .stage .xxx` works.
The `count` layout: I want "12,408" big with "likes" small below. Let's structure:
```html
<div class="count">
<div class="num">
<span class="n n0">12,408</span>
<span class="n n1">12,409</span>
</div>
<div class="clabel">likes</div>
</div>
```
`.num { position: relative; height: 1.1em; width: 4ch? }` Two absolutely positioned spans need a defined width — use `display:inline-grid` with both spans in the same grid cell (grid-area: 1/1). That auto-sizes to the widest.
```css
.num { display: inline-grid; }
.num > span { grid-area: 1 / 1; }
```
Nice. Then transitions on transform/opacity. But `overflow: hidden` on .num with 1.1em height to make the slide look like a roll. Use `.num { overflow: hidden; height: 1.15em; line-height: 1.15em; }` and the spans translate up/down.
Hmm, if line-height equals height it works.
Let's write CSS now in full.
```css
:root{
--pink:#ff2d6f;
--pink2:#ff6b9d;
--gold:#ffd166;
--cyan:#5ee7ff;
--violet:#a78bfa;
--size: clamp(116px, 22vmin, 168px);
}
```
Careful with vmin on mobile — 22vmin at 375x800 → 22*3.75 = 82px. clamp min 116 → 116px. Fine.
Body:
```css
body{
margin:0;
min-height:100vh;
background:#08050f;
color:#fff;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
display:grid;
place-items:center;
overflow:hidden;
}
```
`overflow:hidden` on body prevents scrollbars. But at 375px width the card must fit. Card padding, size clamp — should fit.
Stage:
```css
.stage{
position:fixed;
inset:0;
display:grid;
place-items:center;
overflow:hidden;
isolation:isolate;
}
```
Aurora blobs:
```css
.aurora{ position:absolute; inset:-20%; filter: blur(60px); opacity:.55; z-index:-1; }
.b{ position:absolute; border-radius:50%; }
.b1{ width:46vmax; height:46vmax; left:5%; top:5%; background: radial-gradient(circle, #ff2d6f, transparent 70%); animation: drift1 18s ease-in-out infinite; }
...
```
Since .stage has isolation:isolate and z-index:-1 on aurora, it will be behind but still within the stage's stacking context — fine since the stage background is on body. Actually z-index:-1 within isolated stacking context places it behind other stage children but not behind the stage background (stage has no background). Good.
Card:
```css
.card{
position:relative;
display:flex;
flex-direction:column;
align-items:center;
gap: clamp(14px, 2.5vmin, 24px);
padding: clamp(22px, 4vmin, 40px) clamp(28px, 6vmin, 56px);
border-radius: 32px;
background: linear-gradient(160deg, rgba(255,255,255,.09), rgba(255,255,255,.03));
border: 1px solid rgba(255,255,255,.12);
box-shadow: 0 30px 80px -30px rgba(0,0,0,.9), inset 0 1px 0 rgba(255,255,255,.15);
backdrop-filter: blur(14px);
transition: box-shadow .4s, border-color .4s;
}
```
Card shake on checked: `animation: cardShake .7s ease-out;` But careful — a `transition` + `animation` on the same element for different properties is fine (animation on transform, transition on box-shadow). OK.
Hmm, shake the card may cause the whole layout to jitter — that's the point ("crazy").
Actually maybe shake the whole stage instead? Card shake is fine.
Also on checked, card border becomes pink and glow.
Now `.like-btn`:
```css
.like-btn{
position:relative;
display:grid;
place-items:center;
width:var(--size);
height:var(--size);
border-radius:50%;
cursor:pointer;
background:
radial-gradient(circle at 32% 26%, rgba(255,255,255,.16), rgba(255,255,255,0) 62%),
rgba(255,255,255,.06);
border:1.5px solid rgba(255,255,255,.18);
box-shadow:
0 18px 44px -18px rgba(255,45,111,.55),
inset 0 1px 0 rgba(255,255,255,.22);
transition: transform .3s cubic-bezier(.34,1.56,.64,1), background .45s, border-color .45s, box-shadow .45s;
-webkit-tap-highlight-color:transparent;
user-select:none;
touch-action: manipulation;
}
```
Hover: `transform: scale(1.06)`. Active: `scale(.93)`. On checked, the button background becomes a pink radial.
But careful: the `:active` state during click then the checked animation... fine.
Heart-holder:
```css
.heart-holder{
position:relative;
display:grid;
place-items:center;
width:52%;
height:52%;
animation: idleBeat 3.4s ease-in-out infinite;
filter: drop-shadow(0 6px 18px rgba(255,45,111,.35));
transition: filter .4s;
}
.heart{ width:100%; height:100%; display:block; }
.heart path{ fill:url(#hg); stroke: rgba(255,255,255,.5); stroke-width:.8; transition: stroke .45s; }
```
Hmm, stroke on the heart path with the classic path — stroke-width 0.8 in a 24-unit viewBox scaled to ~80px means ~2.7px. Maybe 0.6.
Gradient stops:
```css
.s1{ stop-color:#7c7a92; transition: stop-color .45s; }
.s2{ stop-color:#4a4860; transition: stop-color .45s; }
#like:checked ~ .stage .s1{ stop-color:#ff8ab0; }
#like:checked ~ .stage .s2{ stop-color:#ff1f4b; }
```
Wait, the `#hg` gradient is inside the label inside .stage — the stops are descendants of .stage. Yes.
But careful: `stop-color` transitions might not animate; that's fine, it'll snap. Actually stop-color is animatable in Chrome I believe. Either way it works.
Halo: a blurred circle behind the button that pulses.
```css
.halo{
position:absolute;
inset:-25%;
border-radius:50%;
background: radial-gradient(circle, rgba(255,45,111,.55), rgba(255,45,111,0) 65%);
filter: blur(18px);
opacity:.35;
transition: opacity .45s, transform .45s;
animation: haloPulse 3.4s ease-in-out infinite;
pointer-events:none;
}
```
Hmm, halo inside the label with inset -25% — it will extend outside the button. That's fine (overflow visible). But `place-items:center` in a grid with an absolutely positioned child — absolute children are out of flow, fine.
Note: `.like-btn` has border-radius 50% but doesn't clip (no overflow hidden), so halo shows.
On checked: halo opacity 1, bigger.
Rings: `<span class="rings"><i></i><i></i><i></i></span>` positioned absolute inset 0, each `i` absolute inset 0 border-radius 50% border 3px solid var(--pink2) opacity 0.
On checked: animation ringOut .9s ease-out forwards with delays 0, .12s, .24s.
Hmm "forwards" with opacity 0 at 100% — fine, and when unchecked animation removed → opacity 0 from base rule.
Burst particles: base `opacity:0`. On checked: `animation: burst .9s cubic-bezier(.15,.7,.3,1) forwards;` with per-particle delay maybe `calc(var(--i) * 8ms)`. Let's just add small random delays via nth-child... simpler: no delay, or use `--dl` inline.
I'll skip delays for particles; instead vary distances.
Now confetti — let's put ~26 pieces. Each: `--x` left position, `--c` color, `--d` duration, `--dl` delay, `--r` rotation.
```css
.confetti{ position:fixed; inset:0; pointer-events:none; overflow:hidden; z-index:5; }
.confetti i{
position:absolute; top:-8vh; left:var(--x); width:9px; height:14px; border-radius:2px;
background:var(--c);
opacity:0;
}
#like:checked ~ .stage .confetti i{
animation: fall var(--d) cubic-bezier(.3,.1,.6,1) var(--dl) forwards;
}
@keyframes fall{
0%{ opacity:0; transform: translate3d(0,-10vh,0) rotate(0deg) scale(.6); }
10%{ opacity:1; }
100%{ opacity:0; transform: translate3d(var(--dx), 115vh, 0) rotate(var(--r)) scale(1); }
}
```
Hmm opacity 0 at 100% means they disappear before reaching bottom; fine, looks like they fade.
Wait — the confetti is inside `.stage` which is `position: fixed; inset: 0`. `.confetti` is fixed too. Fine.
But `.stage` has `overflow:hidden` — confetti inside it will be clipped to the stage, which is the viewport. Fine.
Flash:
```css
.flash{
position:absolute; inset:0; pointer-events:none; z-index:10;
background: radial-gradient(circle at 50% 50%, rgba(255,255,255,.9), rgba(255,120,170,.4) 30%, transparent 62%);
opacity:0;
}
#like:checked ~ .stage .flash{ animation: flash .55s ease-out; }
@keyframes flash{ 0%{opacity:0} 12%{opacity:.75} 100%{opacity:0} }
```
Now the checked heart animation.
```css
#like:checked ~ .stage .heart-holder{
animation: heartPop .85s cubic-bezier(.2,.9,.25,1) both, heartbeat 1.7s 1s ease-in-out infinite;
filter: drop-shadow(0 8px 26px rgba(255,45,111,.85));
}
```
Careful: `animation: ... both` on the first and second infinite. The second has no fill mode; that's fine.
But there's an issue: `.heart-holder` also has `animation: idleBeat ...` in base. The checked rule overrides the whole `animation` property. Good.
Card shake:
```css
#like:checked ~ .stage .card{
animation: cardShake .75s cubic-bezier(.36,.07,.19,.97);
border-color: rgba(255,90,150,.45);
box-shadow: 0 30px 90px -30px rgba(255,45,111,.85), inset 0 1px 0 rgba(255,255,255,.2);
}
```
Card shake keyframes with translate and rotate — but the card also has `transform` from nothing. OK.
Hmm, careful: animating `.card` transform while it's centered in a grid — the transform doesn't affect layout. Good.
Counter roll:
```css
.n{ grid-area:1/1; transition: transform .45s cubic-bezier(.34,1.56,.64,1), opacity .3s; }
.n1{ transform: translateY(110%); opacity:0; }
#like:checked ~ .stage .n0{ transform: translateY(-110%); opacity:0; }
#like:checked ~ .stage .n1{ transform: translateY(0); opacity:1; }
```
With `.num{ overflow:hidden; }` and height 1.2em.
Hmm, translateY(110%) of a span inside an inline-grid — percentage relative to the span's own height. OK.
Wait, the spans are grid items in the same cell, so they have the same height. Fine.
Hint text switch:
```css
.hint{ position:relative; height:1.4em; width:100%; display:grid; place-items:center; }
.hint .h{ grid-area:1/1; transition: opacity .3s, transform .3s; }
.h1{ opacity:0; transform: translateY(6px); }
#like:checked ~ .stage .h0{ opacity:0; transform: translateY(-6px); }
#like:checked ~ .stage .h1{ opacity:1; transform: translateY(0); }
```
Also add a keyboard hint "or press space" — but the checkbox needs focus. Hmm, the label click focuses the checkbox? Clicking a label associated with a checkbox does focus the checkbox in Chrome (yes, it activates it). Then space toggles. Let's add hint "Click the heart · Space to toggle". Eh, might be too much text. Keep "Click the heart".
Also: hide the checkbox input visually but keep it focusable:
```css
.like-input{
position:fixed; top:0; left:0; width:1px; height:1px; opacity:0; pointer-events:none; margin:0;
}
```
Hmm, `opacity: 0` and pointer-events none — the label still toggles it. Keyboard focus works. Add a focus ring on the label via `#like:focus-visible ~ .stage .like-btn { outline: ... }`.
Actually `:focus-visible` on a visually hidden input — should work when tabbing. Add:
```css
#like:focus-visible ~ .stage .like-btn{ box-shadow: 0 0 0 4px rgba(255,255,255,.35), 0 18px 44px -18px rgba(255,45,111,.55); }
```
Now the floaters (ambient hearts):
```css
.floaters{ position:absolute; inset:0; pointer-events:none; z-index:0; }
.floaters span{
position:absolute; bottom:-10vh; color: rgba(255,120,170,.5); font-size: 22px;
animation: floatUp var(--d) linear var(--dl) infinite;
opacity:0;
}
@keyframes floatUp{
0%{ opacity:0; transform: translateY(0) scale(.6) rotate(0deg); }
10%{ opacity:.75; }
90%{ opacity:.2; }
100%{ opacity:0; transform: translateY(-120vh) scale(1.1) rotate(40deg); }
}
```
Position them with `left: var(--x)`, `--d: 12-22s`, `--dl: -0..-20s` (negative delays start mid-animation, giving immediate motion at first paint). Good — negative delays mean at 3s screenshot there are hearts all over.
Let's make sure the floaters don't cover the card visually — they're behind (z-index 0, card has no z-index but comes later in DOM... hmm, the card is after floaters in DOM so it paints on top if same stacking level). Let's give `.card { z-index: 2; }` and floaters z-index 0.
Now, one concern: `place-items: center` on `.stage` with `position: fixed; inset: 0` and multiple absolutely-positioned children. The absolutely positioned children (aurora, floaters, confetti, flash) are out of flow. The card is the only in-flow child → centered.
Now let's write the particle list. 20 particles with angle, distance factor, color, size.
Angles: 0, 18, 36, ..., 342.
Colors cycle: #ff2d6f, #ffd166, #5ee7ff, #a78bfa, #ff6b9d, #4ade80.
Distances: alternate .55, .78, .62, .9, .7...
Sizes: 6-12px.
I'll write:
```html
<i style="--a:0deg;--k:.72;--c:#ff2d6f;--s:10px"></i>
```
And CSS:
```css
.burst i{
position:absolute;
width: var(--s, 9px);
height: var(--s, 9px);
border-radius:50%;
background: var(--c, #fff);
opacity:0;
box-shadow: 0 0 10px var(--c, #fff);
will-change: transform, opacity;
}
```
Some with `border-radius: 2px` via class `.sq`. And some as tiny stars? Keep circles + squares.
Keyframes burst:
```css
@keyframes burst{
0%{ opacity:0; transform: rotate(var(--a)) translateX(0) scale(.2); }
6%{ opacity:1; }
50%{ transform: rotate(var(--a)) translateX(calc(var(--size) * var(--k))) scale(1); opacity:1; }
100%{ opacity:0; transform: rotate(var(--a)) translateX(calc(var(--size) * var(--k) * 1.5)) scale(.1); }
}
```
Hmm — the `50%` keyframe has both transform and opacity; the `6%` has only opacity. That's fine, transform interpolates from 0% to 50% to 100%.
Wait, there's an issue: at 0% opacity 0, at 6% opacity 1, at 50% opacity 1, at 100% opacity 0. And transform from 0 → d at 50% → 1.5d at 100%. Good.
Note `--size` is `clamp(116px, 22vmin, 168px)`; `calc(var(--size) * var(--k))` works with clamp inside calc? `calc(clamp(...) * .72)` — yes, valid in Chrome.
Actually careful: `var(--size)` resolves to `clamp(116px, 22vmin, 168px)`, so calc becomes `calc(clamp(116px,22vmin,168px) * 0.72)`. Multiplication of a length by a number is fine. Good.
The `.burst` container: `position:absolute; inset:0; display:grid; place-items:center;` — the `i` elements are absolutely positioned relative to... `.burst` needs `position:relative` or absolute; it is absolute with inset 0, so it's a containing block. The `i` elements are `position:absolute` with no top/left → they stay at their static position which for a grid container... they're out of flow so they use the grid area placement. Hmm, static position of an abspos child in a grid container is the grid container's content box padding edge (start corner), not centered. That would put particles at top-left of the burst container, not center.
To center them: give `.burst i { top: 50%; left: 50%; margin: calc(var(--s) / -2) ... }`. Simpler: `top:50%; left:50%; transform: translate(-50%,-50%)` — but the transform is used by the animation.
Alternative: `top: calc(50% - var(--s)/2); left: calc(50% - var(--s)/2);` — using the size var. Let's define `--s` for all. `left: calc(50% - var(--s) / 2)`.
Hmm, `calc(50% - var(--s)/2)` where --s is 10px → `calc(50% - 5px)`. Valid.
Actually simpler: make `.burst` a flex/grid with `place-items:center` and make the `i` elements `position:absolute` with `inset:0; margin:auto; width:var(--s); height:var(--s)` — that centers an absolutely positioned element with fixed size in its containing block. Yes! `inset: 0; margin: auto;` with width/height set centers it. Clean.
But then transform-origin is center of the element, and rotation happens around its own center. The translateX moves it outward from its center. That works since the element is at the center of the button.
So:
```css
.burst i{
position:absolute;
inset:0;
margin:auto;
width:var(--s,9px);
height:var(--s,9px);
border-radius:50%;
background:var(--c,#fff);
box-shadow: 0 0 12px var(--c,#fff);
opacity:0;
}
```
Rings similarly: `.rings{ position:absolute; inset:0; }` and `.rings i{ position:absolute; inset:0; border-radius:50%; border:3px solid var(--pink2); opacity:0; }`. inset 0 makes them the full button size. Good.
Now let's double-check the `#like:checked ~ .stage` selector. HTML: `<input id="like">` then `<div class="stage">`. They're siblings. ✓.
One more idea for "crazy": add a "wobble" to the whole stage, plus emoji-ish flying text "❤️" ... maybe add a big "LIKED" text that scales in and out? The hint already handles it.
Let's also add the counter color change to pink when liked.
Let me now write out the final code.
Additional: prefers-reduced-motion? Skip, or add a minimal. Skip for brevity... Actually it's good practice but the demo is about crazy animation. Skip.
Let me write the floaters with negative delays.
```html
<div class="floaters" aria-hidden="true">
<span style="--x:8%; --d:16s; --dl:-2s; --fs:20px">♥</span>
<span style="--x:18%; --d:22s; --dl:-11s; --fs:14px">♥</span>
<span style="--x:29%; --d:19s; --dl:-16s; --fs:26px">♥</span>
<span style="--x:41%; --d:25s; --dl:-6s; --fs:16px">♥</span>
<span style="--x:52%; --d:17s; --dl:-19s; --fs:22px">♥</span>
<span style="--x:63%; --d:21s; --dl:-9s; --fs:13px">♥</span>
<span style="--x:74%; --d:24s; --dl:-14s; --fs:28px">♥</span>
<span style="--x:86%; --d:18s; --dl:-4s; --fs:18px">♥</span>
<span style="--x:94%; --d:23s; --dl:-21s; --fs:15px">♥</span>
<span style="--x:36%; --d:20s; --dl:-13s; --fs:12px">♥</span>
<span style="--x:68%; --d:26s; --dl:-1s; --fs:20px">♥</span>
</div>
```
`font-size: var(--fs)`.
Confetti pieces — 24. Left positions spread, colors varied.
```html
<i style="--x:4%; --c:#ff2d6f; --d:1.6s; --dl:.05s; --r:520deg; --dx:40px"></i>
```
Let me generate a reasonable set.
Now writing the final CSS in order.
One more consideration on `backdrop-filter` — fine in Chrome.
Let me be careful about the `.num` overflow hidden + translateY(110%) — if the parent `.num` has `overflow:hidden` and the spans slide, we see the roll. But `.num` needs a defined height. With inline-grid and one row, height = line-height of content. Set `line-height: 1.05` and `height: 1.05em`? Let's use `.num { display: inline-grid; overflow: hidden; }` — height auto from content = 1.05em. The spans at translateY(110%) are clipped. Good. But the visual roll needs a bit of padding... it's fine.
Actually with overflow hidden, the glow of the text might clip. Fine.
Also add a subtle scale pop to `.num` on check? The counter text is a nice big number. Let's give `.count` a small bump animation on check:
```css
#like:checked ~ .stage .count{ animation: countPop .6s cubic-bezier(.34,1.56,.64,1); }
@keyframes countPop{ 0%{transform:scale(1)} 30%{transform:scale(1.12)} 100%{transform:scale(1)} }
```
Good.
Now, potential problem: multiple animations on the same element triggered by :checked will not re-run if the element already has that animation... They re-run when the animation is removed and re-added, which happens on uncheck/check. ✓
Let me write it.
Also: the label should have `display: grid; place-items: center;` — done via `.like-btn`.
One more: on click, `:active` scale(.93) might interfere visually with the pop but it's momentary.
Also for touch: `touch-action: manipulation` prevents double-tap zoom delay.
Let me finalize the heart SVG path with `stroke-linejoin: round`.
Also: `.heart path { transition: stroke .45s; }` with base stroke rgba(255,255,255,.35).
Let me write everything.
Final check on the flash: it's `position:absolute` inside `.stage` with `inset:0` and z-index 10, above the card. On click it flashes white-ish. Maybe tone it down to .55 opacity max.
Also add a subtle vignette? Not needed.
Writing the code now.
For the burst particle list, I'll write 22 particles.
Angles: I'll do 0, 16.36... let's just use multiples of 18 for 20 particles, plus 2 extra at odd angles. Let's do 20.
k values: .58, .78, .66, .9, .62, .74, .55, .84, .7, .95, .6, .8, .68, .88, .57, .76, .64, .86, .72, .92
Colors: alternate through palette.
Sizes: 6–12px.
Let me write:
```
<i style="--a:0deg; --k:.62; --c:#ff2d6f; --s:11px"></i>
<i style="--a:18deg; --k:.85; --c:#ffd166; --s:8px"></i>
<i style="--a:36deg; --k:.55; --c:#5ee7ff; --s:10px"></i>
<i style="--a:54deg; --k:.92; --c:#ff6b9d; --s:7px"></i>
<i style="--a:72deg; --k:.68; --c:#a78bfa; --s:12px"></i>
<i style="--a:90deg; --k:.8; --c:#ff2d6f; --s:9px"></i>
<i style="--a:108deg; --k:.58; --c:#ffd166; --s:11px"></i>
<i style="--a:126deg; --k:.88; --c:#5ee7ff; --s:7px"></i>
<i style="--a:144deg; --k:.64; --c:#ff6b9d; --s:10px"></i>
<i style="--a:162deg; --k:.95; --c:#a78bfa; --s:8px"></i>
<i style="--a:180deg; --k:.6; --c:#ff2d6f; --s:12px"></i>
<i style="--a:198deg; --k:.82; --c:#ffd166; --s:7px"></i>
<i style="--a:216deg; --k:.57; --c:#5ee7ff; --s:10px"></i>
<i style="--a:234deg; --k:.9; --c:#ff6b9d; --s:9px"></i>
<i style="--a:252deg; --k:.7; --c:#a78bfa; --s:11px"></i>
<i style="--a:270deg; --k:.86; --c:#ff2d6f; --s:8px"></i>
<i style="--a:288deg; --k:.56; --c:#ffd166; --s:12px"></i>
<i style="--a:306deg; --k:.84; --c:#5ee7ff; --s:7px"></i>
<i style="--a:324deg; --k:.66; --c:#ff6b9d; --s:10px"></i>
<i style="--a:342deg; --k:.94; --c:#a78bfa; --s:9px"></i>
```
Good.
Now let me consider whether the burst particles will be clipped. `.like-btn` has no overflow hidden. The `.card` doesn't either. `.stage` has overflow hidden but is full viewport. Fine.
Write it.
Also note: the `<i>` inside `.rings` — I'm using `<i>` for rings too. Fine, style with `.rings i`.
Let me write the final HTML.
One concern: `place-items: center` on `.like-btn` (a grid) with children: halo (absolute), rings (absolute), burst (absolute), heart-holder (in flow). The heart-holder is 52% width/height of the button. Grid place-items center → centered. ✓
Final code below.
Also add `body { -webkit-font-smoothing: antialiased; }`.
For the count text, use a nice font weight 800 and letter-spacing.
Let me assemble.
Double-check the `.num` grid:
```css
.num{
display:inline-grid;
overflow:hidden;
line-height:1.06;
height:1.06em;
}
```
Hmm if I set height:1.06em and line-height:1.06, the text sits fine. But the em is relative to the font-size of .num which is big (clamp). OK.
Actually the grid items will have height = line-height. With overflow hidden and height 1.06em, and the span content... the span's box height equals line-height (1.06em). Good.
translateY(110%) → moves down by 110% of the span's height → fully clipped. ✓
Alright, writing.
Also I should double check: does `.n1 { transform: translateY(110%) }` conflict with the `.count` pop animation? No, different elements.
Writing final.
Actually, one more thought about making the first-paint screenshot impressive: the aurora blobs animate with a long duration; at 3s they're somewhere. Fine.
Let me add a title text above the card? Like "Pure CSS Like Button" — hmm, that could look nice as a small caption. Actually the card contains heart + count + hint. Maybe add a small badge at top of card: "PURE CSS · NO JS". That's a nice touch and communicates. Let's add it as a pill at the top of the card.
```html
<div class="tag">PURE CSS · NO JS</div>
```
Positioned above the button inside the card.
Let's finalize.
Card layout: tag, button, count, hint.
Spacing: gap clamp(14px,2.5vmin,22px).
Alright, writing the final code.
For the fall animation of confetti, `--dx` should be a px value; let's vary from -60px to 60px.
Let me write 24 confetti pieces.
Done thinking; write it.