Design system and UX specs
The UX specs for sesame-bun. PLAN.md §4 has the summary; these files are the detail that frontend engineers build from. The UI/UX designer owns this folder andfrontend/src/styles/tokens.css.
Principles
- Fewest clicks. Every task in the click-budget table (PLAN §4.1) is met on desktop and on mobile. Actions sit on the thing they act on (row actions, ladder header), not behind menus. Anything reachable only through a menu needs a palette command as well.
-
Instant feedback. Every click changes something on screen within one frame: an
order chip appears in
sendingstate, a button shows its pressed state, a switch shows its pending state. Server confirmation then replaces the pending state. Nothing waits for an animation. -
Never move a price under the cursor. A price, button or order chip stays at the
same pixel while the pointer (or a finger) is over its container. The rules that
follow from this:
- Ladder rows are fixed price levels. The ladder never re-centres while hovered or touched.
- Column widths are fixed and digits are equal-width. Columns such as P&L-if-closed and the pending-order dot keep their space even when empty.
- Lists defer insert, remove and reorder while hovered (see interactions.md).
- Status banners use reserved lines, never insert rows above content.
- Opening or closing a panel never shifts a price column that is left of it.
- One click, visible stake. A price click sends a GTC limit at exactly that price for the active stake, with no confirmation. So the stake is always on screen (top bar on desktop, stake bar on mobile), and hovering a price shows exactly what a click sends (the cursor tag).
- Stale data looks stale. When a feed lags, the last values stay visible but turn grey, their tint and flash stop, clicking them does nothing, and the panel shows the age. We never fill a gap from another source.
- Colour is never the only cue. Green is bid, buy, profit and Yes; red is ask, sell, loss and No. Every coloured value also has a position (bid column left, ask right), a label (Buy, Sell) or a sign (+, −).
- Same features on phone and desktop. Ladders become bottom sheets, right-click becomes long-press, hover becomes the always-visible stake bar. Touch targets are at least 44 px.
-
Calm, consistent layout. Controls in one row share one height (
--control-hon desktop,--touch-minon touch). Siblings share padding from the spacing scale, and a control is vertically centred in its bar. Each section has at most one container level; tabs, segmented controls and summary figures sit on the surface, separated by spacing or a single divider, not wrapped in extra boxes. Every UX review measures this at 1440 and 390 px (seeCLAUDE.md“Layout consistency”). -
Divider before box, simple before busy. Separate with a thin low-contrast line or
spacing; box only dialogs, sheets, popovers and toasts. Colour only for meaning, units in
headers, blanks instead of zeros, quiet status until it matters, and desktop row actions
on hover or focus (see
CLAUDE.md“Visual simplicity”). - Content never resizes the layout. Long names truncate with a tooltip, number slots reserve their widest value, live values never change an element’s size, and loading, empty and stale states keep the loaded dimensions.
Files
Conventions used in these specs
- Sizes are CSS pixels at 100% zoom. Desktop wireframes assume 1440 × 900, mobile 390 × 844 (iPhone 14 class) with safe-area insets on top of the stated heights.
- Price format. Prices show as cents:
42¢,42.5¢on a 0.001 tick. The ladder price column drops the¢sign to save width.lib/formatowns the formatting. - Money is pUSD, shown as
$1,234.56. Compact form in dense cells:$1.2k. P&L is always signed:+$12.40,−$3.10(U+2212 minus). - Sizes are shares: exact in your own orders (
59.52), compact in book depth (1.2k,12.5k,1.1M). - Token names are written without
var():--bidmeansvar(--bid). - Wireframe notation. No emoji or pictographic symbols, in wireframes either.
[name]in lower case is a lucide icon ([bell],[x],[chevron-down],[ellipsis]);[Label]in sentence case is a button;[ ]and[*]are an unchecked and a checked checkbox;(dot)is a 6 px status dot;[logo]is the app or team logo;|is a 2 px bar (selection, last trade, possession). Box-drawing lines and░depth shading are layout, not content. Labels are in sentence case, as in the UI. - “Armed” means the Safe/Armed switch is Armed and trading is enabled (not
geoblocked,
LIVE_TRADINGset). Every trade action in these specs assumes armed; the disabled behaviour is in components.md. - Out of scope: funding (deposit, withdraw, bridge, redeem, split, merge) and Perps. A resolved position links out to polymarket.com to claim.
Open questions for the tech lead
The specs follow the proposals below until the tech lead decides otherwise. None of them edit PLAN.md.--dur-flash(400 ms). PLAN §4.1 sets a 400 ms price flash; CLAUDE.md caps every duration at--dur-slow. Proposal: record the flash as the one exception in CLAUDE.md’s Motion section.- Portfolio row expand. PLAN §4.1 says “height and opacity”; the hard rules forbid layout animation on streaming rows. Proposal: height changes in one frame and the contents fade (motion.md). Update the PLAN table to match.
- Hotkey classes. CLAUDE.md says hotkeys are disabled unless Armed and Cancel all is
the only global one. Navigation keys (
Mod+K,Esc,?,gchords,F6,`) must work in Safe. Proposal: the Armed/focus rule applies to trading hotkeys only (interactions.md). - Browser arithmetic. CLAUDE.md puts money arithmetic in Go. The ladder’s P&L column,
the walk price, liquidation value, hedge figures and cursor-tag share estimates are
money values. Proposal: the backend sends them (per-level P&L can come with the
position in the ladder topic; walk price and liquidation value from
engine/portfolio); cursor-tag share counts are shown as estimates using a decimal library inlib/format, never sent. - Delay window cancels. Delayed orders cannot be cancelled. Proposal:
engine/ordersqueues the cancel and sends it when the delay ends (chipDelayed · cancel queued). Until decided, the UI reports which orders stayed. The contract also needs the delay end time (e.g.delayed_until) to draw the countdown. - Exit price. Proposal: Exit (the ladder header button and the portfolio action) sells the whole position at the walk price
(the lowest bid level that fills the full size), not the best bid, so one click gets
out. Large exits may then fall outside
price_band_ticks; consider exempting position-reducing sells from the band. - Ladder rows 28 px. The comfortable density is 36 px; the ladder alone uses 28 px on fine pointers (about 22 levels visible instead of 17) and 44 px on touch.
- Ladder dock scope. PLAN puts “up to 4 ladders” on the board. Proposal: the dock is shared by Live and Watchlist and persists across routes.
- Selling without a position is disabled (no automatic conversion into buying the other outcome), in line with “never convert a click”.
- Double-click guard. A repeat click on the same target within 350 ms reuses the
idempotency key, so the backend places one order. Confirm this with
engine/orders. - Contract gaps for later phases: notification settings (resolved in the Phase 4
contract:
NotificationSettings), layout state (dock height, docked ladders, grouping), the feed age inFeedStatusfor theStale 6slabel, and a cancel-only venue state for the status pill. - Palette ranking. PLAN gives pinned > held > live > volume. Proposal: apply it inside two text-match tiers, so a strong match always beats a weak pinned one.
- Mobile repricing in the ladder sheet costs one extra tap (select the chip, then tap per tick), because a tap on a level places an order. The Orders tab meets the budget exactly.