Models 44

Claude Sonnet 5.5

by Anthropicreasoning defaultClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
2 min 17 s
Cost
$0.186
Output tokens
18,462
Spent thinking
11,260 tokens
Speed
137.5 tok/s
Console errors
0
First token after
2.4 s
First visible text after
1 min 42 s
Prompt tokens
538
Code size
14.1 KB
Finish reason
stop
Served by
Claude Platform on AWS
Model id
anthropic/claude-sonnet-5.5
Reasoning sent
model default
Max output
128,000 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 · 340 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pure CSS Like Button</title>
<style>
@property --ang { syntax: '<angle>'; initial-value: 0deg; inherits: false; }

* { box-sizing: border-box; margin: 0; }
html, body { height: 100%; }
body {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  background: #0c0e1f;
  color: #e8eaff;
  overflow: hidden;
}
.page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
  overflow: hidden;
  background:
    radial-gradient(900px 500px at 15% 10%, rgba(120, 70, 255, .22), transparent 60%),
    radial-gradient(800px 500px at 90% 90%, rgba(255, 60, 120, .18), transparent 60%),
    #0c0e1f;
}

.stage {
  --T: 2.8s;
  --dl: -.6s;
  --it: infinite;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  text-align: center;
  position: relative;
}
.stage:has(input:checked) { --dl: 0s; --it: 1; }

h1 { font-size: clamp(22px, 4vw, 34px); letter-spacing: .02em; font-weight: 800; }
h1 span { background: linear-gradient(90deg, #ff3d71, #ffb13d, #3dffa0, #3dc7ff, #b43dff); -webkit-background-clip: text; background-clip: text; color: transparent; }
.sub { color: #8d92c9; font-size: 15px; max-width: 34ch; line-height: 1.5; }
.hint { color: #6e73a8; font-size: 13px; margin-top: 56px; }
.hint b { color: #ffb1c7; }

.shaker { padding: 120px 100px; margin: -80px -70px; display: grid; place-items: center; }

/* hidden checkbox */
.stage input {
  position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;
}

/* ---------- the button ---------- */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 18px 30px 18px 24px;
  border-radius: 999px;
  font-weight: 800;
  font-size: clamp(18px, 4.6vw, 24px);
  color: #cfd3ff;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform .2s, color .3s;
}
.btn:hover { transform: translateY(-3px) scale(1.04); }
.btn:active { transform: scale(.92); }
.stage input:focus-visible + .btn { outline: 3px solid #fff; outline-offset: 6px; }

.face {
  position: absolute; inset: 0; border-radius: inherit; z-index: -1;
  background: #1d2140;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45), inset 0 0 0 2px rgba(255, 255, 255, .08);
  transition: background .3s, box-shadow .3s;
}
.face::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: linear-gradient(135deg, #ff2d6f, #ff8a3d);
  opacity: 0; transition: opacity .35s;
}
.btn::before, .btn::after {
  content: ""; position: absolute; inset: -4px; border-radius: inherit; z-index: -2;
  background: conic-gradient(from var(--ang), #ff2d6f, #ffd23d, #3dff9a, #3dc7ff, #b43dff, #ff2d6f);
  opacity: 0; transition: opacity .4s;
  animation: spin 1.6s linear infinite;
}
.btn::after { inset: -4px; z-index: -3; filter: blur(16px); }
@keyframes spin { to { --ang: 360deg; } }

.stage:has(input:checked) .btn { color: #fff; animation: jelly .9s both; }
.stage:has(input:checked) .face::before { opacity: 1; }
.stage:has(input:checked) .btn::before { opacity: 1; }
.stage:has(input:checked) .btn::after { opacity: .7; }

@keyframes jelly {
  0% { transform: scale(1); }
  8% { transform: scale(.7, 1.15) rotate(-4deg); }
  20% { transform: scale(1.3, .8) rotate(3deg); }
  32% { transform: scale(.88, 1.14) rotate(-2deg); }
  46% { transform: scale(1.1, .94); }
  62% { transform: scale(.97, 1.03); }
  100% { transform: scale(1); }
}
.stage:has(input:checked) .shaker { animation: shake .7s both; }
@keyframes shake {
  0%, 100% { transform: translate(0); }
  10% { transform: translate(-12px, 6px) rotate(-1deg); }
  20% { transform: translate(11px, -7px) rotate(1deg); }
  30% { transform: translate(-9px, -5px); }
  40% { transform: translate(8px, 6px) rotate(-.5deg); }
  55% { transform: translate(-5px, 3px); }
  70% { transform: translate(3px, -2px); }
  85% { transform: translate(-1px, 1px); }
}
.flash {
  position: fixed; inset: 0; pointer-events: none; opacity: 0; z-index: 5;
  background: radial-gradient(circle at 50% 45%, rgba(255, 90, 140, .6), rgba(255, 180, 60, .2) 40%, transparent 70%);
}
.stage:has(input:checked) .flash { animation: flash 1s ease-out both; }
@keyframes flash { 0% { opacity: 0; } 8% { opacity: 1; } 100% { opacity: 0; } }

/* ---------- icon + effects ---------- */
.icon { position: relative; width: 34px; height: 34px; flex: none; }
.heart {
  position: relative; z-index: 2; width: 100%; height: 100%; overflow: visible;
  fill: transparent; stroke: #cfd3ff; stroke-width: 2.2; stroke-linejoin: round;
  animation-name: hp, hc; animation-timing-function: ease-in-out;
}
.stage:has(input:checked) .heart {
  fill: #fff; stroke: #fff; animation-name: hpk;
  filter: drop-shadow(0 0 8px rgba(255, 255, 255, .9));
}
.fx { position: absolute; left: 50%; top: 50%; width: 0; height: 0; pointer-events: none; z-index: 3; }
.fx > * { position: absolute; }

.stage :is(.s, .c, .r, .heart) {
  animation-duration: var(--T);
  animation-delay: var(--dl);
  animation-iteration-count: var(--it);
  animation-fill-mode: both;
}

/* streaks */
.s {
  left: -2px; top: -8px; width: 4px; height: 16px; border-radius: 4px;
  background: hsl(var(--h) 100% 62%);
  box-shadow: 0 0 8px hsl(var(--h) 100% 60%);
  animation-name: sb;
}
.stage:has(input:checked) .s { animation-name: sbk; }
@keyframes sb {
  0%, 3% { transform: rotate(var(--a)) translateY(-14px) scaleY(.2); opacity: 0; }
  8% { opacity: 1; }
  30% { transform: rotate(var(--a)) translateY(calc(var(--d) * -.8)) scaleY(1.7); }
  60%, 100% { transform: rotate(var(--a)) translateY(calc(var(--d) * -1.15)) scaleY(.1); opacity: 0; }
}
@keyframes sbk {
  0%, 3% { transform: rotate(var(--a)) translateY(-14px) scaleY(.2); opacity: 0; }
  8% { opacity: 1; }
  30% { transform: rotate(var(--a)) translateY(calc(var(--d) * -.8)) scaleY(1.7); }
  60%, 100% { transform: rotate(var(--a)) translateY(calc(var(--d) * -1.15)) scaleY(.1); opacity: 0; }
}

/* confetti */
.c {
  left: -4px; top: -6px; width: 8px; height: 12px;
  background: hsl(var(--h) 100% 60%);
  animation-name: cx, cy;
  animation-timing-function: cubic-bezier(.15, .6, .3, 1), linear;
}
.c:nth-child(3n) { border-radius: 50%; width: 10px; height: 10px; }
.c:nth-child(4n+1) { width: 14px; height: 5px; }
.stage:has(input:checked) .c { animation-name: cxk, cyk; }
@keyframes cx { 0% { translate: 0 0; } 65%, 100% { translate: var(--x) 0; } }
@keyframes cxk { 0% { translate: 0 0; } 65%, 100% { translate: var(--x) 0; } }
@keyframes cy {
  0% { transform: translateY(0) rotate(0) scale(0); opacity: 1; animation-timing-function: ease-out; }
  10% { transform: translateY(calc(var(--u) * .4)) rotate(calc(var(--r) * .15)) scale(1); }
  28% { transform: translateY(var(--u)) rotate(calc(var(--r) * .4)) scale(1); animation-timing-function: ease-in; }
  68% { transform: translateY(var(--g)) rotate(var(--r)) scale(1); opacity: 1; }
  74%, 100% { transform: translateY(var(--g)) rotate(var(--r)) scale(1); opacity: 0; }
}
@keyframes cyk {
  0% { transform: translateY(0) rotate(0) scale(0); opacity: 1; animation-timing-function: ease-out; }
  10% { transform: translateY(calc(var(--u) * .4)) rotate(calc(var(--r) * .15)) scale(1); }
  28% { transform: translateY(var(--u)) rotate(calc(var(--r) * .4)) scale(1); animation-timing-function: ease-in; }
  68% { transform: translateY(var(--g)) rotate(var(--r)) scale(1); opacity: 1; }
  74%, 100% { transform: translateY(var(--g)) rotate(var(--r)) scale(1); opacity: 0; }
}

/* shockwave rings */
.r {
  left: -12px; top: -12px; width: 24px; height: 24px; border-radius: 50%;
  border: 4px solid #ffb1c7;
  animation-name: rg; animation-timing-function: cubic-bezier(.1, .6, .3, 1);
}
.stage .r + .r { border-color: #ffd23d; animation-delay: calc(var(--dl) + .14s); }
.stage:has(input:checked) .r { animation-name: rgk; }
@keyframes rg {
  0%, 2% { transform: scale(0); opacity: .95; }
  35% { opacity: .6; }
  45%, 100% { transform: scale(8); opacity: 0; border-width: 0; }
}
@keyframes rgk {
  0%, 2% { transform: scale(0); opacity: .95; }
  35% { opacity: .6; }
  45%, 100% { transform: scale(8); opacity: 0; border-width: 0; }
}

/* heart pop */
@keyframes hp {
  0%, 2% { transform: scale(1) rotate(0); }
  6% { transform: scale(.55) rotate(-25deg); }
  15% { transform: scale(1.9) rotate(15deg); }
  24% { transform: scale(.85) rotate(-8deg); }
  33% { transform: scale(1.2) rotate(4deg); }
  42%, 100% { transform: scale(1) rotate(0); }
}
@keyframes hpk {
  0%, 2% { transform: scale(1) rotate(0); }
  6% { transform: scale(.55) rotate(-25deg); }
  15% { transform: scale(1.9) rotate(15deg); }
  24% { transform: scale(.85) rotate(-8deg); }
  33% { transform: scale(1.2) rotate(4deg); }
  42%, 100% { transform: scale(1) rotate(0); }
}
@keyframes hc {
  0%, 5% { fill: transparent; stroke: #cfd3ff; }
  14%, 82% { fill: #ff3d71; stroke: #ff3d71; }
  92%, 100% { fill: transparent; stroke: #cfd3ff; }
}

/* +1 */
.plus {
  position: absolute; left: 50%; top: -6px; z-index: 4;
  font-size: 30px; font-weight: 900; color: #fff;
  text-shadow: 0 0 12px #ff3d71, 0 0 24px #ffb13d;
  opacity: 0; pointer-events: none; transform: translateX(-50%);
}
.stage:has(input:checked) .plus { animation: plus 1.3s .1s ease-out both; }
@keyframes plus {
  0% { opacity: 0; transform: translate(-50%, 0) scale(.3); }
  20% { opacity: 1; transform: translate(-50%, -30px) scale(1.7) rotate(-8deg); }
  100% { opacity: 0; transform: translate(-50%, -130px) scale(1) rotate(10deg); }
}

/* label + counter */
.txt { display: grid; min-width: 4.4ch; text-align: left; }
.txt span { grid-area: 1 / 1; transition: transform .45s cubic-bezier(.3, 1.8, .5, 1), opacity .3s; }
.txt .t1 { opacity: 0; transform: translateY(70%) rotate(8deg); }
.stage:has(input:checked) .txt .t0 { opacity: 0; transform: translateY(-70%) rotate(-8deg); }
.stage:has(input:checked) .txt .t1 { opacity: 1; transform: none; }
.sep { width: 2px; height: 1.4em; background: currentColor; opacity: .25; border-radius: 2px; }
.count { height: 1.3em; line-height: 1.3em; overflow: hidden; min-width: 3.6ch; text-align: left; font-variant-numeric: tabular-nums; }
.col { display: block; transition: transform .6s cubic-bezier(.3, 1.5, .5, 1); }
.col b { display: block; font-weight: 800; }
.stage:has(input:checked) .col { transform: translateY(-1.3em); }

@media (max-width: 480px) {
  .shaker { padding: 110px 40px; margin: -80px -40px; }
  .btn { gap: 10px; padding: 16px 22px 16px 18px; }
  .hint { margin-top: 40px; }
}
@media (prefers-reduced-motion: reduce) {
  .stage { --it: 1; }
}
</style>
</head>
<body>
<div class="page">
  <div class="stage">
    <div class="flash"></div>
    <h1>Pure CSS <span>Like</span> Button</h1>
    <p class="sub">Zero JavaScript. Just a checkbox, a label and a lot of keyframes.</p>

    <div class="shaker">
      <input type="checkbox" id="like" aria-label="Like">
      <label for="like" class="btn">
        <span class="face"></span>
        <span class="icon">
          <svg class="heart" viewBox="0 0 24 24" aria-hidden="true">
            <path d="M12 21s-7.5-4.6-9.6-9.2C.9 8.4 2.6 4.5 6.4 4.5c2.1 0 3.9 1.2 5.6 3.2 1.7-2 3.5-3.2 5.6-3.2 3.8 0 5.5 3.9 4 7.3C19.5 16.4 12 21 12 21z"/>
          </svg>
          <span class="plus">+1</span>
          <span class="fx">
            <i class="r"></i><i class="r"></i>

            <i class="s" style="--a:0deg;--d:70px;--h:340"></i>
            <i class="s" style="--a:30deg;--d:100px;--h:10"></i>
            <i class="s" style="--a:60deg;--d:70px;--h:35"></i>
            <i class="s" style="--a:90deg;--d:100px;--h:55"></i>
            <i class="s" style="--a:120deg;--d:70px;--h:130"></i>
            <i class="s" style="--a:150deg;--d:100px;--h:165"></i>
            <i class="s" style="--a:180deg;--d:70px;--h:190"></i>
            <i class="s" style="--a:210deg;--d:100px;--h:215"></i>
            <i class="s" style="--a:240deg;--d:70px;--h:260"></i>
            <i class="s" style="--a:270deg;--d:100px;--h:285"></i>
            <i class="s" style="--a:300deg;--d:70px;--h:310"></i>
            <i class="s" style="--a:330deg;--d:100px;--h:330"></i>

            <i class="c" style="--x:-150px;--u:-120px;--g:80px;--r:720deg;--h:340"></i>
            <i class="c" style="--x:140px;--u:-130px;--g:90px;--r:-680deg;--h:20"></i>
            <i class="c" style="--x:-90px;--u:-160px;--g:120px;--r:900deg;--h:50"></i>
            <i class="c" style="--x:100px;--u:-170px;--g:110px;--r:-840deg;--h:140"></i>
            <i class="c" style="--x:-40px;--u:-190px;--g:150px;--r:540deg;--h:190"></i>
            <i class="c" style="--x:60px;--u:-200px;--g:140px;--r:-600deg;--h:260"></i>
            <i class="c" style="--x:-170px;--u:-70px;--g:60px;--r:480deg;--h:300"></i>
            <i class="c" style="--x:170px;--u:-80px;--g:70px;--r:-520deg;--h:340"></i>
            <i class="c" style="--x:-120px;--u:-140px;--g:160px;--r:960deg;--h:20"></i>
            <i class="c" style="--x:130px;--u:-150px;--g:170px;--r:-900deg;--h:50"></i>
            <i class="c" style="--x:-20px;--u:-130px;--g:90px;--r:360deg;--h:140"></i>
            <i class="c" style="--x:25px;--u:-150px;--g:100px;--r:-400deg;--h:190"></i>
            <i class="c" style="--x:-70px;--u:-100px;--g:130px;--r:640deg;--h:260"></i>
            <i class="c" style="--x:80px;--u:-110px;--g:140px;--r:-700deg;--h:300"></i>
            <i class="c" style="--x:-190px;--u:-100px;--g:100px;--r:800deg;--h:340"></i>
            <i class="c" style="--x:190px;--u:-110px;--g:120px;--r:-760deg;--h:20"></i>
            <i class="c" style="--x:-110px;--u:-200px;--g:180px;--r:1000deg;--h:50"></i>
            <i class="c" style="--x:115px;--u:-210px;--g:190px;--r:-980deg;--h:140"></i>
            <i class="c" style="--x:0px;--u:-220px;--g:170px;--r:420deg;--h:190"></i>
            <i class="c" style="--x:-55px;--u:-170px;--g:200px;--r:-500deg;--h:260"></i>
          </span>
        </span>
        <span class="txt"><span class="t0">Like</span><span class="t1">Liked!</span></span>
        <span class="sep"></span>
        <span class="count"><span class="col"><b>1,204</b><b>1,205</b></span></span>
      </label>
    </div>

    <p class="hint">Demo loop playing — <b>click the button</b> to take over (click again to unlike &amp; re-trigger)</p>
  </div>
</div>
</body>
</html>
Reasoning trace (11,260 tokens)
I'm working on a pure CSS button that does a wild animation on click, using the checkbox hack with a label and no JavaScript. Since it has to look alive on first paint with no interaction, I'm considering an idle pulse or floating hint, and maybe a second preview button that loops the animation with CSS keyframes.

Still working out the selectors so the animation runs both on click and on a looping auto preview. I'm leaning toward a wrapper rule that matches either the checked state or an auto class, with a longer infinite duration for the preview.

I'm weighing a looping auto-demo on the unchecked state, so the button animates on its own until someone clicks. Stopping that loop once the box is checked would hand over to the click animation, and a cycle of about 5 seconds with the burst landing near the 3-second mark would keep the first-paint screenshot lively.

I'm settling on a looping auto-demo for the unchecked state, with the burst landing around the 1 to 3 second mark so the first-paint screenshot still shows it. The trouble is that a one-time keyframe for the click and an infinite one for the loop would be duplicated, and I'm still looking for a way to keep the rest period without splitting the keyframes.

I'm weighing whether the loop can keep the rest period without duplicating the keyframes, and I'm leaning toward keyframes that hold their final frames for a few percent of the cycle. Using the same keyframes for the checked state with a single iteration would avoid splitting them.

Looping the demo means the particles just restart right away, with the rest built into the keyframes. Once the box is checked, the loop switches off and the click burst plays once. The heart needs its own looping keyframes for the demo. Unchecking would drop back into the loop, which is a bit odd, though it's acceptable for now.

Now I'm laying out the animation pieces: a squish-and-wobble on the button with a screen shake, an inline SVG heart whose fill transitions, two expanding shockwave rings, and about fourteen radiating particles driven by angle and distance variables.

Still working out the confetti: about 20 pieces that fall with gravity, split between an x-axis drift and a y-axis arc so the motion looks like a parabola.

Still writing the confetti markup by hand, since CSS can't index siblings to vary each piece. I'm planning around 12 burst particles at 30° steps and 24 confetti pieces, each given its own inline variables.

Still writing the markup: a stage wrapper with a hidden checkbox, a label button holding the rings, the heart icon, the text, the counter, and a particle layer. I'm placing the particle layer over the heart so the burst originates there.

Still writing the CSS keyframes, now the burst particles, each rotated by its angle and flying outward while shrinking and fading. I'm also sorting out the particle shape and whether the idle loop can share the same keyframes.

Styling the particles as thin radial streaks that shrink as they fly out, with the confetti split into separate horizontal and vertical motion.

Now I'm settling the loop timing: every element will share one 2.4s cycle, so the confetti finishes early in each cycle and the heart goes grey, then liked, then grey again on the same clock.

I'm settling the timing: every element runs on one shared 2.8s cycle, with the actions packed into the first 70% and the heart liked from about 5% to 85%. A screenshot taken around 3 seconds would land just 0.2 seconds into the second cycle, right as the burst begins, so the particles should already be moving. The title and the rest of the page will be visible too.

Still working out the timing: I'm giving the loop a negative delay of about 0.6 seconds through a shared variable, so a screenshot near the 3-second mark lands partway into the action, with the checked state resetting that delay to zero.

The problem I hit is that changing the iteration count and delay on a running loop won't restart it, so clicking would never fire a fresh burst. I need the click to trigger its own animation, probably by swapping animation names rather than tweaking shared variables.

I'm weighing whether the checked state can simply swap the keyframe name, since changing iteration count on a running loop won't restart the burst. Swapping to a second set of keyframe names for the clicked state seems like the cleanest way to get a fresh animation on each click.

I'm reconsidering the two animation modes, since duplicating the keyframes for the click state feels clumsy. Keeping only one set of keyframes with a single name might still let the click restart the burst, but I'm not sure browsers treat a changed animation list that way.

Still working out the keyframe duplication, which would double the burst and confetti animations for the checked state. I'm weighing whether to duplicate the keyframes or split the demo and clicked elements, and the keyframe route is smaller.

I'm weighing whether to duplicate the keyframes so the looping idle state and the clicked state each get their own names, since changing the iteration count on a running animation won't restart the burst. I'm leaning toward keeping the set small: burst, confetti drift, confetti fall, rings, heart, and shake.

Now I'm figuring out how to switch the click state to a fresh burst, since changing the iteration count on a running loop won't restart it, so I'll duplicate the keyframes under new names for the checked state.

Continuing the CSS: I'm settling on shared longhand animation properties for the particle streaks and confetti, using the same duration, delay, and iteration count variables so they all follow one timeline. Each element gets its own keyframe name, with a checked-state variant to restart the burst on click.

Still writing the keyframes, now the particle streaks that rotate outward, shrink, and fade over the loop.

Still writing the confetti keyframes, with the x-drift and y-fall each on their own animation, duplicated under new names for the checked state.

Still writing the confetti keyframes, now the vertical motion: a rise and fall with a rotation and a fade near the end.

Still writing the CSS keyframes for the rings and the remaining confetti, with the inline variables for about 20 pieces.

Still writing the heart animation: a looping version that goes grey, pops red with a little rotation, then settles back, and a checked-state variant that ends on red instead.

Still writing the CSS for the heart and the button itself. The heart gets a transform loop plus a separate color cycle, and the checked state keeps it red. The whole button gets a squish-and-bounce animation, with a pink background shift when it's checked.

Looking at the background and button text, I'm keeping the loop's button unchanged, plain "Like", and only swapping to "Liked!" when checked. For the shake, I'll apply it to the wrapper just once in the checked state through a `:has()` selector, so it needs no duplicated keyframes.

Now I'm adding a full-viewport flash overlay that only appears when checked, plus a "+1" floater and a rolling count, all in single sets with no duplicated keyframes. I'm also planning a conic-gradient ring that spins around the button when liked, and I'll keep the button's overflow visible so the effects aren't clipped.

Still working out the hint text and the background: a dark navy gradient with faint drifting dots, a "Pure CSS Like" title above, and a line under the button saying no JavaScript and to click it.

Next I'm laying out the button's structure: an inline-flex row with a pill shape and fixed padding. The count sits inside it as a short clipped column that rolls from one number to the next when checked, and the hint text and focus outline are set up around the hidden checkbox.

Still writing the CSS for the button's text swap and its checked-state colors, a pink-to-orange gradient with a glow. Next up are the shake on the wrapper and the floating "+1" element.

Still writing the CSS for the plus floater and the heart's SVG markup.

Still writing the heart's SVG markup, with the stroke and fill colors set up so the outline turns solid when liked.

Still laying out the button's background and the responsive sizing, with the checked state layering a gradient over a plain base color.

Still writing the CSS: page overflow hidden so the confetti doesn't cause horizontal scrolling, and the confetti pieces get their sizes and hues set up.

Still writing the confetti data, filling in the remaining pieces' drift, fall, and rotation values.

Still writing the CSS for the button's glow ring, with the spinning conic gradient set up behind it and turned on when checked.

Still writing the ring: the glow is a spinning conic gradient placed behind the button, so the button face has to sit on its own layer above it.

Still writing the heart's keyframes: the loop version fills it red with a grey outline, then fades back, and the checked version holds it solid white.

Still writing the heart's keyframes, now the spring-like scale and rotation bounces, and I'm setting the heart to use two animations at once: the pop and the color change.

Still writing the CSS for the shake and the spinning ring behind the button, with the stacking set so the gradient sits behind the button face.

Still writing the count display: a fixed-height clipped column that rolls the digits from one number to the next.

Still writing the count display: a clipped column of two stacked digits that slides up when checked, with tabular numbers so the width stays steady.