TimeCurve Simple view: aggressive simplification pass — buy-button-first UX with worldbuilding intact (review checklist) #62

Closed
opened 2026-04-24 08:18:40 +00:00 by PlasticDigits · 0 comments
PlasticDigits commented 2026-04-24 08:18:40 +00:00 (Migrated from gitlab.com)

Context

The /timecurve Simple view (TimeCurveSimplePage) has been through one full design pass (issue #44) and an asset wiring pass (issue #45 → #57) plus several iterative UX rounds. The page already hosts only a TimeCurveSubnav, the timer + buy "sale hub", a PageHero strip, an optional "Your stake at launch" panel, and a 3-row "Recent buys" ticker. The global footer is hidden on this route.

It is still too dense for the canonical persona we are designing for: a first-time visitor who wants to buy CHARM the way they would buy any token in a normal sale, without learning the curve, the launch anchor, the timer-extension rules, or the difference between Simple / Arena / Protocol.

This issue is the review and aggressive-simplification pass for that persona, with one explicit constraint: the worldbuilding and design philosophy are non-negotiable — the arcade leprechaun-bunny mascot system, the deep-green / gold "stickered playing card" surface, the Bungee/Plex Mono numerals, and the custom cursors are part of what makes Yieldomega Yieldomega and have to survive the cuts.

Tension statement we are explicitly resolving:
Maximum simplicity for "I just want to buy" should win at the information layer (less to read, fewer choices, defaults that just work), while worldbuilding should win at the presentation layer (mascots, scene art, cursors, fonts, gold-glow CTA, hard shadows). Cuts that would strip personality to win 50ms of cognition cost are not in scope.

Authoritative references that frame this work (must read before checking off items):

Baseline commit at the time this issue was written: 88e4eb6.


1. Research foundation — simplicity principles to apply

Every checklist item below is grounded in one of these established UX/UI principles. Skim the named principle when an item is unclear; do not re-derive a rationale from scratch.

  • 1.1 Steve Krug, Don't Make Me Think — three laws: (a) we don't read pages, we scan them; (b) we don't make optimal choices, we satisfice; (c) we don't figure out how things work, we muddle through. Applied: hero copy must be scan-able, default values must be safe to satisfice on, the buy flow must be muddle-through-able by a wallet-only user.
  • 1.2 Jakob Nielsen, 10 Usability Heuristics — visibility of system status, match real-world language, user control, consistency, error prevention, recognition over recall, flexibility, minimalist design, help users recognize/recover from errors, help/documentation. Applied: every visible block on the page is justified against minimalist design (#8) before it ships.
  • 1.3 Hick-Hyman law — decision time scales with log2(n+1) choices. Applied: count the visible decisions in the buy flow; we want ≤ 3 before "Buy CHARM" tap.
  • 1.4 Fitts' law — acquisition time scales with log2(D/W + 1). Applied: primary CTA gets the largest touch target on the page; the slider thumb gets a custom cursor and adequate hit area.
  • 1.5 Edward Tufte, data-ink ratio — maximize information per non-decoration pixel. Applied: each panel justifies its non-data chrome (badges, ledes, padding) against the information it conveys.
  • 1.6 Cognitive Load Theory (Sweller) — separate intrinsic / extraneous / germane load. Applied: jargon ("clearing price", "1.2× anchor", "endSale()") is extraneous for the buy persona and gets disclosed-not-displayed; the timer urgency is intrinsic and stays loud.
  • 1.7 F-pattern reading + above-the-fold convention — first eye-stop must contain the headline answer. Applied: the timer + buy CTA must both fit the first viewport at ≥ 1280×900 desktop and 390×844 mobile.
  • 1.8 Apple HIG — clarity, deference, depth. Applied: chrome defers to content; depth (shadows, glow) communicates hierarchy not decoration.
  • 1.9 Google Material — bold, graphic, intentional motion. Applied: motion is reserved for state change (pulse on critical timer) and never as ambient noise.
  • 1.10 Refactoring UI (Wathan & Schoger) — hierarchy via size/weight/color (not borders), depth via shadow, color via HSL families. Applied: no new borders to separate things; reach for size/weight first.
  • 1.11 Don Norman, Design of Everyday Things — defaults are the most important UI element. Applied: pay-with defaults to CL8Y, slippage to a sane bps, slider to the live mid-price.
  • 1.12 Progressive disclosure — power-user controls behind <details> or modal. Applied: pay-with picker, slippage, referral, balance row already in <details>; verify nothing else leaks above it.

2. Worldbuilding & design philosophy — non-negotiable preserves

Every cut below must survive these. If a simplification proposal would damage one of these, it is rejected.

2.1 Brand voice

  • 2.1.1 Tone is arcade game / treasure hunt, not "DeFi protocol". Examples that must survive: "Every buy adds 2 minutes; clutch buys hard-reset the clock.", "Spend CL8Y now — every block waited makes CHARM cost more."
  • 2.1.2 Sentences with personality — verbs that move ("clutch", "hard-reset", "ticks up"), not corporate hedges ("may be subject to", "please be aware").
  • 2.1.3 Lore acknowledged in copy where it matters (e.g. "1.2× per-CHARM clearing price" in the rate board) — but never as the primary thing a buy persona must read.

2.2 Mascot, motif, and scene system

  • 2.2.1 At least one mascot/cutout or scene image visible in the first viewport. Today: scenes/timecurve-simple.jpg (low-opacity backplate behind the timer hero). Acceptable. Not acceptable: stripping it for a flat green panel.
  • 2.2.2 Custom cursors stay wired on the primary CTA (/art/cursors/primary-cta.png) and the slider thumb (/art/cursors/slider-grab.png) at @media (hover: hover) breakpoints.
  • 2.2.3 PageBadge icons (status-{live, prelanch, ended, cooldown, etc.}) stay; they are the cheapest worldbuilding pixels on the page.

2.3 Color, font, art-style invariants

  • 2.3.1 Arcade green family (--bg-deep, --card, the linear-gradient(165deg, #122e1d → #1d4329 → #2c5d3f) timer stage) is the dominant page color.
  • 2.3.2 Gold (--arcade-gold-200/400/600) is used only for: the primary CTA cursor/style, the timer hero days chip, the Simple-tab pill in the subnav, and the rate board's at-launch row. No gold creep into secondary chrome.
  • 2.3.3 Hard 4–5px offset shadows preserved on cards and chips ("stickered playing card" feel). No drift to soft material shadows.
  • 2.3.4 Display font (Bungee) reserved for big numerals, days chip, and wordmark; body font (Fredoka / system) for narrative; mono font (IBM Plex Mono) for addresses, tx hashes, and explicit code spans.
  • 2.3.5 font-variant-numeric: tabular-nums on every live-updating number on the page (timer digits, rate-board prices, stake values). Already true today; keep it true.

3. Section-by-section component review

For every section currently rendered by TimeCurveSimplePage, decide: keep / simplify / move-to-disclosure / remove. Every "remove" must justify against §2.

3.1 TimeCurveSubnav (Simple / Arena / Protocol)

  • 3.1.1 Keep. It is the only cross-page nav on the page since the "Want more?" tile row was removed.
  • 3.1.2 Confirm the Simple tab is visually obvious as "you are here" without reading the label (gold pill vs. white).
  • 3.1.3 Audit: at narrow widths, can a buy-persona user still see the subnav and not assume the page is broken? (Non-buy persona's escape hatch.)

3.2 Sale hub — Timer panel (.timecurve-simple__timer-panel)

  • 3.2.1 Keep. Timer is the single most important affordance on the page (it justifies why you should buy now).
  • 3.2.2 Header lede (currently phaseNarrative() output) — review: is one sentence sufficient? Can we cut the header lede entirely on Simple and let the timer-hero foot line do the talking? The PageSection header eyebrow ("TIME REMAINING") + title ("TIME LEFT") may be redundant with each other.
  • 3.2.3 Timer hero foot copy stays: "Every buy adds 2 minutes; clutch buys hard-reset the clock." This is the only place on the Simple page that explains why the timer is interesting; it earns its real estate.
  • 3.2.4 Days chip (Nd) only appears when secondsRemaining ≥ 86400. Confirmed in TimeCurveTimerHero.tsx. No change.
  • 3.2.5 Decide: do we keep BOTH a panel-level PageBadge ("TIME REMAINING") AND a panel title ("TIME LEFT")? At least one is redundant.

3.3 Sale hub — Buy CHARM panel (.timecurve-simple__buy-panel)

This panel is where almost all the simplification work lives.

  • 3.3.1 Live rate board — keep. The current price (big, fixed-6) is the page's #2 most-glanced number. Audit: is the at-launch row still earning its space for a buy-only persona, or should it move into <details> ?
  • 3.3.2 Min–max pill ("Live band X – Y CL8Y") — review: a buy persona doesn't pick the bounds, the slider does. Consider hiding the literal numbers and only showing them inside the Advanced disclosure. The visual band on the slider track communicates the bounds visually.
  • 3.3.3 Slider + numeric input — keep slider, audit numeric input. A buy persona almost never types an exact CL8Y amount; the numeric input is a power-user override. Consider moving the numeric input into the Advanced disclosure (or using a small inline "type exact amount" link to reveal it).
  • 3.3.4 Buy preview ("You add X CHARM" + "Worth at launch ≈ Y CL8Y") — keep. This is the buy persona's "did I dial in the right amount" check.
  • 3.3.5 Buy preview footnote — currently text-only; consider replacing/augmenting with a small hat-coin-front.png glyph next to the CL8Y figure.
  • 3.3.6 Buy CHARM CTA — keep. Already the largest button on the page. Verify it owns the primary-cta.png cursor and the gold-glow class.
  • 3.3.7 Cooldown line — keep, but only when walletCooldownRemainingSec > 0. Already gated. No change.
  • 3.3.8 Error line — keep with dismiss button. Already gated. Audit: does the dismiss button stand out enough that a panicked user can find it in 1 second?
  • 3.3.9 <details> Advanced disclosure — keep. Audit each item inside it (pay-with, slippage, referral, wallet balance) for whether it should even be on the Simple view at all (vs. living only on /timecurve/arena). For the buy persona, every advanced control we move off the Simple page is a win.
  • 3.3.10 Disconnected state CTA — currently "Connect a wallet to buy CHARM and lock in your share of the DOUB launch." + <WalletConnectButton />. Keep both, but verify the copy is the first thing read (above the rate board) when no wallet is connected. Today the rate board renders only in saleActive, so the order is fine; double-check on saleStartPending and saleEnded.

3.4 PageHero (title + lede + chain-time deadline)

  • 3.4.1 Title "TimeCurve sale" — review: is this redundant with the subnav active tab ("Simple — Time left + buy CHARM") and the panel titles? Candidate to remove entirely on Simple, keeping the badge + lede only.
  • 3.4.2 HERO_LEDE copy — review against §2.1; trim to one sentence if it has grown to two.
  • 3.4.3 coinSrc="/art/token-logo.png" — keep, it is a worldbuilding pixel (§2.2).
  • 3.4.4 headerContent (chain-time deadline) — review: a buy persona reads the timer for "when". The chain-time deadline is operator-tier info. Consider moving into the timer hero foot OR into the Advanced disclosure OR removing entirely on Simple.

3.5 "Your stake at launch" panel (gated on walletConnected && charmWeightWad)

  • 3.5.1 Keep. This is the page's answer to "what did my CHARM buy me?", and it is gated to wallets that already hold CHARM, so a fresh visitor never sees it.
  • 3.5.2 Title + badge ("Your stake at launch" + "1.2× launch anchor") — review: badge is jargon. Consider replacing with a smaller "Updates each block" eyebrow or removing.
  • 3.5.3 Lede paragraph — currently 2 sentences with <strong> highlights. Trim to 1 sentence ("Your CHARM is worth at least this much in CL8Y when DOUB lists.") for the buy persona; full lore stays in docs/frontend/timecurve-views.md.
  • 3.5.4 Stake foot ("enforced by DoubLPIncentives; see the Protocol view…") — review: keep the link to Protocol (escape hatch for power users), but drop the contract name from the visible string (it is meaningless to the buy persona).

3.6 "Recent buys" ticker

  • 3.6.1 Audit: does this ticker move someone closer to "I want to buy"? It's social proof, which is useful — but at the cost of one more panel below the fold. Decide: keep as-is, shrink to 1–2 rows, or move behind a "Recent activity" toggle.
  • 3.6.2 Empty state ("Waiting for the first buy of this round.") — consider replacing the text-only placeholder with a small cutouts/cutout-bunnyleprechaungirl-playful.png mascot for personality.
  • 3.6.3 Each row currently shows: short address · amount · +Ns extension OR "hard reset" · tx link. The "hard reset" / +Ns column is lore-y; for a buy persona it is opaque. Decide: keep (because it visually rewards big buys), or simplify to a small icon (e.g. lightning glyph for hard reset).
  • 3.7.1 Already hidden on /timecurve only via RootLayout.showFooter. Confirm still hidden after this pass and that operator info is one click away (Protocol view).

3.8 Header / RootLayout chrome (brand, primary nav, chain pill, wallet button)

  • 3.8.1 Out of scope for this issue (shared shell), but must not regress: chain pill, wallet ConnectButton, brand wordmark, and the cross-app nav stay visible and functional.

4. Common simplicity heuristics — checklist (3 levels)

4.1 Information hierarchy

  • 4.1.1 Single primary CTA per page. "Buy CHARM" is the only btn-primary--priority on the Simple view. Verify by rg "btn-primary--priority" frontend/src/pages/TimeCurveSimplePage.tsx (must return 1 hit, on the buy/redeem button).
  • 4.1.2 Inverted pyramid. First viewport = timer + buy CTA. Second viewport = stake panel (when applicable) + recent buys. Third viewport = nothing.
  • 4.1.3 Visual weight matches semantic weight. Largest text on the page is the timer digits. Second-largest is the live current-price. Buy CTA is the largest button. Anything currently larger than its semantic role gets demoted (font-size, weight, or color).

4.2 Cognitive load reduction

  • 4.2.1 Reduce simultaneous visible decisions. Count visible interactive controls in the saleActive + walletConnected first viewport: today = slider + advanced toggle + buy CTA = 3. Goal: stay ≤ 3.
  • 4.2.2 Progressive disclosure. Pay-with, slippage, referral, wallet balance must remain inside <details>. Add an audit grep: rg "session.payWith" frontend/src/pages/TimeCurveSimplePage.tsx | wc -l should match what's inside the <details> block, no more.
  • 4.2.3 Plain language above the fold. No occurrences of: WAD, wei, bps, revertReason, endSale(), contract names. Run rg "WAD|wei\\b|bps|revertReason|endSale|DoubLPIncentives|TimeCurve\\.sol" frontend/src/pages/TimeCurveSimplePage.tsx and confirm hits are inside <details>, comments, or test-ids only.

4.3 "I just want to buy" decision support

  • 4.3.1 No financial calculations required of the user. They never multiply, never convert, never look up. The page does the math (rate board + buy preview).
  • 4.3.2 Defaults that work for 90% of buyers. Slider lands at the live midpoint, pay-with defaults to CL8Y, slippage defaults to a sane bps. Verify defaults in useTimeCurveSaleSession.
  • 4.3.3 Confirm-and-go, not configure-then-go. From a connected wallet: drag slider once → tap Buy CHARM → wallet pops the tx. Anything between drag and tap is a leak.

4.4 Trust signals (smaller surface, same strength)

  • 4.4.1 Chain identity visible in header chain pill at all times.
  • 4.4.2 Transparent pricing — the rate board shows current and at-launch prices live; no dark-pattern hidden cost between the slider and the wallet pop.
  • 4.4.3 Onchain provenance. Recent buys link to explorer via TxHash. Keep.

4.5 Accessibility & inclusivity

  • 4.5.1 Touch targets ≥ 44×44 pt (Apple HIG) on the buy CTA, slider thumb, advanced toggle, recent-buys tx link.
  • 4.5.2 Contrast ≥ 4.5:1 for body, ≥ 3:1 for large text. Verify with axe / pa11y on /timecurve against the deep-green panels.
  • 4.5.3 Reduced motion — sparks animation, critical timer pulse, button hover transforms all gated behind @media (prefers-reduced-motion: reduce). Verify timer hero, button motion, and any new animations introduced by this pass.
  • 4.5.4 Live regions — timer + rate board + cooldown + error already use aria-live="polite". Verify after any layout reorder.
  • 4.5.5 Reading level of all above-the-fold copy ≤ 8th grade (Hemingway / readable.com check).
  • 4.5.6 Keyboard order — tab from the wallet button reaches Subnav → Buy CTA → Advanced toggle → (open) Pay-with → … in logical order.

4.6 Performance & speed perception

  • 4.6.1 Loading skeletons match real layout (no jumping between Loading sale state… and the live numbers).
  • 4.6.2 Critical art preloaded — scenes/timecurve-simple.jpg already uses loading="eager". Add fetchPriority="high" if not already set.
  • 4.6.3 Secondary art lazy — recent-buys mascot (if added per §8.2.3), stake panel art, etc., use loading="lazy" and decoding="async".
  • 4.6.4 Time-to-interactive on Buy CHARM ≤ 2s on a cold load over a throttled 4G profile (Lighthouse).

5. Theming checklist (3 levels)

5.1 Color tokens

  • 5.1.1 Arcade green palette — page surfaces use var(--bg-deep), var(--card), var(--ink), var(--text-main). No new hex literals introduced for surface colors.
  • 5.1.2 Gold accent palette — var(--arcade-gold-200/400/600) reserved for: primary CTA, days chip, Simple-tab pill, at-launch rate row. Audit: rg "arcade-gold" frontend/src/index.css and confirm no creep.
  • 5.1.3 Urgency palette — yellow under timer-hero--warning, red under timer-hero--critical. Only on the timer hero. Confirm no other component uses these urgency hues.
  • 5.1.4 Text contrast pairs — body text on --card ≥ 4.5:1, white on gold-CTA ≥ 4.5:1, white on green-spotlight ≥ 4.5:1.

5.2 Typography

  • 5.2.1 Display face (Bungee) used only for: brand wordmark, timer digits, days chip number. Audit: rg "Bungee" frontend/src/index.css.
  • 5.2.2 Body face (Fredoka / system stack) for narrative, badges, ledes.
  • 5.2.3 Mono face (IBM Plex Mono) for addresses, tx hashes, explicit <code> spans, and the rate-board numerals where digit alignment matters.
  • 5.2.4 Tabular numerals on every live-updating number — already enforced in the timer hero CSS; confirm the rate board's price values also opt in (they should, via font-variant-numeric: tabular-nums on the parent or the value class).
  • 5.2.5 Line-height body ≥ 1.45, hero ≥ 1.0 (digits) / 1.45 (foot).

5.3 Spacing & rhythm

  • 5.3.1 Base grid — multiples of 0.25rem (4px). No padding / margin off-grid.
  • 5.3.2 Fluid spacing — clamp(min, vw, max) for every padding that has to scale across desktop and mobile (already used in the timer hero; verify Buy panel uses the same scale).
  • 5.3.3 Vertical rhythm between sections: 0.5rem (chip-to-chip), 0.85rem (label-to-value), 1.25rem (panel-internal sections), 2rem (panel-to-panel). Anything off these increments needs justification or correction.

5.4 Surface & depth

  • 5.4.1 data-panel base — white card with hard offset shadow. Used by stake + recent buys panels.
  • 5.4.2 data-panel--spotlight — gold-glow border + accent. Used by timer + buy panels (the two spotlight items above the fold). Confirm exactly two data-panel--spotlight on this page.
  • 5.4.3 Nested cards inside a panel max 1 level deep. Today: rate board nests inside the buy panel (1 level), buy preview nests inside the buy panel (1 level). No 2-level nesting allowed.

6. Style checklist — CSS hygiene (3 levels)

6.1 Naming

  • 6.1.1 BEM — every new class follows .timecurve-simple__<block>__<element>--<modifier>. Audit: rg "class.*timecurve-simple" frontend/src/pages/TimeCurveSimplePage.tsx should not surface ad-hoc unprefixed classes.
  • 6.1.2 No utility-class explosion — we are not Tailwind. Compose with semantic class names + variables.
  • 6.1.3 data-testid parity — every visible block that has e2e or vitest coverage keeps its data-testid; removed blocks remove their data-testid from the spec files.

6.2 Layout primitives

  • 6.2.1 CSS Grid for page-level structure (the hub is a grid).
  • 6.2.2 Flexbox for component-internal flow (rate board rows, timer clock).
  • 6.2.3 Container queries for hub-style responsive behavior, with @media fallback. Already in place after the timer-hero pass — confirm no new container queries introduced that try to query their own container.

6.3 Animations & motion

  • 6.3.1 Every @keyframes paired with a @media (prefers-reduced-motion: reduce) killswitch.
  • 6.3.2 No animation > 4s loop on the primary fold (sparks rise is ~4.2s — at the edge, accept).
  • 6.3.3 Transition duration ≤ 200ms for input feedback (button press, hover).
  • 6.3.4 No autoplaying scene-art animation; backplate stays static.

6.4 Design-token discipline

  • 6.4.1 Use var(--*) rather than hex literals for any color reused 2+ times on the page.
  • 6.4.2 No style={{...}} literal values in JSX (acceptable: style={{ ["--fx-i" as string]: i }} for keyframe-staggering).

6.5 Dead code & orphaned rules

  • 6.5.1 After the pass, rg "timecurve-simple__" frontend/src/pages/TimeCurveSimplePage.tsx ⊆ rg "timecurve-simple__" frontend/src/index.css (every used class exists in CSS).
  • 6.5.2 And the reverse: every .timecurve-simple__* rule in index.css is referenced from the page (no orphan rules left behind from prior passes — e.g. the __timer-value / __timer-foot cleanup just done is the kind of audit we want to do for any block we remove).

6.6 Responsive behavior

  • 6.6.1 No horizontal scroll at 360px viewport.
  • 6.6.2 Touch targets ≥ 44×44 (Apple HIG) / ≥ 48×48 (Material) at all breakpoints.
  • 6.6.3 Content-first stacking on narrow widths — timer first, then buy. (Already enforced via align-items: stretch on the hub grid + container query stack.)

7. Image usage checklist (3 levels) — with recommendations

All paths below resolve under frontend/public/art/. The catalog with consumers lives in frontend/public/art/README.md.

7.1 Required (must keep — worldbuilding glue)

  • 7.2.1 cutouts/leprechaun-bag-bunny-pair.png — already noted in frontend/public/art/README.md as the Simple-view sidekick. Add as a small corner cutout on the timer panel (mirrors the LaunchCountdown pattern that uses cutout-bunnyleprechaungirl-full.png + bunny-jump.png). Tucked into the bottom-left, behind the digits, at low z-index, with cutout-decoration--float.

    leprechaun + bunny pair
  • 7.2.2 hat-coin-stack.png or hat-coin-front.png — small coin glyph in the buy preview row next to the CL8Y figure ("≈ 12.34 CL8Y"). Reinforces "you are getting tokens" without copy.

    hat coin stack hat coin front

  • 7.2.3 cutouts/cutout-bunnyleprechaungirl-playful.png — empty-state mascot for the "Recent buys" placeholder. Replaces the text-only "Waiting for the first buy of this round." with a mascot + caption.

    playful mascot
  • 7.2.4 cutouts/bunny-podium-win.png — only when the post-sale "Redeem CHARM" state is active; tucked alongside the CTA to celebrate the win. Conditional render so it never appears for a buy persona.

    bunny podium win
  • 7.2.5 icons/token-charm.png and icons/token-cl8y.png — small inline glyphs adjacent to "CHARM" and "CL8Y" labels in the buy preview / stake panel. Cheap worldbuilding, replaces some plain-text noise.

    charm token cl8y token

7.3 Candidates to REMOVE / re-evaluate

  • 7.3.1 PageHero coinSrc — if §3.4.1 cuts the hero title, the floating coin goes with it. Justify either way.
  • 7.3.2 Multiple stacked PageBadges above the fold — timer panel has one ("TIME REMAINING") and buy panel has one ("PRIMARY ACTION"). Decide whether both earn their visual weight; consider dropping one.
  • 7.3.3 Stake panel "1.2× launch anchor" badge — see §3.5.2; this is the most jargon-heavy badge on the page.
  • 7.3.4 Any inline "decorative" emoji or stray unicode glyph that crept in — there should be none. Audit: rg "[\\x{2600}-\\x{27BF}]|[\\x{1F300}-\\x{1F9FF}]" frontend/src/pages/TimeCurveSimplePage.tsx.

7.4 Image discipline & performance (per <img>)

  • 7.4.1 Every <img> has width, height, alt, decoding, and loading attributes set. Audit: rg "<img" frontend/src/pages/TimeCurveSimplePage.tsx frontend/src/pages/timecurve/TimeCurveTimerHero.tsx frontend/src/components/CutoutDecoration.tsx.
  • 7.4.2 decoding="async" for all non-LCP, decoding="sync" only for the LCP image.
  • 7.4.3 fetchPriority="high" on the LCP image only (today: scenes/timecurve-simple.jpg inside the timer hero).
  • 7.4.4 Mobile alternative crop served via <picture> + media for any image that is wider than 480px on a portrait viewport.
  • 7.4.5 No raster > 200KB on the Simple page initial render. Sum first-paint images and confirm.
  • 7.4.6 Cutouts use CutoutDecoration helper (which provides reduced-motion-safe animations) rather than inline <img>.

7.5 Cursors (worldbuilding micro-detail)

  • 7.5.1 primary-cta.png on the Buy CHARM button via .btn-primary--priority — confirm.
  • 7.5.2 slider-grab.png on the slider thumb via .timecurve-simple__slider — confirm at @media (hover: hover) only.
  • 7.5.3 No danger-pvp.png on the Simple view (PvP cursors live on Arena).

8. Worldbuilding & design philosophy preservation — final guardrails

8.1 Brand voice

  • 8.1.1 No copy refactor that erases personality verbs ("clutch", "ticks up", "hard-reset") in the name of plainness.
  • 8.1.2 Mascot identity preserved on at least one surface above the fold — today the scene-art bunny+leprechaun behind the timer satisfies this; if §7.2.1 ships a corner cutout, even better.

8.2 Color personality

  • 8.2.1 No shift away from arcade green/gold to neutral fintech grays.
  • 8.2.2 Hard 4–5px offset shadows preserved across cards, chips, CTAs.

8.3 Information density (worldbuilding ↔ simplicity bargain)

  • 8.3.1 The "1.2× launch anchor" lore is part of the worldbuilding — keep the at-launch row in the rate board (one of the few pieces of jargon that earns its space because it explains why you should buy now).
  • 8.3.2 Numbers in arcade font (Bungee / Plex Mono) communicate "this is a game". Do not refactor them to neutral system fonts to "look cleaner".

8.4 Mascot affordances

  • 8.4.1 At least one mascot/cutout visible in the first viewport. Today: scene-art mascot satisfies. After §7.2.1: even stronger.
  • 8.4.2 Empty / loading / post-sale states each get a mascot or scene moment (per §7.2.3 and §7.2.4).

9. Acceptance criteria (final cut)

  • 9.1 "I just want to buy" walkthrough as a fresh visitor (no wallet → connect → buy) completes in ≤ 3 visible decisions before tapping Buy CHARM.
  • 9.2 Worldbuilding signals: at least 3 of 5 of {scene art, mascot/cutout, custom cursor, arcade-font numerals, gold-glow CTA} present in the first viewport.
  • 9.3 Mobile (390×844) layout: timer + Connect/Buy in the first viewport, no horizontal scroll, hub stacks vertically.
  • 9.4 Lighthouse: Performance ≥ 90, Accessibility ≥ 95, Best Practices ≥ 95 on /timecurve against a local production build.
  • 9.5 WCAG AA contrast across all visible text on /timecurve.
  • 9.6 No occurrences of WAD, wei, bps, revertReason, endSale, DoubLPIncentives, or TimeCurve.sol in visible above-the-fold copy. (Inside <details>, comments, test-ids, and tooltips: allowed.)
  • 9.7 Every removed component leaves no orphan CSS rule, no orphan data-testid in tests, no broken doc cross-link.
  • 9.8 Every added image has width, height, alt, decoding, loading, and is registered in frontend/public/art/README.md consumer table.
  • 9.9 npx tsc -b --noEmit clean, npx vitest run green, npx playwright test e2e/timecurve.spec.ts green.
  • 9.10 docs/frontend/timecurve-views.md, docs/qa/YO-TimeCurve-QA-Checklist.md (C7, C11), and frontend/public/art/README.md updated in the same MR as the code change.

## Context The `/timecurve` Simple view (`TimeCurveSimplePage`) has been through one full design pass (issue [#44](https://gitlab.com/PlasticDigits/yieldomega/-/issues/44)) and an asset wiring pass (issue [#45](https://gitlab.com/PlasticDigits/yieldomega/-/issues/45) → [#57](https://gitlab.com/PlasticDigits/yieldomega/-/issues/57)) plus several iterative UX rounds. The page already hosts only a TimeCurveSubnav, the timer + buy "sale hub", a `PageHero` strip, an optional "Your stake at launch" panel, and a 3-row "Recent buys" ticker. The global footer is hidden on this route. It is **still too dense** for the canonical persona we are designing for: **a first-time visitor who wants to buy CHARM the way they would buy any token in a normal sale, without learning the curve, the launch anchor, the timer-extension rules, or the difference between Simple / Arena / Protocol**. This issue is the **review and aggressive-simplification pass** for that persona, with one explicit constraint: **the worldbuilding and design philosophy are non-negotiable** — the arcade leprechaun-bunny mascot system, the deep-green / gold "stickered playing card" surface, the Bungee/Plex Mono numerals, and the custom cursors are part of *what makes Yieldomega Yieldomega* and have to survive the cuts. > **Tension statement we are explicitly resolving:** > *Maximum simplicity for "I just want to buy" should win at the **information** layer (less to read, fewer choices, defaults that just work), while worldbuilding should win at the **presentation** layer (mascots, scene art, cursors, fonts, gold-glow CTA, hard shadows). Cuts that would strip personality to win 50ms of cognition cost are not in scope.* Authoritative references that frame this work (must read before checking off items): - [`docs/frontend/timecurve-views.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/frontend/timecurve-views.md) — current Simple-view layout contract. - [`docs/frontend/design.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/frontend/design.md) — global frontend patterns (no raw wei, plain timestamps, etc.). - [`docs/product/vision.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/product/vision.md) — joy / participation / agent-friendly tone. - [`frontend/public/art/README.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/README.md) — full asset catalog with consumer table. - [`frontend/src/pages/TimeCurveSimplePage.tsx`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/src/pages/TimeCurveSimplePage.tsx) — page under review. - [`docs/qa/YO-TimeCurve-QA-Checklist.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/qa/YO-TimeCurve-QA-Checklist.md) (C7, C11) — current acceptance criteria. Baseline commit at the time this issue was written: `88e4eb6`. --- ## 1. Research foundation — simplicity principles to apply Every checklist item below is grounded in one of these established UX/UI principles. Skim the named principle when an item is unclear; do not re-derive a rationale from scratch. - [ ] **1.1** Steve Krug, *Don't Make Me Think* — three laws: (a) we don't read pages, we scan them; (b) we don't make optimal choices, we satisfice; (c) we don't figure out how things work, we muddle through. Applied: hero copy must be scan-able, default values must be safe to satisfice on, the buy flow must be muddle-through-able by a wallet-only user. - [ ] **1.2** Jakob Nielsen, *10 Usability Heuristics* — visibility of system status, match real-world language, user control, consistency, error prevention, recognition over recall, flexibility, minimalist design, help users recognize/recover from errors, help/documentation. Applied: every visible block on the page is justified against minimalist design (#8) before it ships. - [ ] **1.3** Hick-Hyman law — decision time scales with `log2(n+1)` choices. Applied: count the visible decisions in the buy flow; we want **≤ 3** before "Buy CHARM" tap. - [ ] **1.4** Fitts' law — acquisition time scales with `log2(D/W + 1)`. Applied: primary CTA gets the largest touch target on the page; the slider thumb gets a custom cursor and adequate hit area. - [ ] **1.5** Edward Tufte, *data-ink ratio* — maximize information per non-decoration pixel. Applied: each panel justifies its non-data chrome (badges, ledes, padding) against the information it conveys. - [ ] **1.6** Cognitive Load Theory (Sweller) — separate intrinsic / extraneous / germane load. Applied: jargon ("clearing price", "1.2× anchor", "endSale()") is **extraneous** for the buy persona and gets disclosed-not-displayed; the timer urgency is **intrinsic** and stays loud. - [ ] **1.7** F-pattern reading + above-the-fold convention — first eye-stop must contain the headline answer. Applied: the timer + buy CTA must both fit the first viewport at ≥ 1280×900 desktop and 390×844 mobile. - [ ] **1.8** Apple HIG — *clarity, deference, depth*. Applied: chrome defers to content; depth (shadows, glow) communicates hierarchy not decoration. - [ ] **1.9** Google Material — *bold, graphic, intentional motion*. Applied: motion is reserved for state change (pulse on critical timer) and never as ambient noise. - [ ] **1.10** Refactoring UI (Wathan & Schoger) — hierarchy via size/weight/color (not borders), depth via shadow, color via HSL families. Applied: no new borders to separate things; reach for size/weight first. - [ ] **1.11** Don Norman, *Design of Everyday Things* — defaults are the most important UI element. Applied: pay-with defaults to CL8Y, slippage to a sane bps, slider to the live mid-price. - [ ] **1.12** Progressive disclosure — power-user controls behind `<details>` or modal. Applied: pay-with picker, slippage, referral, balance row already in `<details>`; verify nothing else leaks above it. --- ## 2. Worldbuilding & design philosophy — non-negotiable preserves Every cut below must survive these. If a simplification proposal would damage one of these, it is rejected. ### 2.1 Brand voice - [ ] **2.1.1** Tone is **arcade game / treasure hunt**, not "DeFi protocol". Examples that must survive: *"Every buy adds 2 minutes; clutch buys hard-reset the clock."*, *"Spend CL8Y now — every block waited makes CHARM cost more."* - [ ] **2.1.2** Sentences with personality — verbs that move ("clutch", "hard-reset", "ticks up"), not corporate hedges ("may be subject to", "please be aware"). - [ ] **2.1.3** Lore acknowledged in copy where it matters (e.g. "1.2× per-CHARM clearing price" in the rate board) — but never as the **primary** thing a buy persona must read. ### 2.2 Mascot, motif, and scene system - [ ] **2.2.1** At least one mascot/cutout **or** scene image visible in the first viewport. Today: `scenes/timecurve-simple.jpg` (low-opacity backplate behind the timer hero). Acceptable. **Not acceptable:** stripping it for a flat green panel. - [ ] **2.2.2** Custom cursors stay wired on the primary CTA (`/art/cursors/primary-cta.png`) and the slider thumb (`/art/cursors/slider-grab.png`) at `@media (hover: hover)` breakpoints. - [ ] **2.2.3** `PageBadge` icons (status-{live, prelanch, ended, cooldown, etc.}) stay; they are the cheapest worldbuilding pixels on the page. ### 2.3 Color, font, art-style invariants - [ ] **2.3.1** Arcade green family (`--bg-deep`, `--card`, the `linear-gradient(165deg, #122e1d → #1d4329 → #2c5d3f)` timer stage) is the dominant page color. - [ ] **2.3.2** Gold (`--arcade-gold-200/400/600`) is used **only** for: the primary CTA cursor/style, the timer hero days chip, the Simple-tab pill in the subnav, and the rate board's at-launch row. No gold creep into secondary chrome. - [ ] **2.3.3** Hard 4–5px offset shadows preserved on cards and chips ("stickered playing card" feel). No drift to soft material shadows. - [ ] **2.3.4** Display font (Bungee) reserved for big numerals, days chip, and wordmark; body font (Fredoka / system) for narrative; mono font (IBM Plex Mono) for addresses, tx hashes, and explicit code spans. - [ ] **2.3.5** `font-variant-numeric: tabular-nums` on every live-updating number on the page (timer digits, rate-board prices, stake values). Already true today; keep it true. --- ## 3. Section-by-section component review For every section currently rendered by `TimeCurveSimplePage`, decide: **keep / simplify / move-to-disclosure / remove**. Every "remove" must justify against §2. ### 3.1 `TimeCurveSubnav` (Simple / Arena / Protocol) - [ ] **3.1.1 Keep.** It is the **only** cross-page nav on the page since the "Want more?" tile row was removed. - [ ] **3.1.2** Confirm the Simple tab is visually obvious as "you are here" without reading the label (gold pill vs. white). - [ ] **3.1.3** Audit: at narrow widths, can a buy-persona user still see the subnav and not assume the page is broken? (Non-buy persona's escape hatch.) ### 3.2 Sale hub — Timer panel (`.timecurve-simple__timer-panel`) - [ ] **3.2.1 Keep.** Timer is the single most important affordance on the page (it justifies *why* you should buy now). - [ ] **3.2.2** Header `lede` (currently `phaseNarrative()` output) — review: is one sentence sufficient? Can we cut the header lede entirely on Simple and let the timer-hero foot line do the talking? The `PageSection` header eyebrow ("TIME REMAINING") + title ("TIME LEFT") may be redundant with each other. - [ ] **3.2.3** Timer hero foot copy stays: *"Every buy adds 2 minutes; clutch buys hard-reset the clock."* This is the **only place** on the Simple page that explains why the timer is interesting; it earns its real estate. - [ ] **3.2.4** Days chip (`Nd`) only appears when `secondsRemaining ≥ 86400`. Confirmed in `TimeCurveTimerHero.tsx`. No change. - [ ] **3.2.5** Decide: do we keep BOTH a panel-level `PageBadge` ("TIME REMAINING") AND a panel title ("TIME LEFT")? At least one is redundant. ### 3.3 Sale hub — Buy CHARM panel (`.timecurve-simple__buy-panel`) This panel is where almost all the simplification work lives. - [ ] **3.3.1 Live rate board** — keep. The current price (big, fixed-6) is the page's #2 most-glanced number. Audit: is the at-launch row still earning its space for a buy-only persona, or should it move into `<details>` ? - [ ] **3.3.2 Min–max pill** ("Live band X – Y CL8Y") — review: a buy persona doesn't pick the bounds, the slider does. Consider hiding the literal numbers and only showing them inside the Advanced disclosure. The visual band on the slider track communicates the bounds visually. - [ ] **3.3.3 Slider + numeric input** — keep slider, audit numeric input. A buy persona almost never types an exact CL8Y amount; the numeric input is a power-user override. Consider moving the numeric input into the Advanced disclosure (or using a small inline "type exact amount" link to reveal it). - [ ] **3.3.4 Buy preview** ("You add X CHARM" + "Worth at launch ≈ Y CL8Y") — keep. This is the buy persona's "did I dial in the right amount" check. - [ ] **3.3.5 Buy preview footnote** — currently text-only; consider replacing/augmenting with a small `hat-coin-front.png` glyph next to the CL8Y figure. - [ ] **3.3.6 Buy CHARM CTA** — keep. Already the largest button on the page. Verify it owns the `primary-cta.png` cursor and the gold-glow class. - [ ] **3.3.7 Cooldown line** — keep, but only when `walletCooldownRemainingSec > 0`. Already gated. No change. - [ ] **3.3.8 Error line** — keep with dismiss button. Already gated. Audit: does the dismiss button stand out enough that a panicked user can find it in 1 second? - [ ] **3.3.9 `<details>` Advanced disclosure** — keep. Audit each item inside it (pay-with, slippage, referral, wallet balance) for whether it should even be on the Simple view at all (vs. living only on `/timecurve/arena`). For the buy persona, every advanced control we move *off* the Simple page is a win. - [ ] **3.3.10 Disconnected state CTA** — currently "Connect a wallet to buy CHARM and lock in your share of the DOUB launch." + `<WalletConnectButton />`. Keep both, but verify the copy is the **first thing** read (above the rate board) when no wallet is connected. Today the rate board renders only in `saleActive`, so the order is fine; double-check on `saleStartPending` and `saleEnded`. ### 3.4 `PageHero` (title + lede + chain-time deadline) - [ ] **3.4.1** Title "TimeCurve sale" — review: is this redundant with the subnav active tab ("Simple — Time left + buy CHARM") and the panel titles? Candidate to **remove** entirely on Simple, keeping the badge + lede only. - [ ] **3.4.2** `HERO_LEDE` copy — review against §2.1; trim to one sentence if it has grown to two. - [ ] **3.4.3** `coinSrc="/art/token-logo.png"` — keep, it is a worldbuilding pixel (§2.2). - [ ] **3.4.4** `headerContent` (chain-time deadline) — review: a buy persona reads the timer for "when". The chain-time deadline is operator-tier info. Consider moving into the timer hero foot OR into the Advanced disclosure OR removing entirely on Simple. ### 3.5 "Your stake at launch" panel (gated on `walletConnected && charmWeightWad`) - [ ] **3.5.1 Keep.** This is the page's answer to "what did my CHARM buy me?", and it is gated to wallets that already hold CHARM, so a fresh visitor never sees it. - [ ] **3.5.2** Title + badge ("Your stake at launch" + "1.2× launch anchor") — review: badge is jargon. Consider replacing with a smaller "Updates each block" eyebrow or removing. - [ ] **3.5.3** Lede paragraph — currently 2 sentences with `<strong>` highlights. Trim to 1 sentence ("Your CHARM is worth at least this much in CL8Y when DOUB lists.") for the buy persona; full lore stays in `docs/frontend/timecurve-views.md`. - [ ] **3.5.4** Stake foot ("enforced by `DoubLPIncentives`; see the Protocol view…") — review: keep the link to Protocol (escape hatch for power users), but drop the contract name from the visible string (it is meaningless to the buy persona). ### 3.6 "Recent buys" ticker - [ ] **3.6.1** Audit: does this ticker move someone closer to "I want to buy"? It's social proof, which is useful — but at the cost of one more panel below the fold. Decide: keep as-is, shrink to 1–2 rows, or move behind a "Recent activity" toggle. - [ ] **3.6.2** Empty state ("Waiting for the first buy of this round.") — consider replacing the text-only placeholder with a small `cutouts/cutout-bunnyleprechaungirl-playful.png` mascot for personality. - [ ] **3.6.3** Each row currently shows: short address · amount · `+Ns` extension OR "hard reset" · tx link. The "hard reset" / `+Ns` column is lore-y; for a buy persona it is opaque. Decide: keep (because it visually rewards big buys), or simplify to a small icon (e.g. lightning glyph for hard reset). ### 3.7 Global app footer (currently hidden on `/timecurve`) - [ ] **3.7.1** Already hidden on `/timecurve` only via `RootLayout.showFooter`. Confirm still hidden after this pass and that operator info is one click away (Protocol view). ### 3.8 Header / `RootLayout` chrome (brand, primary nav, chain pill, wallet button) - [ ] **3.8.1** Out of scope for this issue (shared shell), but **must not regress**: chain pill, wallet `ConnectButton`, brand wordmark, and the cross-app nav stay visible and functional. --- ## 4. Common simplicity heuristics — checklist (3 levels) ### 4.1 Information hierarchy - [ ] **4.1.1 Single primary CTA per page.** "Buy CHARM" is the only `btn-primary--priority` on the Simple view. Verify by `rg "btn-primary--priority" frontend/src/pages/TimeCurveSimplePage.tsx` (must return 1 hit, on the buy/redeem button). - [ ] **4.1.2 Inverted pyramid.** First viewport = timer + buy CTA. Second viewport = stake panel (when applicable) + recent buys. Third viewport = nothing. - [ ] **4.1.3 Visual weight matches semantic weight.** Largest text on the page is the timer digits. Second-largest is the live current-price. Buy CTA is the largest button. Anything currently larger than its semantic role gets demoted (font-size, weight, or color). ### 4.2 Cognitive load reduction - [ ] **4.2.1 Reduce simultaneous visible decisions.** Count visible interactive controls in the saleActive + walletConnected first viewport: today = slider + advanced toggle + buy CTA = 3. Goal: stay ≤ 3. - [ ] **4.2.2 Progressive disclosure.** Pay-with, slippage, referral, wallet balance must remain inside `<details>`. Add an audit grep: `rg "session.payWith" frontend/src/pages/TimeCurveSimplePage.tsx | wc -l` should match what's inside the `<details>` block, no more. - [ ] **4.2.3 Plain language above the fold.** No occurrences of: `WAD`, `wei`, `bps`, `revertReason`, `endSale()`, contract names. Run `rg "WAD|wei\\b|bps|revertReason|endSale|DoubLPIncentives|TimeCurve\\.sol" frontend/src/pages/TimeCurveSimplePage.tsx` and confirm hits are inside `<details>`, comments, or test-ids only. ### 4.3 "I just want to buy" decision support - [ ] **4.3.1 No financial calculations required of the user.** They never multiply, never convert, never look up. The page does the math (rate board + buy preview). - [ ] **4.3.2 Defaults that work for 90% of buyers.** Slider lands at the live midpoint, pay-with defaults to CL8Y, slippage defaults to a sane bps. Verify defaults in `useTimeCurveSaleSession`. - [ ] **4.3.3 Confirm-and-go, not configure-then-go.** From a connected wallet: drag slider once → tap Buy CHARM → wallet pops the tx. Anything between drag and tap is a leak. ### 4.4 Trust signals (smaller surface, same strength) - [ ] **4.4.1 Chain identity** visible in header chain pill at all times. - [ ] **4.4.2 Transparent pricing** — the rate board shows current and at-launch prices live; no dark-pattern hidden cost between the slider and the wallet pop. - [ ] **4.4.3 Onchain provenance.** Recent buys link to explorer via `TxHash`. Keep. ### 4.5 Accessibility & inclusivity - [ ] **4.5.1 Touch targets** ≥ 44×44 pt (Apple HIG) on the buy CTA, slider thumb, advanced toggle, recent-buys tx link. - [ ] **4.5.2 Contrast** ≥ 4.5:1 for body, ≥ 3:1 for large text. Verify with axe / pa11y on `/timecurve` against the deep-green panels. - [ ] **4.5.3 Reduced motion** — sparks animation, critical timer pulse, button hover transforms all gated behind `@media (prefers-reduced-motion: reduce)`. Verify timer hero, button motion, and any new animations introduced by this pass. - [ ] **4.5.4 Live regions** — timer + rate board + cooldown + error already use `aria-live="polite"`. Verify after any layout reorder. - [ ] **4.5.5 Reading level** of all above-the-fold copy ≤ 8th grade (Hemingway / readable.com check). - [ ] **4.5.6 Keyboard order** — tab from the wallet button reaches Subnav → Buy CTA → Advanced toggle → (open) Pay-with → … in logical order. ### 4.6 Performance & speed perception - [ ] **4.6.1 Loading skeletons** match real layout (no jumping between `Loading sale state…` and the live numbers). - [ ] **4.6.2 Critical art preloaded** — `scenes/timecurve-simple.jpg` already uses `loading="eager"`. Add `fetchPriority="high"` if not already set. - [ ] **4.6.3 Secondary art lazy** — recent-buys mascot (if added per §8.2.3), stake panel art, etc., use `loading="lazy"` and `decoding="async"`. - [ ] **4.6.4 Time-to-interactive on Buy CHARM** ≤ 2s on a cold load over a throttled 4G profile (Lighthouse). --- ## 5. Theming checklist (3 levels) ### 5.1 Color tokens - [ ] **5.1.1 Arcade green palette** — page surfaces use `var(--bg-deep)`, `var(--card)`, `var(--ink)`, `var(--text-main)`. No new hex literals introduced for surface colors. - [ ] **5.1.2 Gold accent palette** — `var(--arcade-gold-200/400/600)` reserved for: primary CTA, days chip, Simple-tab pill, at-launch rate row. Audit: `rg "arcade-gold" frontend/src/index.css` and confirm no creep. - [ ] **5.1.3 Urgency palette** — yellow under `timer-hero--warning`, red under `timer-hero--critical`. Only on the timer hero. Confirm no other component uses these urgency hues. - [ ] **5.1.4 Text contrast pairs** — body text on `--card` ≥ 4.5:1, white on gold-CTA ≥ 4.5:1, white on green-spotlight ≥ 4.5:1. ### 5.2 Typography - [ ] **5.2.1 Display face** (Bungee) used only for: brand wordmark, timer digits, days chip number. Audit: `rg "Bungee" frontend/src/index.css`. - [ ] **5.2.2 Body face** (Fredoka / system stack) for narrative, badges, ledes. - [ ] **5.2.3 Mono face** (IBM Plex Mono) for addresses, tx hashes, explicit `<code>` spans, and the rate-board numerals where digit alignment matters. - [ ] **5.2.4 Tabular numerals** on every live-updating number — already enforced in the timer hero CSS; confirm the rate board's price values also opt in (they should, via `font-variant-numeric: tabular-nums` on the parent or the value class). - [ ] **5.2.5 Line-height** body ≥ 1.45, hero ≥ 1.0 (digits) / 1.45 (foot). ### 5.3 Spacing & rhythm - [ ] **5.3.1 Base grid** — multiples of 0.25rem (4px). No padding / margin off-grid. - [ ] **5.3.2 Fluid spacing** — `clamp(min, vw, max)` for every padding that has to scale across desktop and mobile (already used in the timer hero; verify Buy panel uses the same scale). - [ ] **5.3.3 Vertical rhythm** between sections: 0.5rem (chip-to-chip), 0.85rem (label-to-value), 1.25rem (panel-internal sections), 2rem (panel-to-panel). Anything off these increments needs justification or correction. ### 5.4 Surface & depth - [ ] **5.4.1 `data-panel` base** — white card with hard offset shadow. Used by stake + recent buys panels. - [ ] **5.4.2 `data-panel--spotlight`** — gold-glow border + accent. Used by timer + buy panels (the two spotlight items above the fold). Confirm exactly two `data-panel--spotlight` on this page. - [ ] **5.4.3 Nested cards** inside a panel max 1 level deep. Today: rate board nests inside the buy panel (1 level), buy preview nests inside the buy panel (1 level). No 2-level nesting allowed. --- ## 6. Style checklist — CSS hygiene (3 levels) ### 6.1 Naming - [ ] **6.1.1 BEM** — every new class follows `.timecurve-simple__<block>__<element>--<modifier>`. Audit: `rg "class.*timecurve-simple" frontend/src/pages/TimeCurveSimplePage.tsx` should not surface ad-hoc unprefixed classes. - [ ] **6.1.2 No utility-class explosion** — we are not Tailwind. Compose with semantic class names + variables. - [ ] **6.1.3 `data-testid` parity** — every visible block that has e2e or vitest coverage keeps its `data-testid`; removed blocks remove their `data-testid` from the spec files. ### 6.2 Layout primitives - [ ] **6.2.1 CSS Grid** for page-level structure (the hub is a grid). - [ ] **6.2.2 Flexbox** for component-internal flow (rate board rows, timer clock). - [ ] **6.2.3 Container queries** for hub-style responsive behavior, with `@media` fallback. Already in place after the timer-hero pass — confirm no new container queries introduced that try to query their own container. ### 6.3 Animations & motion - [ ] **6.3.1** Every `@keyframes` paired with a `@media (prefers-reduced-motion: reduce)` killswitch. - [ ] **6.3.2** No animation > 4s loop on the primary fold (sparks rise is ~4.2s — at the edge, accept). - [ ] **6.3.3** Transition duration ≤ 200ms for input feedback (button press, hover). - [ ] **6.3.4** No autoplaying scene-art animation; backplate stays static. ### 6.4 Design-token discipline - [ ] **6.4.1** Use `var(--*)` rather than hex literals for any color reused 2+ times on the page. - [ ] **6.4.2** No `style={{...}}` literal values in JSX (acceptable: `style={{ ["--fx-i" as string]: i }}` for keyframe-staggering). ### 6.5 Dead code & orphaned rules - [ ] **6.5.1** After the pass, `rg "timecurve-simple__" frontend/src/pages/TimeCurveSimplePage.tsx` ⊆ `rg "timecurve-simple__" frontend/src/index.css` (every used class exists in CSS). - [ ] **6.5.2** And the reverse: every `.timecurve-simple__*` rule in `index.css` is referenced from the page (no orphan rules left behind from prior passes — e.g. the `__timer-value` / `__timer-foot` cleanup just done is the kind of audit we want to do for any block we remove). ### 6.6 Responsive behavior - [ ] **6.6.1** No horizontal scroll at 360px viewport. - [ ] **6.6.2** Touch targets ≥ 44×44 (Apple HIG) / ≥ 48×48 (Material) at all breakpoints. - [ ] **6.6.3** Content-first stacking on narrow widths — timer first, then buy. (Already enforced via `align-items: stretch` on the hub grid + container query stack.) --- ## 7. Image usage checklist (3 levels) — with recommendations > All paths below resolve under `frontend/public/art/`. The catalog with consumers lives in [`frontend/public/art/README.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/README.md). ### 7.1 Required (must keep — worldbuilding glue) - [ ] **7.1.1** [`scenes/timecurve-simple.jpg`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/scenes/timecurve-simple.jpg) — already wired as the timer hero backplate at low opacity. **Keep.** <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/scenes/timecurve-simple.jpg" width="320" alt="timecurve-simple scene" /> - [ ] **7.1.2** [`token-logo.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/token-logo.png) — `PageHero` `coinSrc`, the DOUB coin glyph anchoring the hero. **Keep** (or consciously remove if §3.4 cuts the hero entirely). <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/token-logo.png" width="96" alt="token logo" /> - [ ] **7.1.3** Status icon set used by `phaseBadge()`: [`icons/status-live.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/icons/status-live.png), [`icons/status-prelanch.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/icons/status-prelanch.png), [`icons/status-ended.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/icons/status-ended.png), [`icons/status-cooldown.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/icons/status-cooldown.png). **Keep** — at least one is always shown. <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/icons/status-live.png" width="64" alt="status live" /> <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/icons/status-prelanch.png" width="64" alt="status prelaunch" /> <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/icons/status-ended.png" width="64" alt="status ended" /> <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/icons/status-cooldown.png" width="64" alt="status cooldown" /> - [ ] **7.1.4** [`cursors/primary-cta.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/cursors/primary-cta.png) — leprechaun cursor on the Buy CHARM button. Worldbuilding micro-detail. **Keep** wired via `.btn-primary--priority` in `index.css`. - [ ] **7.1.5** [`cursors/slider-grab.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/cursors/slider-grab.png) — slider thumb cursor at `@media (hover: hover)`. **Keep** unless we move the slider behind disclosure (then re-evaluate). - [ ] **7.1.6** [`icons/nav-simple.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/icons/nav-simple.png) (subnav) — keep, it is the sole "you are here" worldbuilding pixel for the active tab. <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/icons/nav-simple.png" width="64" alt="nav simple" /> ### 7.2 Recommended additions (worldbuilding push, low ink-cost) - [ ] **7.2.1** [`cutouts/leprechaun-bag-bunny-pair.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/cutouts/leprechaun-bag-bunny-pair.png) — already noted in [`frontend/public/art/README.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/README.md) as the Simple-view sidekick. **Add as a small corner cutout on the timer panel** (mirrors the LaunchCountdown pattern that uses `cutout-bunnyleprechaungirl-full.png` + `bunny-jump.png`). Tucked into the bottom-left, behind the digits, at low z-index, with `cutout-decoration--float`. <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/cutouts/leprechaun-bag-bunny-pair.png" width="200" alt="leprechaun + bunny pair" /> - [ ] **7.2.2** [`hat-coin-stack.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/hat-coin-stack.png) or [`hat-coin-front.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/hat-coin-front.png) — small coin glyph in the buy preview row next to the CL8Y figure ("≈ 12.34 CL8Y"). Reinforces "you are getting tokens" without copy. <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/hat-coin-stack.png" width="96" alt="hat coin stack" /> <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/hat-coin-front.png" width="80" alt="hat coin front" /> - [ ] **7.2.3** [`cutouts/cutout-bunnyleprechaungirl-playful.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/cutouts/cutout-bunnyleprechaungirl-playful.png) — empty-state mascot for the "Recent buys" placeholder. Replaces the text-only "Waiting for the first buy of this round." with a mascot + caption. <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/cutouts/cutout-bunnyleprechaungirl-playful.png" width="200" alt="playful mascot" /> - [ ] **7.2.4** [`cutouts/bunny-podium-win.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/cutouts/bunny-podium-win.png) — only when the post-sale "Redeem CHARM" state is active; tucked alongside the CTA to celebrate the win. Conditional render so it never appears for a buy persona. <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/cutouts/bunny-podium-win.png" width="160" alt="bunny podium win" /> - [ ] **7.2.5** [`icons/token-charm.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/icons/token-charm.png) and [`icons/token-cl8y.png`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/icons/token-cl8y.png) — small inline glyphs adjacent to "CHARM" and "CL8Y" labels in the buy preview / stake panel. Cheap worldbuilding, replaces some plain-text noise. <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/icons/token-charm.png" width="64" alt="charm token" /> <img src="https://gitlab.com/PlasticDigits/yieldomega/-/raw/main/frontend/public/art/icons/token-cl8y.png" width="64" alt="cl8y token" /> ### 7.3 Candidates to REMOVE / re-evaluate - [ ] **7.3.1 `PageHero coinSrc`** — if §3.4.1 cuts the hero title, the floating coin goes with it. Justify either way. - [ ] **7.3.2 Multiple stacked `PageBadge`s** above the fold — timer panel has one ("TIME REMAINING") and buy panel has one ("PRIMARY ACTION"). Decide whether both earn their visual weight; consider dropping one. - [ ] **7.3.3 Stake panel "1.2× launch anchor" badge** — see §3.5.2; this is the most jargon-heavy badge on the page. - [ ] **7.3.4** Any inline "decorative" emoji or stray unicode glyph that crept in — there should be none. Audit: `rg "[\\x{2600}-\\x{27BF}]|[\\x{1F300}-\\x{1F9FF}]" frontend/src/pages/TimeCurveSimplePage.tsx`. ### 7.4 Image discipline & performance (per `<img>`) - [ ] **7.4.1** Every `<img>` has `width`, `height`, `alt`, `decoding`, and `loading` attributes set. Audit: `rg "<img" frontend/src/pages/TimeCurveSimplePage.tsx frontend/src/pages/timecurve/TimeCurveTimerHero.tsx frontend/src/components/CutoutDecoration.tsx`. - [ ] **7.4.2** `decoding="async"` for all non-LCP, `decoding="sync"` only for the LCP image. - [ ] **7.4.3** `fetchPriority="high"` on the LCP image only (today: `scenes/timecurve-simple.jpg` inside the timer hero). - [ ] **7.4.4** Mobile alternative crop served via `<picture>` + `media` for any image that is wider than 480px on a portrait viewport. - [ ] **7.4.5** No raster > 200KB on the Simple page initial render. Sum first-paint images and confirm. - [ ] **7.4.6** Cutouts use `CutoutDecoration` helper (which provides reduced-motion-safe animations) rather than inline `<img>`. ### 7.5 Cursors (worldbuilding micro-detail) - [ ] **7.5.1** `primary-cta.png` on the Buy CHARM button via `.btn-primary--priority` — confirm. - [ ] **7.5.2** `slider-grab.png` on the slider thumb via `.timecurve-simple__slider` — confirm at `@media (hover: hover)` only. - [ ] **7.5.3** **No** `danger-pvp.png` on the Simple view (PvP cursors live on Arena). --- ## 8. Worldbuilding & design philosophy preservation — final guardrails ### 8.1 Brand voice - [ ] **8.1.1** No copy refactor that erases personality verbs ("clutch", "ticks up", "hard-reset") in the name of plainness. - [ ] **8.1.2** Mascot identity preserved on at least one surface above the fold — today the scene-art bunny+leprechaun behind the timer satisfies this; if §7.2.1 ships a corner cutout, even better. ### 8.2 Color personality - [ ] **8.2.1** No shift away from arcade green/gold to neutral fintech grays. - [ ] **8.2.2** Hard 4–5px offset shadows preserved across cards, chips, CTAs. ### 8.3 Information density (worldbuilding ↔ simplicity bargain) - [ ] **8.3.1** The "1.2× launch anchor" lore is part of the worldbuilding — keep the at-launch row in the rate board (one of the few pieces of jargon that earns its space because it explains *why* you should buy now). - [ ] **8.3.2** Numbers in arcade font (Bungee / Plex Mono) communicate "this is a game". Do not refactor them to neutral system fonts to "look cleaner". ### 8.4 Mascot affordances - [ ] **8.4.1** At least one mascot/cutout visible in the first viewport. Today: scene-art mascot satisfies. After §7.2.1: even stronger. - [ ] **8.4.2** Empty / loading / post-sale states each get a mascot or scene moment (per §7.2.3 and §7.2.4). --- ## 9. Acceptance criteria (final cut) - [ ] **9.1** "I just want to buy" walkthrough as a fresh visitor (no wallet → connect → buy) completes in **≤ 3 visible decisions** before tapping Buy CHARM. - [ ] **9.2** Worldbuilding signals: at least **3 of 5** of {scene art, mascot/cutout, custom cursor, arcade-font numerals, gold-glow CTA} present in the first viewport. - [ ] **9.3** Mobile (390×844) layout: timer + Connect/Buy in the first viewport, no horizontal scroll, hub stacks vertically. - [ ] **9.4** Lighthouse: Performance ≥ 90, Accessibility ≥ 95, Best Practices ≥ 95 on `/timecurve` against a local production build. - [ ] **9.5** WCAG AA contrast across all visible text on `/timecurve`. - [ ] **9.6** No occurrences of `WAD`, `wei`, `bps`, `revertReason`, `endSale`, `DoubLPIncentives`, or `TimeCurve.sol` in visible above-the-fold copy. (Inside `<details>`, comments, test-ids, and tooltips: allowed.) - [ ] **9.7** Every removed component leaves no orphan CSS rule, no orphan `data-testid` in tests, no broken doc cross-link. - [ ] **9.8** Every added image has `width`, `height`, `alt`, `decoding`, `loading`, and is registered in [`frontend/public/art/README.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/README.md) consumer table. - [ ] **9.9** `npx tsc -b --noEmit` clean, `npx vitest run` green, `npx playwright test e2e/timecurve.spec.ts` green. - [ ] **9.10** [`docs/frontend/timecurve-views.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/frontend/timecurve-views.md), [`docs/qa/YO-TimeCurve-QA-Checklist.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/qa/YO-TimeCurve-QA-Checklist.md) (C7, C11), and [`frontend/public/art/README.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/README.md) updated in the same MR as the code change. --- ## 10. Cross-links - Issue [#44](https://gitlab.com/PlasticDigits/yieldomega/-/issues/44) — design pass that built the current Simple view. - Issue [#45](https://gitlab.com/PlasticDigits/yieldomega/-/issues/45) — issue-45 art pack. - Issue [#57](https://gitlab.com/PlasticDigits/yieldomega/-/issues/57) — round-2 asset backlog (cursors, status icons, story scenes, social/PWA refresh). - [`docs/frontend/timecurve-views.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/frontend/timecurve-views.md) — current Simple-view layout contract (must stay in sync with this issue's outcome). - [`docs/frontend/design.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/frontend/design.md) — global frontend design patterns. - [`docs/product/vision.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/product/vision.md) — joy / participation / agent-friendly tone. - [`frontend/public/art/README.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/public/art/README.md) — full asset catalog and consumer table. - [`frontend/src/pages/TimeCurveSimplePage.tsx`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/src/pages/TimeCurveSimplePage.tsx) — page under review. - [`frontend/src/pages/timecurve/TimeCurveTimerHero.tsx`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/src/pages/timecurve/TimeCurveTimerHero.tsx) — shared timer hero (sibling of `LaunchCountdownPage`).
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-04-24 12:34:34 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#62
No description provided.