Skip to content

Airdrop chart: rebuild as time-series area chart (ultrasound.money style) #1048

@realproject7

Description

@realproject7

Why this ticket exists

The /airdrop chart has been re-implemented 5+ times since #924 (see #930, #937, #1009, #1014, #1038, #1047). Each iteration fixed the previous symptom but kept the same broken concept: a one-dimensional progress gauge with milestone targets on the X-axis.

When current MCap ($31K) sits well below the first milestone ($1M), this concept renders as an empty rectangle with a dot pinned to the left edge — which is the current production state. No amount of scale tweaking (linear / log / fixed-position) fixes that, because the X-axis itself is wrong for the data.

The benchmark the team keeps pointing at (ultrasound.money "ETH supply—2y projection") is a time-series chart, not a progress gauge. This ticket replaces the current gauge with a true time-series chart matching that shape.

Out of scope (do NOT do these)

  • Do not iterate on `MCapChart` in `CampaignHero.tsx`. Replace it.
  • Do not put MCap on the X-axis.
  • Do not reuse `MILESTONE_POS` / `mcapToX` (these are the old gauge concept).
  • Do not add yet another scale option (linear vs log vs fixed). The new chart's X is time, not MCap.

Spec

Axes

  • X-axis: time. Domain = `campaignStart` → `campaignEnd` from `/api/airdrop/status`. Tick labels at appropriate intervals (day labels for test mode, month labels for prod).
  • Y-axis: MCap on log scale, domain = `[$1K, $100M]` (or adapt so test mode thresholds also fit — see Airdrop: MilestoneTrack uses hardcoded FDV targets instead of config #1004 for config-driven precedent).
  • Y-axis labels visible on desktop only (`sm:` breakpoint), hidden on mobile.

Series

  1. Historical MCap line (solid, accent color, area fill below): from `/api/airdrop/daily-prices` (route exists at `src/app/api/airdrop/daily-prices/route.ts`, returns `[{ date, fdv }]`). Plotted from campaign start to today.
  2. Linear projection line (dashed, accent-dim color): straight line from `(campaignStart, startingFdv)` to `(campaignEnd, $100M)`. This is "the constant growth rate needed to hit Diamond."
  3. Current MCap dot (heartbeat animation, inside SVG as ``): at `(now, currentFdv)`. Because X=time, the dot sits at today's horizontal position regardless of MCap value — never pinned to the chart edge as long as time has elapsed.

Milestone markers

  • 4 horizontal dashed lines at Y = $1M / $10M / $50M / $100M.
  • Each line labeled at the right edge with: `$1M — unlocks 10% (≈ #1900)` etc.
  • Lines and labels use accent color at 50% opacity (they are reference, not data).

Colors

  • Use CSS custom properties only. No hardcoded hex.
  • Accent: `var(--accent)` (= `#8B4513` per `globals.css:10`).
  • Accent-dim (for projection / reference lines): `var(--accent-dim)` (= `#6B3410` per `globals.css:11`).
  • Text: `var(--color-foreground)` / `var(--color-muted)`.
  • Forbidden: `#00ff88`, `#8B4513` literal, any inline hex. (Past PRs hardcoded these — Airdrop chart: fix label overlap, scale, and accent color #1043 attempted a fix but only partially.)

Mobile (< 640px)

  • Same chart, but:
    • Hide Y-axis tick labels (chart still renders against log Y).
    • Move milestone labels from right-edge inline → 2×2 legend grid below the chart (pattern from current code, keep this).
    • Min-height 160px, no horizontal scroll.

Empty-state

  • If `pl_daily_prices` returns no rows: still render the chart frame, projection line, milestone lines, and current-dot at `(now, currentFdv)`. Don't fall back to a non-chart view.

Files

  • Replace `MCapChart` component in `src/components/airdrop/CampaignHero.tsx`. Keep the surrounding `CampaignHero` shell — only the chart guts change.
  • Use `/api/airdrop/daily-prices` route (confirmed existing, returns `[{ date, fdv }]` from `pl_daily_prices` table).
  • Drive milestone values from `data.milestones` (config-driven, per Airdrop: MilestoneTrack uses hardcoded FDV targets instead of config #1004), not hardcoded `MILESTONES` constant.
  • Delete `MILESTONE_POS`, `mcapToX`, `ACCENT` constants — all vestiges of the gauge concept.

Acceptance criteria

  • Chart X-axis is time, not MCap. Verifiable: change `currentFdv` mock to $0 and to $200M — dot moves vertically only, never to chart edges horizontally.
  • Current dot sits inside the chart at production FDV ($31K) — visible against the Y-axis around the bottom portion of the chart, not pinned to any edge.
  • All 4 milestones render as horizontal reference lines with right-edge labels (desktop) or below-chart legend (mobile).
  • No hex literals in chart code — `grep '#[0-9a-fA-F]\{3,6\}'` on the chart section should return nothing.
  • Test mode (`NEXT_PUBLIC_AIRDROP_MODE=test`) still works — Y-axis adapts to test thresholds.
  • No new horizontal scroll on 320px viewport.
  • PR description includes a screenshot at production FDV (~$31K) clearly showing the dot inside the chart.

Reviewer note for @T2A / @t2b

Block this PR if it:

  • (a) keeps any form of "MCap on X-axis"
  • (b) reintroduces `MILESTONE_POS` / `mcapToX` / similar piecewise-linear gauge math
  • (c) hardcodes any accent hex anywhere
  • (d) "fixes" the milestone-label-overlap problem (it can't exist on this design — labels live on horizontal lines, not above the chart)

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/T3Assigned to T3 builder agent

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions