Files
zeekay 0cbf9dffb3 feat: glossy pass — live activity ticker, count-up, monochrome gloss
Adds real social proof and a glossy depth pass on top of the existing
brand-neutral monochrome. Every new piece is configurable end-to-end.

Activity feed

  • Mock-api: GET /v1/waitlist/activity?waitlist=&limit=&types= returning
    most-recent events with a server-now timestamp so clients render
    consistent relative times across clock skew. Ring-buffer last 200
    events (env override: ACTIVITY_MAX_HISTORY). Cap on limit: 100
    (env: ACTIVITY_LIMIT_CAP). Types filter (join,share,invite,referral)
    is server-validated, unknown types silently dropped.
  • join handler pushes a `join` event + a `referral` event when the
    sender's refCode resolves. track-share pushes a `share` event the
    same day-it-was-credited (skipped on duplicate same-day to match
    the daily-cap economy). invite pushes one event per accepted email.
  • Seeder synthesizes 30 backdated events on first boot so the ticker
    is alive on first paint, not waiting on real traffic.
  • Widget: <WaitlistActivity waitlist baseUrl limit types pollInterval
    formatEvent formatRelative showHeading heading emptyLabel theme
    className style /> — every visible knob is a prop. Polling interval
    is configurable (0 disables, default 12s). Default formatter handles
    copy/webshare/email/sms with natural copy ("copied their link",
    "shared via system", "shared by email", "texted their link"); other
    platforms get a clean platformLabel mapping (twitter→X, etc.).

Animated count-up

  • useCountUp(target, { duration, ease, retrigger }) hook — 1200ms
    cubic-out by default, honors prefers-reduced-motion, 0 disables.
  • <WaitlistLeaderboard animateTotal={true | false | { duration }}/>
    runs the count-up on the "N on the list" subtitle when the meta
    first lands. Truly off-switchable.

Glossy aesthetic (all monochrome — no new color tokens)

  • Cards: multi-layer shadow + a 1px inner highlight on the top edge
    using inset color-mix(--hw-bg). Reads as real material without
    breaking neutrality.
  • Leaderboard rows: 6px radius, translateX(2px) on hover (skipped
    under prefers-reduced-motion).
  • Activity pulse: 8px accent dot with a soft expanding ring keyframe
    (1.8s loop). Reduced-motion turns it off.
  • Demo hero: subtle radial gradient halo via .section--glow::before
    using color-mix(--fg 6%) to stay neutral. Disabled when the user
    prefers reduced transparency.
  • Topbar Join pill: inset top highlight + accentuated drop shadow
    on hover for that glossy CTA look.
  • Two-column board grid on desktop (leaderboard + Live activity),
    stacks on <=880px.
  • OpenGraph + Twitter card metadata on the demo for proper social
    previews when someone tweets the page URL.
2026-06-10 13:32:08 -07:00

26 lines
720 B
TypeScript

import './globals.css'
import '@hanzo/waitlist/styles.css'
export const metadata = {
title: 'Hanzo · Waitlist',
description: 'Reserve your spot. Climb the list. Refer friends, share, invite — every action earns points.',
openGraph: {
title: 'Hanzo · Waitlist',
description: 'Reserve your spot. Climb the list — every action earns points.',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'Hanzo · Waitlist',
description: 'Reserve your spot. Climb the list — every action earns points.',
},
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}