chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,456 @@
|
||||
<!-- Copyright 2026 Anthropic PBC · SPDX-License-Identifier: Apache-2.0 -->
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Acme — Task completed micro-interaction</title>
|
||||
<style>
|
||||
:root {
|
||||
--ivory: #FAF9F5;
|
||||
--slate: #141413;
|
||||
--clay: #D97757;
|
||||
--oat: #E3DACC;
|
||||
--olive: #788C5D;
|
||||
--gray-50: #F0EEE6;
|
||||
--gray-200: #D1CFC5;
|
||||
--gray-500: #87867F;
|
||||
--gray-800: #3D3D3A;
|
||||
--white: #ffffff;
|
||||
--serif: ui-serif, Georgia, "Times New Roman", serif;
|
||||
--sans: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
--mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
||||
/* swapped at runtime by the easing panel */
|
||||
--ease: cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body {
|
||||
margin: 0;
|
||||
background: var(--ivory);
|
||||
color: var(--slate);
|
||||
font-family: var(--sans);
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.wrap {
|
||||
max-width: 1080px;
|
||||
margin: 0 auto;
|
||||
padding: 48px 32px 64px;
|
||||
}
|
||||
header { margin-bottom: 28px; }
|
||||
.eyebrow {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
h1 {
|
||||
font-family: var(--serif);
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 6px 0 4px;
|
||||
}
|
||||
.sub {
|
||||
color: var(--gray-500);
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
/* ---------- layout ---------- */
|
||||
.bench {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 240px;
|
||||
gap: 24px;
|
||||
align-items: start;
|
||||
}
|
||||
.stage {
|
||||
background: var(--white);
|
||||
border: 1.5px solid var(--gray-200);
|
||||
border-radius: 12px;
|
||||
height: 360px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.hint {
|
||||
position: absolute;
|
||||
bottom: 14px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
|
||||
/* ---------- the task row ---------- */
|
||||
.task {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 14px 18px;
|
||||
border: 1.5px solid var(--gray-200);
|
||||
border-radius: 8px;
|
||||
background: var(--white);
|
||||
width: 360px;
|
||||
max-height: 64px;
|
||||
position: relative;
|
||||
transition: max-height 280ms var(--ease) 600ms,
|
||||
opacity 280ms var(--ease) 600ms,
|
||||
padding 280ms var(--ease) 600ms,
|
||||
border-color 200ms var(--ease);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.task:hover { border-color: var(--gray-500); }
|
||||
|
||||
.check {
|
||||
flex: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid var(--gray-500);
|
||||
background: var(--white);
|
||||
position: relative;
|
||||
transition: background 180ms var(--ease),
|
||||
border-color 180ms var(--ease),
|
||||
transform 260ms var(--ease);
|
||||
}
|
||||
.check svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.check path {
|
||||
stroke: var(--white);
|
||||
stroke-width: 2.5;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
stroke-dasharray: 20;
|
||||
stroke-dashoffset: 20;
|
||||
transition: stroke-dashoffset 220ms var(--ease) 80ms;
|
||||
}
|
||||
|
||||
.label {
|
||||
position: relative;
|
||||
font-size: 15px;
|
||||
color: var(--slate);
|
||||
transition: color 200ms var(--ease) 120ms;
|
||||
}
|
||||
.label::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 52%;
|
||||
height: 1.5px;
|
||||
width: 0;
|
||||
background: var(--gray-500);
|
||||
transition: width 240ms var(--ease) 120ms;
|
||||
}
|
||||
|
||||
.due {
|
||||
margin-left: auto;
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
color: var(--gray-500);
|
||||
background: var(--gray-50);
|
||||
border: 1.5px solid var(--gray-200);
|
||||
padding: 3px 8px;
|
||||
border-radius: 8px;
|
||||
transition: opacity 200ms var(--ease) 120ms;
|
||||
}
|
||||
|
||||
/* confetti — six particles, css-only, driven by .done on the row */
|
||||
.confetti {
|
||||
position: absolute;
|
||||
left: 22px;
|
||||
top: 50%;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 2px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.confetti.c1 { background: var(--clay); }
|
||||
.confetti.c2 { background: var(--olive); }
|
||||
.confetti.c3 { background: var(--oat); }
|
||||
.confetti.c4 { background: var(--clay); }
|
||||
.confetti.c5 { background: var(--olive); }
|
||||
.confetti.c6 { background: var(--gray-800); }
|
||||
|
||||
/* ---------- done state ---------- */
|
||||
.task.done .check {
|
||||
background: var(--olive);
|
||||
border-color: var(--olive);
|
||||
transform: scale(1.12);
|
||||
animation: settle 380ms var(--ease) forwards;
|
||||
}
|
||||
@keyframes settle {
|
||||
0% { transform: scale(0.8); background: var(--clay); border-color: var(--clay); }
|
||||
55% { transform: scale(1.18); }
|
||||
100% { transform: scale(1); background: var(--olive); border-color: var(--olive); }
|
||||
}
|
||||
.task.done .check path { stroke-dashoffset: 0; }
|
||||
.task.done .label { color: var(--gray-500); }
|
||||
.task.done .label::after { width: 100%; }
|
||||
.task.done .due { opacity: 0.35; }
|
||||
.task.done {
|
||||
max-height: 44px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.task.done .confetti { animation: pop 520ms var(--ease) 200ms forwards; }
|
||||
.task.done .confetti.c1 { --dx: -28px; --dy: -22px; --rot: 40deg; }
|
||||
.task.done .confetti.c2 { --dx: -6px; --dy: -34px; --rot: -60deg; }
|
||||
.task.done .confetti.c3 { --dx: 18px; --dy: -26px; --rot: 90deg; }
|
||||
.task.done .confetti.c4 { --dx: 30px; --dy: -4px; --rot: -30deg; }
|
||||
.task.done .confetti.c5 { --dx: -24px; --dy: 14px; --rot: 20deg; }
|
||||
.task.done .confetti.c6 { --dx: 12px; --dy: 22px; --rot: -80deg; }
|
||||
@keyframes pop {
|
||||
0% { opacity: 0; transform: translate(0, 0) rotate(0) scale(0.6); }
|
||||
15% { opacity: 1; }
|
||||
100% { opacity: 0; transform: translate(var(--dx), var(--dy)) rotate(var(--rot)) scale(1); }
|
||||
}
|
||||
|
||||
/* ---------- easing panel ---------- */
|
||||
.panel {
|
||||
background: var(--white);
|
||||
border: 1.5px solid var(--gray-200);
|
||||
border-radius: 12px;
|
||||
padding: 18px;
|
||||
}
|
||||
.panel h2 {
|
||||
font-family: var(--serif);
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
.panel p {
|
||||
margin: 0 0 14px;
|
||||
font-size: 12px;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
.ease-btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
background: var(--gray-50);
|
||||
border: 1.5px solid var(--gray-200);
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 8px;
|
||||
font-family: var(--sans);
|
||||
font-size: 13px;
|
||||
color: var(--slate);
|
||||
cursor: pointer;
|
||||
}
|
||||
.ease-btn:hover { border-color: var(--gray-500); }
|
||||
.ease-btn.active {
|
||||
border-color: var(--clay);
|
||||
background: #FBF1EC;
|
||||
}
|
||||
.ease-btn code {
|
||||
display: block;
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
color: var(--gray-500);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* ---------- timeline ---------- */
|
||||
.timeline {
|
||||
margin-top: 28px;
|
||||
background: var(--white);
|
||||
border: 1.5px solid var(--gray-200);
|
||||
border-radius: 12px;
|
||||
padding: 22px 22px 30px;
|
||||
}
|
||||
.timeline h2 {
|
||||
font-family: var(--serif);
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
margin: 0 0 18px;
|
||||
}
|
||||
.track {
|
||||
position: relative;
|
||||
height: 2px;
|
||||
background: var(--gray-200);
|
||||
margin: 20px 8px 0;
|
||||
}
|
||||
.key {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: var(--clay);
|
||||
border: 1.5px solid var(--white);
|
||||
box-shadow: 0 0 0 1.5px var(--clay);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.key span {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
color: var(--gray-800);
|
||||
}
|
||||
.key em {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
font-style: normal;
|
||||
font-size: 11px;
|
||||
color: var(--gray-500);
|
||||
}
|
||||
.k1 { left: 0%; }
|
||||
.k2 { left: 13%; }
|
||||
.k3 { left: 20%; }
|
||||
.k4 { left: 33%; }
|
||||
.k5 { left: 100%; background: var(--olive); box-shadow: 0 0 0 1.5px var(--olive); }
|
||||
|
||||
/* ---------- snippet ---------- */
|
||||
.snippet {
|
||||
margin-top: 28px;
|
||||
}
|
||||
.snippet h2 {
|
||||
font-family: var(--serif);
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
pre {
|
||||
background: var(--gray-800);
|
||||
color: var(--gray-50);
|
||||
border-radius: 12px;
|
||||
padding: 18px 20px;
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
overflow-x: auto;
|
||||
margin: 0;
|
||||
}
|
||||
pre .c { color: var(--gray-500); }
|
||||
pre .k { color: var(--clay); }
|
||||
pre .v { color: #B8C99D; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
|
||||
<header>
|
||||
<div class="eyebrow">Acme / prototype / micro-interaction</div>
|
||||
<h1>Task completed</h1>
|
||||
<p class="sub">
|
||||
A single click should feel like a tiny win. Circle fills, check draws,
|
||||
label strikes, a small burst, then the row quietly steps back. Click
|
||||
the row to play; click again to reset.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="bench">
|
||||
|
||||
<div class="stage">
|
||||
<div class="task" id="task">
|
||||
<div class="check">
|
||||
<svg viewBox="0 0 16 16"><path d="M3 8.5 L6.5 12 L13 4.5"/></svg>
|
||||
</div>
|
||||
<span class="label">Send weekly digest</span>
|
||||
<span class="due">Fri</span>
|
||||
<div class="confetti c1"></div>
|
||||
<div class="confetti c2"></div>
|
||||
<div class="confetti c3"></div>
|
||||
<div class="confetti c4"></div>
|
||||
<div class="confetti c5"></div>
|
||||
<div class="confetti c6"></div>
|
||||
</div>
|
||||
<div class="hint">click to toggle</div>
|
||||
</div>
|
||||
|
||||
<aside class="panel">
|
||||
<h2>Easing</h2>
|
||||
<p>Swaps <code>--ease</code> across every transition so you can feel the difference.</p>
|
||||
<button class="ease-btn" data-ease="linear">
|
||||
Linear
|
||||
<code>linear</code>
|
||||
</button>
|
||||
<button class="ease-btn" data-ease="cubic-bezier(0.16, 1, 0.3, 1)">
|
||||
Ease-out
|
||||
<code>cubic-bezier(.16, 1, .3, 1)</code>
|
||||
</button>
|
||||
<button class="ease-btn active" data-ease="cubic-bezier(0.34, 1.56, 0.64, 1)">
|
||||
Spring
|
||||
<code>cubic-bezier(.34, 1.56, .64, 1)</code>
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
<section class="timeline">
|
||||
<h2>Keyframes</h2>
|
||||
<div class="track">
|
||||
<div class="key k1"><em>fill</em><span>0ms</span></div>
|
||||
<div class="key k2"><em>check</em><span>80ms</span></div>
|
||||
<div class="key k3"><em>strike</em><span>120ms</span></div>
|
||||
<div class="key k4"><em>confetti</em><span>200ms</span></div>
|
||||
<div class="key k5"><em>collapse</em><span>600ms</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="snippet">
|
||||
<h2>Copy-paste CSS</h2>
|
||||
<pre><span class="c">/* circle: clay flash, settle to olive with spring overshoot */</span>
|
||||
<span class="k">.task.done .check</span> {
|
||||
animation: settle 380ms <span class="v">cubic-bezier(.34,1.56,.64,1)</span> forwards;
|
||||
}
|
||||
<span class="k">@keyframes settle</span> {
|
||||
0% { transform: scale(.8); background: <span class="v">#D97757</span>; }
|
||||
55% { transform: scale(1.18); }
|
||||
100% { transform: scale(1); background: <span class="v">#788C5D</span>; }
|
||||
}
|
||||
|
||||
<span class="c">/* checkmark draws via stroke-dashoffset, 80ms delay */</span>
|
||||
<span class="k">.check path</span> { stroke-dasharray: 20; stroke-dashoffset: 20;
|
||||
transition: stroke-dashoffset 220ms var(--ease) 80ms; }
|
||||
<span class="k">.task.done path</span> { stroke-dashoffset: 0; }
|
||||
|
||||
<span class="c">/* strikethrough grows left → right */</span>
|
||||
<span class="k">.label::after</span> { width: 0; transition: width 240ms var(--ease) 120ms; }
|
||||
<span class="k">.task.done .label::after</span> { width: 100%; }
|
||||
|
||||
<span class="c">/* row steps back after the celebration */</span>
|
||||
<span class="k">.task.done</span> { max-height: 44px; opacity: .6;
|
||||
transition-delay: 600ms; }</pre>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var task = document.getElementById('task');
|
||||
task.addEventListener('click', function () {
|
||||
task.classList.toggle('done');
|
||||
});
|
||||
|
||||
var root = document.documentElement;
|
||||
var buttons = document.querySelectorAll('.ease-btn');
|
||||
buttons.forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
buttons.forEach(function (b) { b.classList.remove('active'); });
|
||||
btn.classList.add('active');
|
||||
root.style.setProperty('--ease', btn.dataset.ease);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user