From e07023af899f6682e088aee9ff3329b16b8f467d Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 18 Sep 2026 23:12:19 -0700 Subject: Zaps: the sound, the sender, and one action bar for every post. Tap feedback: a bolt strike, the amount popping off the button, the post trembling, and the zap.mp3 clip. The clip is 62KB of base64 and messages.js is boot code, so it is its own chunk, fetched once a zappable post is on screen and played inside the tap once loaded; the standalone build inlines it. Ark zap receipts are signed by the wallet key, which has no profile: they now carry the login identity as the sender (NIP-57's P tag), readers prefer it, and our own older receipts read as us. The action bar is the same six slots on every post: the zap button and the who-reacted chevron are always there (inert until they have something to do), so the first zap no longer reflows the row, and you can zap your own posts and messages. Community rows centre their unread dot off the edge. Also in here: reactions with custom emoji, reply-composer focus under the tapped post, profile fetch retry, and Lightning zap failures that say so. Co-Authored-By: Claude Fable 5.1 --- src/style.css | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'src/style.css') diff --git a/src/style.css b/src/style.css index 3fd3de2..3629dfc 100644 --- a/src/style.css +++ b/src/style.css @@ -499,6 +499,43 @@ hr.divider { margin: 16px 0; } .zap-tally { display: inline-flex; align-items: center; gap: 2px; font-size: 11px; font-weight: 650; color: #e6a800; white-space: nowrap; flex-shrink: 0; } .zap-tally.chat-react { font-size: 12px; padding: 1px 7px 1px 5px; } .zap-tally.clickable { cursor: pointer; } +/* A tap-local lightning strike, followed by a floating amount and embers. */ +.zap-fx { position: fixed; inset: 0; z-index: 10000; pointer-events: none; overflow: hidden; contain: strict; } +.zap-fx-impact { position: absolute; left: var(--zap-x); top: var(--zap-y); width: 0; height: 0; color: #ffc83d; } +.zap-fx-bolt { position: absolute; width: 82px; height: 180px; left: -41px; bottom: 0; fill: #fff5c2; filter: drop-shadow(0 0 5px #fff3a1) drop-shadow(0 0 13px #ff9d00); transform-origin: 50% 100%; animation: zap-strike 430ms ease-out both; } +.zap-fx-flash, .zap-fx-ring { position: absolute; left: -28px; top: -28px; width: 56px; height: 56px; border-radius: 50%; animation: zap-impact 550ms 60ms ease-out both; } +.zap-fx-flash { background: radial-gradient(circle, #fffce0, #ffca45 25%, #ff980066 50%, transparent 70%); } +.zap-fx-ring { border: 2px solid #ffd76a; box-shadow: 0 0 12px #ffad32, inset 0 0 10px #ffad3266; animation-name: zap-ring; } +.zap-fx-amount { position: absolute; left: var(--zap-label-x); top: -60px; display: flex; align-items: baseline; gap: 6px; white-space: nowrap; color: #fff2ad; text-shadow: 0 2px 2px #713500, 0 0 9px #ff9c00, 0 0 22px #ff9500aa; animation: zap-amount 1450ms cubic-bezier(.16, 1, .3, 1) both; } +.zap-fx-amount strong { font-size: clamp(34px, 9vw, 48px); font-weight: 900; line-height: 1; letter-spacing: -.045em; } +.zap-fx-amount span { font-size: 13px; font-weight: 800; letter-spacing: .04em; } +.zap-fx-spark { position: absolute; width: 10px; height: 3px; left: -5px; top: -2px; border-radius: 3px; background: #ffe997; box-shadow: 0 0 6px #ffaf20; animation: zap-spark 720ms var(--delay) ease-out both; } +.zap-fx-spark.ember { left: calc(var(--zap-label-x) - 2px); top: -80px; width: 3px; height: 3px; background: #fff4c2; animation-duration: 750ms; } +@keyframes zap-strike { + 0% { opacity: 0; transform: scaleY(.15); } + 16%, 38% { opacity: 1; transform: scaleY(1); } + 65% { opacity: .8; } + 100% { opacity: 0; transform: scaleY(1.04); } +} +@keyframes zap-impact { 0% { opacity: 1; transform: scale(.2); } 100% { opacity: 0; transform: scale(2.4); } } +@keyframes zap-ring { 0% { opacity: .9; transform: scale(.25); } 100% { opacity: 0; transform: scale(2); } } +@keyframes zap-amount { + 0% { opacity: 0; transform: translate(-50%, 35px) scale(.35) rotate(-9deg); } + 16% { opacity: 1; transform: translate(-50%, -8px) scale(1.2) rotate(-5deg); } + 30% { opacity: 1; transform: translate(-50%, -4px) scale(1) rotate(-3deg); } + 58% { opacity: 1; filter: blur(0); } + 100% { opacity: 0; transform: translate(-50%, -65px) scale(1.08) rotate(2deg); filter: blur(3px); } +} +@keyframes zap-spark { + 0% { opacity: 0; transform: translate(0, 0) rotate(var(--angle)) scale(.4); } + 12% { opacity: 1; } + 100% { opacity: 0; transform: translate(var(--dx), var(--dy)) rotate(var(--angle)) scale(.1); } +} +@media (prefers-reduced-motion: reduce) { + .zap-fx-bolt, .zap-fx-flash, .zap-fx-ring, .zap-fx-spark { display: none; } + .zap-fx-amount { animation: zap-amount-reduced 1200ms ease-out both; } + @keyframes zap-amount-reduced { 0%, 65% { opacity: 1; transform: translateX(-50%); } 100% { opacity: 0; transform: translateX(-50%); } } +} /* A zap in flight: the chip lands the moment you tap and breathes until the payment settles — the bolt carries the pulse, the number stays readable. */ .zap-tally.flying { animation: zap-breathe 1.15s ease-in-out infinite; } @@ -529,6 +566,7 @@ hr.divider { margin: 16px 0; } .chat-preview { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .thread-when { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; } .thread-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--red); flex-shrink: 0; } +.chat-thread-row > .thread-dot { margin-right: 8px; } .chat-thread-row.unread .chat-name { font-weight: 700; } .chat-thread-row.unread .chat-preview { color: var(--ink); } .chat-invite { padding: 10px 12px; border-bottom: 1px solid var(--line); background: var(--surface2); flex: 0 0 auto; max-height: 40vh; overflow-y: auto; } @@ -574,6 +612,8 @@ hr.divider { margin: 16px 0; } .note-act-emoji { font-size: 17px; line-height: 1; } .note-act-n { font-size: 12px; font-weight: 600; color: var(--muted); } .note-who-toggle.on { color: var(--muted); } +.note-who-toggle[aria-disabled] { opacity: .3; cursor: default; } +.note-who-toggle[aria-disabled]:hover { background: none; color: var(--faint); } .note-who-chev { display: flex; transition: transform .2s; } .note-who-chev.open { transform: rotate(180deg); } /* who reacted, boosted and zapped: opened by the chevron on the bar */ -- cgit v1.2.3