Back to studio index

Style Studio — Chains

Chain-specific presentation inventory and the zero-opportunity treatment pass.

Theme: light
Viewport: Desktop
Full-width stage

Batch 10a — Chain Surface

Chain opportunity teaser, rails, mystery cards, detail surfaces, and empty-state exploration.

Batch 10a — Trade Chain Signature Surface (starting-state inventory)

Chains survived as components but didn't survive as a flagship product surface. Current state: 18 chain-related files scattered across /chains, /trades, /discover, trade detail, chat, and the widget path. Today's canvas renders every chain presentation we have, inventories every fragment, and audits the state model so we know our starting point. No design decisions yet — just baseline and open questions.

18 fragments
8 states
4 canonical positions
plan 5A.5E-chain
starting state

Canonical family positions (target)

Per §5A.5E-chain, the flagship family has 4 canonical positions. Shows which current component fills each today — and where chains don't appear at all.

PositionRoute / contextCurrent implementationStatus
Teaser / CTA/discover (intended), home widget, my-corner bulletinTradeChainWidget → ChainDiscoveryAnimation; TradeChainPill (mobile expand). Widgets render only when chains exist.
Not on /discover
Opportunity rail / strip/trades sidebar (desktop) or horizontal strip (mobile)ChainOpportunitiesSection wrapping n× TradeChainClothesline (compact). Default collapsed ≠ current default.
Shipped
Mystery / reveal/chains hubMysteryChainCard primitive (@/components/patterns). CorkBoard + <TradeChainClothesline redacted /> silhouette; reveal CTA on cork; IconText-backed meta.
Shipped 2026-04-18
Full chain detail/trade/[id], dialogs, chat embedTradeChainClothesline (full, pinned polaroids on cork) + ChainParticipantList (vertical pinned cards) + ChainOpportunityCard (corkboard-wrapped compact clothesline).
Shipped — decide if clothesline stays signature

Current chain surfaces — rendered with mock data

Every presentation that chains take today. These are the starting visual state we'll evaluate and migrate.

MysteryChainCard
Shipped 2026-04-18 as @/components/patterns/mystery-chain-card. CorkBoard + <TradeChainClothesline redacted /> silhouette (same vocabulary as revealed variant). Reveal CTA on cork.
4-way chain
74% match
4

Reveal to see who's in it, what they want, and what you'd give and get.

ChainOpportunityCard (corkboard + compact clothesline)
Used by ChainOpportunitiesSection; adds pushpin + Join/Pass buttons.

A chain found you!

4 traders · waiting for Sarah W.

You
Vintage Guitar
Mike
Tube Amp
Sarah
Jazz Record Collection
David
Record Player
Vintage Guitar completes the loop
Everyone already expressed interest in the category
TradeChainClothesline (full, signature)
The 30KB flagship — pinned polaroids on a cork surface with SVG string between pins. This is the ~answer for the full chain detail position.
Smart Chain
1 of 4 joined
You
Vintage Guitar
Mike
Tube Amp
Sarah
Jazz Record Collection
David
Record Player
Vintage Guitar completes the loop
Everyone already expressed interest in the category
ChainParticipantList (vertical stack of pinned index cards)
ParticipantCard-based list. Shipped early-land in Batch 8. Used inside trade detail for the 'who's in this chain' view.
Y
You(that's you!)
Gives
?
Vintage Guitar
Gets
?
Tube Amp
from David L.
MS
Mike S.
Gives
?
Tube Amp
Gets
?
Jazz Record Collection
from you
SW
Sarah W.
Gives
?
Jazz Record Collection
Gets
?
Record Player
from Mike S.
DL
David L.
Gives
?
Record Player
Gets
?
Vintage Guitar
from Sarah W.
ChainDiscoveryAnimation (what TradeChainWidget renders)
Bypasses TradeChainWidget's auth fetch by passing mock ChainParticipantNode[] directly. Rebuilt 2026-04-18 to compose TradeChainClothesline — the mystery polaroid's flip is a first-class prop (mysteryIndex + isRevealed). Plays idle → flip → reveal ONCE, then stays revealed. No loop.
Smart Chain
Mike
Tube Amp
Sarah
Jazz Record Collection
David
Record Player
Scanning
TradeChainPill (mobile expandable)
Pill button collapses/expands a TradeChainWidget on mobile. Live render here — tap to expand. Expanded widget itself auth-gates so the inner content is empty without auth. The pill BUTTON is what's unique to this component.

Fragment inventory — 18 chain-related files

Every chain-related file in the tree with its current role and a starting-verdict placeholder. Most are TBD — we'll verdict during iteration.

FileRoleRenders atVerdict
components/trade/TradeChainClothesline.tsxLOCKED 2026-04-18. Signature visual language; owns mystery-flip + redacted-blur + loop-arc + chin typography. Every chain surface composes this.
Flagship — cork + SVG string + loop arc + mysteryIndex/isRevealed + redacted props/trade/[id] · ChainOpportunityCard · ChainOpportunitiesSection · MysteryChainCard · ChainDiscoveryAnimation
keep
components/trade/ChainOpportunityCard.tsxLOCKED 2026-04-18. Thin-but-named wrapper — adds the Join/Pass + 'View trade' actions + pushpin header to the flagship visual.
Corkboard-wrapped compact clothesline with Join/Pass buttons + pushpin/trades (via ChainOpportunitiesSection)
keep
components/trade/ChainOpportunitiesSection.tsxLOCKED 2026-04-18. Rail position. Chrome-gates the flagship inside a collapsible section.
Collapsible section: 'Smart Chains N' header + horizontal/vertical rail of compact clotheslines/trades · /chains (vertical layout for revealed)
keep
components/trade/ChainParticipantList.tsxBatch 8 early-land shipped. Uses ParticipantCard primitive. No known drift.
Vertical stack of pinned participant index cards (ParticipantCard)/trade/[id] detail — 'who's in this chain' view
keep
components/trade/ChainConfirmDialog.tsxLOCKED 2026-04-18. Composes the flagship via mapChainNodesToClothesline; imports ChainNode from TradeChainClothesline.
Modal dialog for confirming chain participation — uses TradeChainClothesline/trades (via confirm flow)
keep
components/trade/ChainNotificationBadge.tsx
Small badge for chain notificationsnav / notifications
TBD
components/trade/DiscoveryChainBadge.tsxCould become part of the teaser family.
Badge shown on discovery surfaces when a chain involves an item(board cards / discovery)
TBD
components/widgets/TradeChainWidget.tsxLOCKED 2026-04-18. Thin — fetches + transforms, delegates render. Mystery is YOUR item (reveal = 'your item made this chain possible').
Data-fetching wrapper → ChainDiscoveryAnimation. Picks mysteryIndex = user's own node.home widgets · my-corner bulletin
keep
components/widgets/TradeChainPill.tsxLOCKED 2026-04-18. Progressive disclosure. Collapsed pill → expands TradeChainWidget. Composition only — no render of its own beyond the pill button.
Mobile-friendly expandable pill around TradeChainWidgetmobile home / bulletin
keep
components/animations/chain-discovery/ChainDiscoveryAnimation.tsxLOCKED 2026-04-18. Rebuilt as thin composition over TradeChainClothesline (was ~580 lines, now ~200). Mystery flip = mysteryIndex + isRevealed props; reveal payoff = MATCH! stamp + CTA state swap. Plays idle→reveal ONCE per chainId (no loop).
Teaser variant: composes TradeChainClothesline + mystery flip + MATCH! stampinside TradeChainWidget
keep
app/chains/page.tsxLOCKED 2026-04-18. Uses @/components/patterns MysteryChainCard (extracted) + ChainOpportunitiesSection. Redirects to /discover when empty (zero-opportunity gap still open).
Hub route: mystery panels + revealed strip + 'how it works' details/chains
keep
app/trades/page.tsxChain sidebar = rail position. Already uses ChainOpportunitiesSection.
Trades list + chain sidebar/trades
TBD
app/discover/page.tsxTODAY: no chain presentation. Plan calls for a teaser/CTA here.
Discovery feed/discover
gap
components/chat/TradeChat.tsxNeeds audit for chain-specific rendering.
Trade chat view — chain-context treatment when trade is a chainchat contexts
TBD
components/trade/TradeDetailView.tsxMain detail host. Composition of existing pieces.
Full trade detail — embeds clothesline + participant list for chain trades/trade/[id]
TBD
components/patterns/participant-card.tsxAlready shipped. Thumbtack + solid/ghost variants.
Pinned index-card for chain participants (Batch 8 early-land)ChainParticipantList (and future chain-detail family)
keep

State model — 8 states across the chain lifecycle

The plan calls for normalizing these states across all chain surfaces. Today's coverage is partial; gaps flagged below.

StateMeaningRepresented todayVerdict
hiddenNo chain opportunities — user doesn't know chains existWidgets don't render (return null); /chains redirects to /discover; no CTA on /discover.
gap
Chains disappear. Plan calls for a 'chains coming soon' persistent teaser.
teaserChain marketing surface — 'chains exist, here's what they are'No dedicated teaser. 'How chains work' details inside /chains only.
gap
Needs a surface for /discover + home widget comeback.
mysteryA chain exists for the user, but they haven't paid to reveal itMysteryChainCard on /chains (inline, not a primitive) + blurred preview strip.
redesign
Extract into a named primitive; align with flagship language.
revealedChain unlocked — user can see everyone/everythingChainOpportunitiesSection (vertical layout) inside /chains; ChainOpportunityCard on /trades.
keep
Shipped path works. Decide if clothesline stays signature.
joinableUser is invited to participate — Join/Pass action availableChainOpportunityCard buttons + ChainConfirmDialog.
keep
Dialog may need redesign for flagship consistency.
in-flightChain joined, waiting on other participantsChainOpportunityCard status text ('waiting for Sarah W.') + ChainParticipantList with per-participant WaxSeal (confirmed/pending).
keep
WaxSeal per participant works well. Audit copy consistency.
declinedChain broken — someone passed or the chain expiredChainState='declined' in TradeChainClothesline; 'Trade declined' status label.
keep
String break visual + red pen tone. Reusable.
expiredChain opportunity aged out before joining(unclear — check API / backend state)
gap
Not visually distinguished from declined today. Needs its own treatment.

Zero-opportunity gap — chains disappear from the product

Today, when a user has no chain opportunities, chains are invisible:

  • /chains redirects to /discover (chains/page.tsx:67)
  • <TradeChainWidget> returns null when no data
  • <TradeChainPill> hides when no chains
  • /discover has no chain presence at all — not even marketing copy
  • <ChainOpportunitiesSection> supports an EmptyState, but the parent only mounts it when count > 0 unless showWhenEmpty is set
  • The plan flags this explicitly: "Revisit discoverability, especially the zero-opportunity case, so chains do not disappear from the product when no live opportunities exist."

    Starting questions — to resolve before authoring

    Design questions that need decisions before we ship new primitives. Each locks direction for the flagship family.

    Q1.

    Which fragments survive as-is, which get redesigned, which get deprecated?

    18 chain-related files. Verdict table above is mostly TBD — needs to be filled before we can propose a new family.

    Q2.

    Is the clothesline the signature visual language for chains across all positions, or reserved for detail?

    Currently the clothesline is used everywhere (full, compact inside cards, inside sections). Flagship family may want a simpler mark for rails/teasers and reserve the clothesline for the full detail surface.

    Q3.

    Does the mystery/reveal surface get its own primitive, or compose from the clothesline + a lock overlay?

    The MysteryChainCard is inline + has a blurred preview strip. Could become <LockedOverlay>-wrapped clothesline OR a distinct named primitive.

    Q4.

    What does the teaser look like on /discover and in the zero-opportunity state?

    Today /discover has nothing about chains. Plan calls for a teaser/CTA but no prior art exists — this is fresh design territory.

    Q5.

    How many canonical primitives does this family expose via the barrel, and what are they named?

    Candidates: <ChainTeaser>, <ChainOpportunityRail>, <ChainMysteryCard>, <ChainDetailSurface>, <ChainParticipantList> (shipped). Need to decide the public surface area.

    Q6.

    How does the state model flow (hidden → teaser → mystery → revealed → joinable → in-flight → declined → expired) actually render? One component with state-driven variants, or separate components per state?

    Batch 10's StepRail used closed-enum variant for 3 registers. Chains have 8 states + 4 positions = 32 combinations. Not all are valid, but the combinatorics are large.

    Q7.

    What's the animation handoff? ChainDiscoveryAnimation is 24KB of motion. Stays in the family, or moves to Batch 15 animation followon?

    Plan §15 Animation Handoff mentions routing residuals to a dedicated follow-on. Chain-discovery animation may qualify.

    Q8.

    Widget vs. pill — do both survive, or does one absorb the other?

    TradeChainPill is a mobile progressive-disclosure wrapper over TradeChainWidget. Could consolidate if the widget is responsive.

    Next session starts here

    From this inventory, we need to:

  • Verdict each of the 18 fragments (keep / redesign / collapse / deprecate).
  • Decide the canonical primitive set + names.
  • Design the zero-opportunity and teaser surfaces (fresh — no prior art).
  • Build mockups for each canonical position + state, ping-pong visually.
  • Migrate all callers and retire deprecated paths.