From bae797fb402e0d9b86c43c5c1dd356cb056e2838 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 2 Aug 2026 23:04:16 -0700 Subject: Motion and polish: navigation choreography, counting balance, gold ribbon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The renderer rebuilds the DOM on every render, so naive CSS mount animations would replay on every background repaint. Two primitives fix that: uiChanged() gates one-shot animations to the render where the thing actually happened, and animWindow() lets a rebuild that lands mid-animation RESUME it via a negative animation-delay instead of killing or restarting it. On top of those: every real navigation rises in (screen/tab/subpage/ chat/profile — one keyed check in render()); the balance counts to its new value and breathes green when money arrives; the avatar menu pops from its corner; the move panel unfolds; the success check springs and rings once when its page lands. Micro-transitions on buttons, tabs, segs, inputs, list rows, avatars, and the settings chevrons. One chromatic signature on the monochrome brand: a slow-drifting bitcoin-gold ribbon across the top of the balance card, echoed by a faint warm wash pooling at the top of the page, plus a film-grain overlay for texture. All of it honors prefers-reduced-motion. The nostr sign-in mark also gets a proper inset ostrich and a size bump. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi --- src/style.css | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'src/style.css') diff --git a/src/style.css b/src/style.css index ca85a8c..f01df60 100644 --- a/src/style.css +++ b/src/style.css @@ -27,6 +27,7 @@ --info-soft: #eef2ff; --info-line: #d4ddfb; --radius: 16px; + --wash: rgba(247, 147, 26, 0.05); /* warm glow behind the page top */ --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace; --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } @@ -55,6 +56,7 @@ --toast-bg: #2c323b; /* raised toast that reads against the dark page */ --toast-fg: #f3f5f8; --tab-bg: #20242b; + --wash: rgba(247, 147, 26, 0.07); --info: #9db4ff; --info-soft: #1a2030; --info-line: #2c3a5e; @@ -406,3 +408,63 @@ hr.divider { margin: 16px 0; } .chat-avatar.fallback.loading { background: var(--surface2); } .wallet-row-btn { font-size: 19px; line-height: 1; padding: 8px 12px; } .chat-avatar.header-ava.fallback { align-items: center; justify-content: center; display: flex; color: var(--muted); } + +/* ============================ motion & polish ============================ */ +/* Navigation animates (the renderer adds .anim-page only when the visible + page changed); background repaints re-create the DOM silently. */ +@keyframes rise-in { from { opacity: 0; transform: translateY(10px); } } +.anim-page { animation: rise-in .32s cubic-bezier(.16, .84, .3, 1) both; } + +@keyframes menu-pop { from { opacity: 0; transform: translateY(-6px) scale(.95); } } +.anim-menu { animation: menu-pop .2s cubic-bezier(.2, .9, .3, 1.35) both; transform-origin: top right; } + +@keyframes unfold-in { from { opacity: 0; transform: translateY(-10px); } } +.anim-unfold { animation: unfold-in .28s cubic-bezier(.16, .84, .3, 1) both; } + +@keyframes flash-green { 0% { color: var(--green); } 100% { color: inherit; } } +.amt-flash { animation: flash-green 1.2s ease-out; } + +/* the success check pops and rings, once, when its page arrives */ +@keyframes check-pop { 0% { transform: scale(.3); opacity: 0; } 60% { transform: scale(1.12); } 100% { transform: scale(1); opacity: 1; } } +@keyframes check-ring { from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--green) 45%, transparent); } to { box-shadow: 0 0 0 30px transparent; } } +.anim-page .check-badge { animation: check-pop .55s cubic-bezier(.34, 1.56, .64, 1) both, check-ring 1.1s ease-out .25s both; } + +/* micro-interactions */ +button, .btn { transition: filter .12s, background .15s, border-color .15s, box-shadow .15s, transform .1s; } +.btn-primary:hover { filter: brightness(1.15); } +.tabs button, .seg button { transition: background .18s, color .18s, box-shadow .18s; } +.item { transition: background .15s; border-radius: 10px; } +.item:hover { background: var(--surface2); } +.clickable { transition: transform .15s ease; } +.chat-avatar.clickable:hover { transform: scale(1.07); } +.header-avatar .chat-avatar { transition: transform .15s ease; } +.header-avatar:hover .chat-avatar { transform: scale(1.08); } +input[type=text], input[type=password], input[type=number], textarea, select { transition: border-color .15s, box-shadow .15s; } +.settings-nav::after { transition: transform .18s ease, color .18s; } +.settings-nav:hover::after { transform: translateX(3px); color: var(--ink); } + +/* the one chromatic signature: a slow bitcoin-gold ribbon on the balance card */ +.balance { position: relative; overflow: hidden; } +.balance::before { + content: ''; position: absolute; inset: 0 0 auto 0; height: 3px; + background: linear-gradient(90deg, #f7931a, #ffd166 30%, #f2a900 55%, #ffb84d 75%, #f7931a); + background-size: 200% 100%; + animation: ribbon 8s linear infinite; +} +@keyframes ribbon { to { background-position: -200% 0; } } + +/* atmosphere: a faint warm wash pooling at the top, plus film grain */ +body { + background-image: radial-gradient(900px 420px at 50% -160px, var(--wash), transparent 70%); + background-attachment: fixed; +} +body::after { + content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 2147483647; + opacity: .028; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); +} + +@media (prefers-reduced-motion: reduce) { + .anim-page, .anim-menu, .anim-unfold, .amt-flash, .check-badge, .balance::before { animation: none !important; } + *, *::before, *::after { transition-duration: .01ms !important; } +} -- cgit v1.2.3