Skip to content

Commit 58ea113

Browse files
ux: reveal Buy me a coffin pill on scroll, not on page load
The pill was visible from the top of every page, which felt like it appeared "from nowhere" and pulled attention away from the hero. Now starts hidden (opacity 0, translateY -8px, pointer-events none), fades in once the user has scrolled past ~180px. Scroll listener is passive and unmounts cleanly. aria-hidden + tabIndex track visibility so the link does not get focused or read by assistive tech while it is hidden. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 2695437 commit 58ea113

2 files changed

Lines changed: 29 additions & 4 deletions

File tree

src/app/globals.css

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,8 @@ a.subpage-inline-mail {
575575
}
576576

577577
/* Floating top-right "Buy me a coffin" button — sits just below the ScannerBanner.
578-
Sharp corners to match the morgue/gothic theme (no rounded pill shapes). */
578+
Sharp corners to match the morgue/gothic theme. Hidden until the user scrolls
579+
past the hero, then fades in. */
579580
.coffin-pill {
580581
position: fixed;
581582
top: 44px;
@@ -595,9 +596,17 @@ a.subpage-inline-mail {
595596
border-radius: 0;
596597
text-decoration: none;
597598
box-shadow: 2px 2px 0 var(--c-ink);
598-
transition: transform 0.1s, box-shadow 0.1s, background 0.15s, color 0.15s;
599+
transition: opacity 220ms ease, transform 220ms ease, background 0.15s, color 0.15s, box-shadow 0.1s;
599600
-webkit-tap-highlight-color: transparent;
600-
animation: section-fade-up 600ms ease-out both;
601+
opacity: 0;
602+
transform: translateY(-8px);
603+
pointer-events: none;
604+
}
605+
606+
.coffin-pill--visible {
607+
opacity: 1;
608+
transform: translateY(0);
609+
pointer-events: auto;
601610
}
602611

603612
.coffin-pill:hover {

src/components/CoffinPill.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
'use client'
22

3+
import { useEffect, useState } from 'react'
34
import { track } from '@vercel/analytics'
45

6+
const REVEAL_AT = 180 // px scrolled before the pill fades in
7+
58
export default function CoffinPill() {
9+
const [visible, setVisible] = useState(false)
10+
11+
useEffect(() => {
12+
function handleScroll() {
13+
setVisible(window.scrollY > REVEAL_AT)
14+
}
15+
handleScroll()
16+
window.addEventListener('scroll', handleScroll, { passive: true })
17+
return () => window.removeEventListener('scroll', handleScroll)
18+
}, [])
19+
620
return (
721
<a
822
href="https://buymeacoffee.com/dotdevs"
923
target="_blank"
1024
rel="noopener noreferrer"
1125
aria-label="Buy me a coffin — support the morgue"
26+
aria-hidden={!visible}
27+
tabIndex={visible ? 0 : -1}
1228
onClick={() => track('buy_me_a_coffin_clicked', { from: 'top_right_pill' })}
13-
className="coffin-pill"
29+
className={`coffin-pill${visible ? ' coffin-pill--visible' : ''}`}
1430
>
1531
<span aria-hidden style={{ fontSize: '13px', lineHeight: 1 }}></span>
1632
<span className="coffin-pill-text">Buy me a coffin</span>

0 commit comments

Comments
 (0)