Back to studio index

Style Studio — Patterns

Pattern batches covering chat surfaces, row families, named patterns, and step rails.

Theme: light
Viewport: Desktop
Full-width stage

Batch 6 — Chat Surface

Chat surface migration references and audit fixtures.

Batch 6 active
approved for migration
The chat family needs to keep the scrapbook brand, but in a way people can live inside all day. The direction below keeps the materials and warmth, while pushing the chat surfaces toward a quieter desk-like rhythm: easier to read, calmer to navigate, and less novelty-driven. This direction is now approved; the next step is extracting the real ChatSurface owner(s) and migrating the live chat files onto them.
Functional first
Reading comfort and quick scanning beat decorative surprise. Every flourish has to justify itself after the tenth hour.
Materials, not gimmicks
Paper grain, note tones, and worn edges stay. Loud scrapbook theatrics get compressed into texture and selective accents.
Accent with restraint
Primary color should mark action, AI presence, and unread signals — not wash the whole chat shell.

Proposed Durable Direction

ChatSidebar
Quieter hierarchy, better scanning, and less visual shout while still feeling like paper.
Quiet desk mode

Messages

3 active
Keep the navigation calm and let unread states carry the signal.
MessageList
A calmer reading lane: soft separators, restrained chrome, and message content staying dominant.
Reading lane
Soft paper, low-contrast separators, signal only where it matters.
Assistant ready
Apr 17
J
Jules Harper

I still have the Olympus body available if you want to bundle the strap too.

12:14 PM

That works for me. Can you send one more photo of the lens mount?

12:18 PM
Assistant

Quick note: based on the recent comps, that body plus strap is landing in a fair range if the shutter is healthy.

12:21 PM
J
Jules Harper

Uploading it now. The mount is clean and there is no visible corrosion.

12:24 PM
Assistant is thinking
J
MessageBubble
Bubble hierarchy should feel authored, but not ornamental. Personality goes into texture and edge tone.
Durable bubble hierarchy
Keep the personality in the paper and AI accent, but make the reading rhythm feel quiet.
J
Jules Harper

I still have the Olympus body available if you want to bundle the strap too.

12:14 PM
Assistant

Quick note: based on the recent comps, that body plus strap is landing in a fair range if the shutter is healthy.

12:21 PM

That works for me. Can you send one more photo of the lens mount?

12:18 PM
MessageInput
The composer becomes a dependable writing surface instead of a decorative object.
Calm composer
The input should feel like a dependable writing surface, not a decorative prop.
EmptyStates
Empty chat surfaces should stay warm and brand-right, but never become louder than the real work.

Your conversations

Pick a chat from the sidebar to continue the conversation, or start a fresh one

No conversations yet

Start trading to begin chatting with other traders. Every trade starts with a conversation.

Chat with your assistant

Ask questions about trades, get recommendations, or explore the marketplace

GlobalAiWidget
The floating assistant stays distinct, but becomes calmer and more desk-like so it can live on-screen all day.
always available
Assistant

Hey wolfenstyne

Need a second opinion on a trade, or looking for your next swap?

Quick prompts

Assistant

Quick note: based on the recent comps, that body plus strap is landing in a fair range if the shutter is healthy.

12:21 PM
Assistant is thinking

Current-State Capture

ChatSidebar
Current shell with grain, header, search band, conversation list body, and footer CTA.
Messages
MessageList
Current paper well with date separators, received/sent/AI rows, typing, and AI thinking.
Apr 17
J
Jules Harper

I still have the Olympus body available if you want to bundle the strap too.

12:14 PM

That works for me. Can you send one more photo of the lens mount?

12:18 PM
Assistant

Quick note: based on the recent comps, that body plus strap is landing in a fair range if the shutter is healthy.

12:21 PM
J
Jules Harper

Uploading it now. The mount is clean and there is no visible corrosion.

12:24 PM
Assistant is thinking
J
MessageBubble
Current bubble treatments for received, sent, and AI messages before pattern extraction.
J
Jules Harper

I still have the Olympus body available if you want to bundle the strap too.

12:14 PM

That works for me. Can you send one more photo of the lens mount?

12:18 PM
Assistant

Quick note: based on the recent comps, that body plus strap is landing in a fair range if the shutter is healthy.

12:21 PM
MessageInput
Current notepad composer shell in default and compact modes, including the AI affordance.
EmptyStates
Current empty chat surfaces before we unify their paper shell language.

Your conversations

Pick a chat from the sidebar to continue the conversation, or start a fresh one

No conversations yet

Start trading to begin chatting with other traders. Every trade starts with a conversation.

Chat with your assistant

Ask questions about trades, get recommendations, or explore the marketplace

GlobalAiWidget
Static bench recreation of the current expanded widget panel, preserving its present shell and chrome.
Assistant

Hey wolfenstyne

Need a second opinion on a trade, or looking for your next swap?

Quick prompts

Assistant

Quick note: based on the recent comps, that body plus strap is landing in a fair range if the shutter is healthy.

12:21 PM
Assistant is thinking

Batch 7 — Rows

Row and micro-pattern family for trade rows, note rows, and toggle rows.

MediaRow

Foundational row. Everyone composes this. Tone × variant × state matrix; size + align knobs.

Tone × variant (size=md)
Neutral / soliddefault state, standard row
40 sites
Success / dasheddraft add
New
Destructive + markedpending removal
Neutral with placeholderno image available
Unread notification rowNew offer on your listing
Note tintvintage paper context
Size
sm — 32px thumbparticipant mini-row contexts
md — 40px thumbstandard row
Align — start vs center (multi-line subtitle)
Align center (default)Short subtitle, centered vertically against the 40px thumb.
Align start (multi-line)When the description wraps to two or three lines, start keeps the thumb anchored at the top of the row rather than floating in the middle of a long subtitle paragraph.

TradeItemRow

Trade-domain wrapper. state=removing|adding drives tone/variant; built-in remove/undo button cluster. Absorbs what the plan originally scoped as a separate PendingAdditionRow.

State — default / removing / adding (size=md)
Vintage Polaroid SX-70
Leather Messenger Bag
Original Nintendo Game Boy
New
Size — sm (mini-row in participant panel)
Moleskine Notebook (unused)
Moleskine Notebook (unused)
No image (placeholder)
Ceramic Mug

VintageNoteRow

Note-domain wrapper. Locks tone=note + paper typography. Sits inside vintage-paper surfaces. size=sm for compact detail sheets, md for full-page wants lists.

Full page — md, align=start, multiline description
Mid-century modern dining chairTeak frame, original upholstery preferred. Happy with matched pair if available; open to partial trade.
Furniture
Open to anything — surprise me!
Detail sheet — sm, truncate
Brass desk lampArt deco preferred
Lighting
Record crate (100+ LPs)Any genre, clean sleeves a plus
Music
Film camera kit
Cameras

KeyValueRow

Distinct geometry: leading indicator + fixed-width key + flex value + trailing actions. State drives shell tint; tone is the explicit override for confidence signals.

State — default / pending / verified / editing
material
Oak
dimensions
24 × 18 × 32 in
condition
Excellent — no scratches
Verified
material
Leather, hand-stitched
Label width — sm / md / lg
color
Walnut
designer / era
Unknown, mid-century
provenance / documentation
Original receipt included

ToggleRow

Label + description stack + trailing control. No leading media — this is not a MediaRow default. Two materials (standard / note) for forms inside vintage-paper surfaces.

Material — standard
Show in DiscoverToggle to hide or show this posting in the Discover feed.
Notify me of counter offers
Material — note (vintage paper context)
Open to tradeToggle to hide or show this posting in Discover.
Local pickup only

RemovableChip

Inline pill with × button. Not a row — a selection affordance. Two intents (accent / neutral) cover selected-user, selected-filter, and tag-with-remove contexts.

Intent — accent (selected-user, selected-filter)
Ada LovelaceAlan TuringGHGrace Hopper
Intent — neutral (tag/pill with remove)
vintageunder $50within 10mi

Composition without a wrapper — NotificationItem

Notifications have only one consumer in the app, so they compose MediaRow directly at the callsite rather than hiding behind a thin <NotificationRow>. Endgame principle: avoid generic wrappers.

New message from AdaRe: Eames Lounge chair offer
Trade completed2h ago
New review from Grace5-star rating · 1d ago

Batch 8 — Named Patterns

Named pattern tail for profile heroes, badge chips, and note surfaces.

GlassBar

Translucent glass-morphism shell for hero-class inputs. Not composed on Card — Card is for content panels, GlassBar is for floating chrome. Two consumers: SearchBar widget, AIEntryPrompt in the landing hero.

Size × aura — hero search shape
Intent — solid (80% bg) vs ghost (60% bg)
solid — bg-background/80
ghost — bg-background/60

ProfileHero

Tall page hero with avatar / identity block / action cluster. Page chrome (flat bg-background with border-b edge), not a Card surface. Used on the public trader profile; dissolves the prior anti-slop-exception div.

Full hero — avatar + identity + meta + actions
AL

Ada Lovelace

@ada

Vintage electronics + original mid-century books. Happy to pick up or ship within NYC.

Brooklyn, NY
Member since March 2025
withBorder=false — seamless hero (no edge divider)
GH

Grace Hopper

@grace

BadgeChip

Compact elevated chip — icon + title + caption + optional trailing. asChild Slot for Link/button passthrough. Collapses the trader page's two raw chip shapes (badges grid, recent trades list) into one primitive.

Badges grid — icon + title + caption (badges flex-wrap)

First Trade

March 3, 2025

5-Star Trader

April 12, 2025

10 Trades

April 16, 2025

ParticipantCard

Pinned index-card for chain participants. Owns the PushPin overlay + variant tint + current-user state that ChainParticipantList used to inline with two anti-slop exceptions.

Variant — solid (chain rail default)
AD
You(that's you!)
✓ Confirmed
GivesVintage Leica M3
GetsEames Lounge Chairfrom grace
GR
grace
Pending
GivesEames Lounge Chair
Gets1964 Gibson SGfrom alan
AL
alan
✓ Confirmed
Gives1964 Gibson SG
GetsVintage Leica M3from ada
Variant — ghost (transparent), pin=false
AD
ada
✓ Confirmed
GivesPolaroid SX-70
GetsMoleskine Notebookfrom alan
AL
alan
Pending
GivesMoleskine Notebook
GetsPolaroid SX-70from ada
Clickable — onClick adds hover tint
HO
hopper
Pending
GivesOriginal Univac Manual
GetsHP-35 Calculatorfrom neumann

VintageNoteItem

Paper-sticky-note surface container. Header + body slots, optional paperclip overlay. Highest-impact extraction in this batch — 8 raw paper-sticky-note divs across routes collapse into one primitive.

Header + body (wants sticky note shape)
I want...
Mid-century side tableTeak or walnut preferred
Pair of brass candlesticksArt deco era
Record crate (100+ LPs)
Header only, cozy padding (16px) — standalone callout
Looking for...

Open to anything — surprise me!

Compact padding (12px) + paperclip overlay
Notes

Compact padding with a Paperclip slot. The overlay positioning is owned by the Paperclip atom; VintageNoteItem just passes it through.

No header — pure body container

A plain paper-sticky-note panel with no heading. Used when the body already owns its own title typography (post detail sheet, etc.).

Batch 10 — Stepper

Closed-enum StepRail primitive and its migrated evidence sites.

Batch 10 — Stepper / Progress Marker Family

Three live evidence sites currently hand-roll a stepper shape. Goal: pick the API (Option A composable / B data-driven / C specialized names) before we author the primitives, then migrate all three sites.

3 live sites
planId 5A.5E
shipped — option B

Shape 1 — Horizontal numbered rail on paper

WizardStepRail (post creation, mobile). Numbered circles + check-mark completion, on paper-notebook surface. Connector is a horizontal progress bar behind the markers.

Today (raw)
drift — 3 sites
Category
2
Details
3
Photos
4
Review
Proposed (system)
A/B/C render the same
Category
2
Details
3
Photos
4
Review

Shape 2 — Vertical pinned stack on paper

NotebookStepItem (post creation, desktop sidebar). PushPin thumbtack for current step, filled dot for completed, hollow dot for upcoming. Red scribble-strikethrough over completed labels. Dotted vertical connector between steps.

Today (raw)
drift — 3 sites
Category
Details
Photos
Review
Proposed (system)
A/B/C render the same
Category
Details
Photos
Review

Shape 3 — Horizontal icon-chip row in chrome

Group-trade creation header. Compact rounded-pill chips with leading icon + (responsive) label, hairline connector between chips, on chromebar surface — not on paper.

Today (raw)
drift — 3 sites
Proposed (system)
A/B/C render the same

API options — A vs B vs C

All three render identical DOM. The decision is about authoring ergonomics, closure (can callers reach inside?), and how we handle the three visual registers (paper-numbered, paper-pinned, chrome-icon-chip).

Option A — composable
// Composable parts. Markers + connectors are the atoms.
<StepRail orientation="horizontal" surface="paper">
  <StepMarker state="completed" variant="numbered">
    <Check />
  </StepMarker>
  <StepConnector variant="bar" active />
  <StepMarker state="current" variant="numbered">2</StepMarker>
  <StepConnector variant="bar" />
  <StepMarker state="upcoming" variant="numbered">3</StepMarker>
</StepRail>
Option B — data-driven
// Data-driven. One JSX node, preset variants.
<StepRail
  variant="numbered"        // "pinned" | "iconChip"
  orientation="horizontal"  // "vertical"
  surface="paper"           // "chrome"
  steps={[
    { key: "category", label: "Category" },
    { key: "details",  label: "Details"  },
    { key: "photos",   label: "Photos"   },
    { key: "review",   label: "Review"   },
  ]}
  currentStep="details"
  completedSteps={["category"]}
  onStepClick={goToStep}
/>
Option C — specialized names
// Three named primitives, shared atoms under the hood.
<StepRail steps={stepsH} currentStep="details" completedSteps={["category"]} />
<StepStack steps={stepsV} currentStep="details" completedSteps={["category"]} />
<StepChipRow steps={chipSteps} currentStep="items" />
DimensionA — composableB — data-drivenC — specialized names
Callsite verbosityHigh — 2N+1 JSX nodesLow — one JSX node + steps[]Low — one JSX node per shape
API closureWeak — caller picks marker + connector variantsStrong — closed enum `variant`Strong — name locks the register
Accepting a 4th shapeAdd a new marker/connector variantAdd a new `variant` + another internal branchShip a new named primitive
Sharing internalsImplicit — all through atomsHidden — one file growsExplicit — atoms shared by 3 primitives
Author ergonomicsFeels like Radix — more code, more controlFeels like a chart component — least codeFeels like semantic HTML — right name for the job
Lint / anti-slopHard to forbid a bad combination (e.g. `dots` + horizontal)Easy — valid `variant` narrows legal statesEasiest — each primitive rejects invalid inputs
Migration cost (3 sites)Tallest — caller composes atomsShortest — assemble `steps` arrayShort — replace callsite with 1 primitive
Bundle / code shareBest — zero duplicationGood — internal fork inside one primitiveGood — three thin wrappers over shared atoms

Decision — Option B (data-driven) SHIPPED 2026-04-18

All three shapes fit a common data model: ordered steps, one current, rest completed-or-upcoming. That's exactly what Option B encodes. Closed variant + closed orientation + closed surface cover every combination we have and most we can imagine. Callers never arrange markers by hand; the primitive enforces visual consistency across the three registers.

Option A is tempting for composability but earns its keep only if we expect callers to need custom marker/connector arrangements — and we don't. Option C spreads the logic across three names for a three-shape inventory; if a 4th register shows up, we add a fourth name rather than a fourth variant, which grows the API faster than it has to.

Emergency hatch: if a callsite needs a shape that doesn't map to a preset variant (unlikely, but possible in the trade-chain redesign in Batch 10a), we promote the shape to a new variant — we do not add a className override. Option B stays closed.

Proposed shipped surface area:

  • <StepRail variant="numbered"|"pinned"|"iconChip" orientation="horizontal"|"vertical" surface="paper"|"chrome" steps={…} currentStep completedSteps onStepClick />
  • No standalone <StepChip> primitive. The icon-chip register is just variant="iconChip". The plan says ship <StepChip>only if the pill form doesn't collapse — it does.
  • Internal atoms (StepMarker, StepConnector) live in the same file but are not exported from the barrel — callers stay on <StepRail>.
  • variant="pinned" owns the PushPin marker + scribble-strikethrough + clickable back-nav contract already in NotebookStepItem.
  • User signed off on Option B — <StepRail> shipped atcomponents/patterns/step-rail.tsx. Three live evidence sites migrated: WizardStepRail + NotebookStepItem (both deleted as single-consumer wrappers, per Batch 7 endgame principle; NotebookStepper now composes <StepRail> directly) and the group-trade chip row in /trade/new/group.