Skip to main content

Session — Redesign: make the interface look like the approved design

What this session is for

tmp/latest-design/ holds an approved visual design for the launchpad. The interface in app/ works but does not look like it. Bring the look across.

This is a visual change to a working application. Every number on every screen is read from a chain or an indexer, and the trading path was debugged transaction by transaction on 21 Sep. None of that may be disturbed. If a change would alter what the app does rather than how it looks, it is out of scope for this session.

Read first

  • CLAUDE.md — the constitution. Especially Working with Claude, and the Frontend and deployment trap list, which has already-paid-for answers for several things this touches
  • notes/STATUS.md — where the project stands
  • tmp/latest-design/Stadium-Money-Dev-Pack-36/README-DEV.md — the designer's own handoff, which states the UI constraints and is authoritative about their intent
  • app/src/theme/tokens.css — the token system the app already has

Where the design is, and how to look at it

cd tmp/latest-design/Stadium-Money-Dev-Pack-36 && PORT=8768 node serve.cjs

Then http://127.0.0.1:8768/. Hash routing — #/lab/, #/create/, and so on.

tmp/ is gitignored. Nothing in it is a build input for app/. Do not import its CSS, do not copy its JavaScript, do not vendor its HTML. It is a reference to look at and reproduce, and its own README says the compiled HTML is not source.

index.html / Stadium-Money-Launchpad.htmlIdentical compiled output. What to look at
previous-preview.htmlLegacy build input, superseded. Ignore
*.cssOrdered refinement layers, last-wins. Read for values, never import
README-DEV.mdThe constraints the designer set. Honour them

The good news

The design is closer to the app than it looks. Both use Fraunces and Manrope. The accent is #a3c791 in both. Both theme through data-theme on <html> and both ship a light theme. So this is a layout, composition and density change, not a palette or type rewrite.

What actually differs

Judged from the rendered design, not from its stylesheets:

RailA narrow icon rail with a collapse control, not a wide text rail
Top barA global search field across the width, and a gold wallet pill at the right
HeroDark forest green, a faded stadium photograph bled into the right, cream "Welcome to" over gold "the stadium.", and exactly two CTAs — one filled gold pill, one outlined
Pairing tickerA "PAIR WITH" strip of small round asset logos along the bottom of the hero
CatalogueFilter chips (Trending / New / Top / Volume / Near graduation / Advanced), a density control, "Refine results", then a dense table: token, FDV, 24h, volume, holders, stage, with a star and a copy-address control per row
LabA grid of large illustrated mechanic cards, each with a category tag, an interactive illustration and an "Add to launch" action
Shape languageRounded pills, more gold, quieter panel borders

Keep the designer's stated constraints: the approved Welcome copy, exactly two hero CTAs, the light theme working, and no filler captions, preview labels or development disclaimers in the interface — with the deliberate exception below.

What the design shows that does not exist yet

This is the one thing that needs judgement rather than transcription. The design was drawn against the vision, not against what is deployed.

The lab. The design shows nine mechanics — Dynamic Fee, Anti-MEV Guard, TWAMM Orders, On-Chain Limit Orders, Holder Rebate, Circuit Breaker, Auto-Compounding LP, Liquidity Vesting Lock, Gated Launch. Five exist, and they are not the same five: app/src/chain/mechanics.ts owns the real list — Fees, Anti-sniping, Graduation protection, Maximum trade size, Maximum wallet — and each is a module deployed on Sepolia and readable in contracts/deploy/421614.json.

So: render the real five in the new card treatment, fully working. Render the design's aspirational ones as visibly "Coming soon" — same card shape, clearly inert, no "Add to launch". Never let a coming-soon card reach mechanics.ts or the create form.

Stock pairing. The hero's ticker shows GOOGL, TSLA, AAPL, MSFT and friends. Stock pairing is real ambition and is deployed nowhere (D31). The live quote menu on Sepolia is two stand-ins, mock ARB and mock RHUB, and the factory will refuse anything else. Show the real quote assets as selectable; if you keep the stock logos for the look, they must be unmistakably not choosable.

The rule. A control that cannot do anything must not look like one that can. A launchpad that offers a mechanic it cannot attach is worse than one that shows fewer.

What must still work when you are done

These were each found the hard way. Breaking one silently is the main risk of this session.

  • The trade panel quotes by simulating the real call (app/src/chain/trade.ts). It is not a formula and must not become one. A module runs inside the trade and the curve cannot report it from a view.
  • The gas floor. sendRaw hands over 3,000,000 because BondingCurve, LaunchHook and LaunchToken each check gasleft() and revert when short, and the floors are sequential. Do not "tidy" that into an estimate.
  • The wallet picker offers the EIP-6963-discovered wallets. connectors[0] reaches whichever extension won the injection race, which is how MetaMask was unreachable.
  • The faucet appears only where the quote asset will actually mint, decided by simulating it.
  • The ETH pay option appears only where a v3 route exists. On Sepolia it never does.
  • The deadline comes from the chain's clock, not the browser's.
  • Sign-in is currently broken on staging by design — see issue #3. The watchlist depends on it. Do not "fix" it here.

Scope

Yours: app/src/** — components, CSS, theme/, page composition. app/index.html if the head needs it. New assets under app/public/ or app/src/assets/.

Not yours: contracts/, services/, supabase/, notes/ other than this file, the Makefile, CI, and anything under tmp/. app/src/chain/** is data-layer code — read it, and change it only where the redesign genuinely requires a new read, never to alter behaviour.

Work on a branch. The developer is on master at the same time, on the indexer and on getting a launch through Sepolia. git switch -c redesign, and rebase rather than merge when you land.

Proving it

  • make check green before every commit. It runs build-app-testnet, which typechecks the app against the chain it is actually deployed on — that check exists because a type error invisible at 31337 broke the Vercel build at 421614.
  • Both themes. Toggle it and look. The light theme is a stated constraint.
  • Measure the responsive behaviour, do not eyeball it. Compare documentElement.scrollWidth against clientWidth per route. A grid or flex item will not shrink below its content, so min-width: 0 on the item and overflow-wrap: anywhere on display text.
  • lightweight-charts parses colours itself and knows nothing of color-mix(), nor of the color(srgb …) a browser resolves one to. It throws inside its own render loop, so the symptom is a 300x150 canvas in the corner of a correctly sized box, not a wrong colour. Resolve tokens to rgba() before handing them over.
  • Run it against the local stack — make dev brings up a chain, 305 seeded launches, the indexer and the interface on :5175. A catalogue with real rows in it is the only honest way to judge a catalogue.

How to work

CLAUDE.md governs, and its Working with Claude section is the part that matters most here: build in small pieces, explain each one, and stop at the edge of what was asked. Screen by screen, committed as you go, is far better than one enormous diff nobody can review.

Comments explain why. A comment restating the line above it is noise; one recording why a thing is done the hard way is the most valuable line in the file.

Commit as [type] A sentence in prose, sentence case, no trailing period. Never add a Claude attribution. Use /commit.

Write to this file when something is decided or abandoned — not at the end.

The record

Branch redesign, off a94ab9f. One commit per screen.

Gate per commit

make fmt-app, yarn lint (tsc) and make build-app-testnet — the app half of make check, which is the half this session can break. A full make check runs before landing; running the contract suite and Slither between screen commits spends minutes on code nothing here touches.

Decided

  • Two emphasis colours, not one. --accent stays semantic (a gain, a link, the focus ring); --gold is ornamental (rims, an active rail icon, the second word of a heading); --cta is the filled call to action. All three resolve to greens in the light theme, so no rule below them asks which theme is on. Radius scale moved in tokens.css at the same time.
  • The search is real or it is not there. The design draws a field across every screen and gives the catalogue none of its own. It navigates to /launches?q=…, a URL the catalogue already understood, and / focuses it. The catalogue now watches q rather than reading it once, because a second search from the top bar changes nothing else about the URL.
  • The hero photograph is cropped in CSS, not on disk. The supplied asset is a screenshot of the whole approved hero and only its right-hand region is artwork; the handoff asks for those bytes preserved. The odd percentages in .hero-field img are that rectangle.
  • The hero paragraph stays ours. The design's says "paired with crypto or stocks". Stock pairing is deployed nowhere (D31), so the claim would be false in the product. The approved headline is unchanged, which is what the handoff's constraint names.
  • The pairing ticker shows only real quote assets, built from the catalogue's own rows, each filtering the catalogue. No stock logos at all — the brief allowed keeping them if unmistakably not choosable, and none of them being there is simpler and says the same thing. This retired the separate "Paired with" band, which repeated the information further down.
  • The hero's three totals moved out, to a rule-separated row beneath it: the design gives the whole right-hand side to the photograph and there is nowhere in it for them.
  • FDV stays out of the catalogue table. The design has a column for it; CatalogRow has no supply to compute one, and adding a read to fill a column is a change to what the page does.
  • The lab's nine aspirational mechanics are a separate band, not interleaved. Same card shape, tint drained, "Coming soon", and no action element at all rather than a disabled one — a greyed-out button says "this would work if something were different".
  • The card's tinted head carries a diagram, not an illustration: which of a trade's three moments the mechanic acts on, derived from Catalogued.scope. Real information we already have, where the design has artwork we would have had to invent.
  • Category is a lab-local map, not a field on Catalogued. It is how one page groups cards; putting it in chain/mechanics.ts would make three screens carry it so one could use it.
  • One page title for every screen, as h1.title in base.css. A class rather than a selector list naming each page, so the next screen opts in rather than having to be added to a file it does not own.
  • The create form's sections are numbered by a CSS counter, not by numbers typed into the JSX: adding or dropping a panel renumbers the rest.
  • The catalogue leads with the table, as the design draws it. The grid and the 4/6/8 control are the alternative, and the count is clamped by breakpoint rather than obeyed — eight cards abreast at tablet width is eight unreadable cards, and the choice is kept for when the window is wide again.
  • Brand artwork and the hero JPEG are copied into app/src/assets/, downscaled. They are deliverables, not the CSS/JS/HTML the brief rules out vendoring. The dark theme burns the wordmark to cream with brightness(0) invert(.94) sepia(.18), so one file serves both themes.

Changed outside app/src styling

  • Create accepts ?mechanic=<key> so the lab's "Add to launch" leads somewhere. Applied only once useLaunchMenu confirms the factory offers that key, because the form renders a card per offered mechanic and a choice with no card cannot be undone.

Found on the way

  • .ticker was two different things. None of these stylesheets is scoped, so they all end up in one sheet: the discover page's trade strip was giving the token page's SYMBOL / QUOTE line a border above and below and masking its first characters away. It looked like a clipped label on a page that had never heard of that file, and in a production bundle it would have been there from the first load. The strip is trade-ticker now. Worth watching for elsewhere — a class as generic as .card, .panel or .grid is shared across this app by accident, not by design.

Measured

documentElement.scrollWidth against clientWidth, per route, in a same-origin iframe of a fixed width — Chrome will not resize its window below its minimum, and page zoom decouples innerWidth from it. Every route clean at 320 and 360px. Two overflows found and fixed this way: the header's search squeezed to eighty pixels below 700px (now wraps to its own row), and the lab grid's minmax(300px, …) track pushing nine pixels of scroll at 320px (now min(300px, 100%)).

Proved

  • make check green on the branch: fmt, boundaries, lint, all four builds, 276 contract tests.
  • Both themes looked at on every screen.
  • No horizontal overflow on any of the eight routes at 320px.
  • The trading path still quotes by simulation: 100 dRHUB on a graduated launch simulates to 155.17k MER9 at 0.04% impact against the local chain, with the worst case under it. Nothing in chain/trade.ts, the gas floor, the wallet picker, the faucet test or the deadline was touched.

The second pass — literal

Asked for on review: the first pass reproduced the design's system and left every structural difference alone, and it did not read as the design. The direction given was as literal as the data allows, and the illustrations to be commissioned rather than substituted.

  • The lab is one grid of ten, with the design's card anatomy: tinted head bleeding to three edges, gold last word, the sentence at nearly full strength, and a foot of a gold-rimmed + beside a wide outlined action. The + reveals where the mechanic runs. What separates the five that exist from the nine that do not is the card, not a heading — drained tint, dashed border, "Coming soon", a label where the action would be — so the difference survives being scrolled past. The tenth card the design draws, On-chain TWAP oracle, had been missed.
  • The token page figures became one rule-divided line; the chart and the prose share the left column and the trade panel takes nearly the other half. The disc between the two boxes turns the trade around and clears the amount with it. The panel's foot carries what it is paired against and how close the curve is to closing. The price shows its 24h move — one more column on a launch_stats select the page already made.
  • The create form is one panel, two numbered steps and the action inside it. It now collects a logo, a description and socials, a pairing select with its threshold written underneath, and one fee number until "Advanced" asks for two. The right-hand card became what a stranger will meet.
  • The front page is the hero and then the catalogue, which is what the design's is. The catalogue moved to catalog/Catalogue.tsx and both screens render it, so the table cannot gain a column on one and not the other.

Decided in the second pass

  • Metadata is collected before the launch and written after it. The design's form asks for a picture and a description alongside the name, and so does ours now. It cannot be saved any earlier: the row is keyed by the token's address and the edge function asks the factory who the creator is, so there is nothing to sign against until the transaction has landed. The form holds it; the screen afterwards offers one signature for the lot, and never appears if nothing was typed. This is a behaviour change, made deliberately and on request.
  • The podium and the endorsement bands became one strip. The design has neither, and two bands of cards above a 305-row table were competing with it. What we chose is not what the chain says, which is the only editorial thing on the page, so it survives rather than being deleted — the podium's three keep their rank. One component to delete if it should go entirely.
  • The stats row and the trade ticker stay on the front page. The design has no slot for either; both are read from the chain.
  • Trade presets stay percentages. The design's are fixed amounts, which need a sensible magnitude per quote asset that nothing here knows.
  • The lab's illustrations have to be drawn. They are not assets: the pack builds its versions from about fifty class names of divs under 3D transforms in playful-lab.css, animated by visual-lab.js — code the brief rules out vendoring, and drawn for the design's nine subjects rather than our five. The slot is built to its dimensions and globbed, so saving an SVG into app/src/assets/mechanics/ is the whole of adding one. That directory's README is the contract.

How to compare the two

A scratch page at 127.0.0.1:8770 renders the design and the app side by side, each laid out at a real desktop width and scaled to fit its column, with route, width and layout switches. Built because judging fidelity from two windows at half width compares each one's narrow layout, and Chrome will not resize below its minimum.

The third pass — measured, not read

Reviewed again and the palette, the type, the sidebar and the "glass" were all called out as wrong. They were. The mistake was writing tokens.css from the design pack's stylesheets — a dozen last-wins layers where what a rule says and what the browser paints are different things.

The fix was to serve a copy of the compiled design from the same origin as a probe page and read getComputedStyle off the live document. Everything below came out of that, and it is how any future question about this design should be answered.

WasIs
Page ground#111e18 only#2d595f teal on html, #16312f on content
Panelsopaque #192920rgb(16 38 43 / .86) + backdrop-filter: blur(10px)
Accentgreen #a3c791gold #c5a46a — green is only a price going up
Ink#f0eee1#f2e9d6
TypeFraunces + ManropeHelvetica Neue at 14px
Headings800, -0.5px500, -0.04em
Table head11px9px, rows 52px
Rail56px, muted icons72px, 44px icons at 13px radius, gold at rest
  • The design sets 2,542 elements in Helvetica Neue and six in Fraunces. It ships Fraunces and Manrope from an earlier draft and overrides them nearly everywhere. The brief said both stacks were shared with the app; that was true of the pack's files and not of its output. Helvetica Neue is an Apple font — Windows and Linux fall to Arial. That is the design's own stack and worth a decision at some point.
  • The glass is backdrop-filter, not transparency. Every surface that sits over the page is frosted; a translucent colour without the blur is a see-through box. A worn canvas (assets/page-grain.jpg, extracted from the pack) sits fixed over the page at 9% in dark and 16% in light.
  • The sidebar logo was cropped because .brand was 44px tall and the wordmark needs the design's 78px box.
  • docs.stadium.money is in the rail, as asked.
  • The portfolio became one summary panel with an allocation bar plus a tray of three views, instead of three stacked bands and three cards.
  • The watchlist became the catalogue narrowed to a saved set — Catalogue now takes only, a title and an empty state, and serves three screens.

Still open in this thread

  • The nine illustrations, and one more for each of the five real mechanics. The lab's cards have a correctly proportioned tint and nothing in it until they are drawn.
  • The design's stock pairing is not represented at all. The brief allowed keeping the stock logos if they were unmistakably not choosable; the strip shows only real quote assets instead. If the hero should still gesture at stocks before D31 lands, that is a decision to take, not an omission to fix quietly.
  • The hero paragraph is ours, not the design's. The design's says "paired with crypto or stocks" and that is not true of anything deployed. Worth a look when it is.
  • The lab has fifteen cards. Five real, ten benched. If that many unbuilt mechanics reads as over-promising rather than as direction, the bench is one BENCH array to trim.
  • Five of the six things the design had no slot for are gone, removed on request after their cost was put in writing. The sixth came back; the rest are worth re-reading before launch:
    • The mechanics disclosure was put back before landing. It went with the other five, and then the public documentation written on master the same day turned out to promise it in four places — mechanics.md twice, intro.md and how-a-launch-works.md all say a launch's mechanics are stated on its token page. Removing it made the public site false about the one thing CLAUDE.md calls most important. So the design gains a section it does not draw, and the docs stay true as written. Five removed, not six.
    • The catalogue's source line. "305 of 305 · indexed" is how D38's fallback stays visible: with the indexer down the page still works and says fewer numbers, and that line was how a visitor could tell. A column of dashes now explains itself to nobody.
    • Also gone: the front page's three totals, its live trade ticker, the "Chosen by us" strip, and the lab's essay (the three seams, why the hook stays boring, the three conditions).
    • data/discover.ts keeps useCuration and useRecentTrades with no caller. The curation table and make dev-curate are untouched, so the strip is one component to restore.
  • The wallet write path still has not been exercised — unchanged by this session, and still the thing STATUS.md lists as outstanding.
  • Brand artwork is 132KB of PNG across two files, downscaled from the pack's 1.2MB. An SVG wordmark would be a fraction of that if the designer has one.