Perp
Component libraryPerp v2 · Component library

Component library

Atoms built from the Phase 1 tokens, with variants typed through CVA. Every control below is live — hover, tab and click them. Nothing here hardcodes a color or a size.

01

The state matrix

6 states per control

Every interactive atom implements the same six states. Disabled, error and loading are rendered statically below; hover, focus and active are live, so use a mouse and the Tab key to exercise them.

Default
Hover · live
Background steps to the 400 tint over 120ms.
Focus · live
Ring is shadow-focus, not the browser default.
Active · live
Disabled
Loading
Error
02

Buttons

6 intents · 3 sizes

Intent is semantic, never decorative. primary is the blue non-directional confirm; buy and sell are reserved for order actions and must never be used for a generic submit.

primary
buy
sell
neutral
ghost
danger
Committed destructive action — use inside a confirm dialog.
danger-ghost
Row actions. Neutral at rest, red on hover — a table of solid red buttons is alarm fatigue.
fullWidth
Icon buttons
03

Form inputs

Field handles a11y wiring

Field owns the id plumbing — label association, aria-describedby for hint and error, and aria-invalid — so no input re-implements it. Error replaces hint rather than stacking; two lines of guidance under one field is noise.

Letters and numbers only.

USD

Arrow keys nudge by the tick size.

SOL

Optional.

Selection controls

Checkbox
Radio
Toggle
Segmented
Leverage
5×
A native range underneath, so arrow keys, Home/End and mobile drag come free. The readout climbs the margin-health ramp — grey, then amber, then red past 8x — because the number is the risk, not the track.
04

Indicators & overlays

badges · dots · Radix overlays
Order status
PendingOpenPartially filledFilledCancelledNon-directional intents only.
Other badges
Liquidated10xLongShortbuy / sell are reserved for market direction — never for status.
Tag
SOL-USDOpen ordersLast 24h
Status dot
Connected ConnectedReconnecting ReconnectingDisconnected Disconnected
Avatar
TPTPSAPB
Scroll area
row 01row 02row 03row 04row 05row 06row 07row 08row 09row 10row 11row 12row 13row 14
The default for any scrollable region. Its scrollbar is an overlay and takes no layout width, so content cannot reflow when it appears — which is why raw overflow-y-auto is the exception here.
Tooltip
Initial margin
Dropdown menu
Arrow through it — items highlight identically for keyboard and pointer, because the style hangs off Radix's data-highlighted, not :hover.
Dialog
Focus is trapped and returned to the trigger, the background goes inert, body scroll locks, Escape and outside-click dismiss. The entrance animates transform only — restating the centring offset in the keyframe made it compose with Tailwind's translate and snap into place.
05

Waiting and absence

skeleton · empty · error

Three states that all look like “nothing here” and mean completely different things. The whole job of this group is to keep them distinguishable: a skeleton says data is coming, an empty state says there is none and this is normal, an error state says the request failed and here is what to do. At rest a skeleton block and an empty panel are the same rectangle — the sweep is the only difference, which is why it is a token rather than decoration.

Skeletons

Shapes
Text
Ladder
Rows are pinned to --size-row, the same token the real ladder uses, so nothing moves when the book arrives.
Table
MarketSizeEntryPnL

Empty states

In a table

No open positions

A position opens here as soon as one of your orders fills.

With an action

No collateral deposited

Deposit to fund your cross-margin account and start trading.

Compact (sm)

No prints yet

Trades appear here the moment the book crosses.

Error states

With retry
Compact (sm)
The error state is danger, never sell. Red means SHORT everywhere else on this screen, so the triangle glyph and the word carry the meaning and the colour only reinforces it. Retry is neutral — solid danger is reserved for a committed destructive action inside a confirm dialog.
06

Toasts

status ≠ direction

Fill confirmations are the reason this system exists, and they are where the two colour rules collide: a fill has a status and a direction, and both want to own the colour. They are split by job — the toast container carries status only, and direction lives in the body as a Side badge, which always prints the word. Click through them; they stack, pause on hover, and can be swiped away.

Fill · long
Neutral container, not success — the success token aliases the long green, so a green “Filled” on a SHORT fill would state the opposite of the truth.
Fill · short
Partial
Resting
A limit order that matched nothing. The quietest outcome the engine has, so it takes the outline badge — nothing has happened to this order yet.
Rejected
The one fill toast that keeps a glyph — a failure is not a direction, and the triangle carries it without hue.
Liquidated
The only order in the system nobody placed. It keeps the glyph and the longer dwell for the same reason a rejection does, and takes danger — non-directional, because a liquidation closes a LONG or a SHORT and the Side badge beside it is the only thing in the toast allowed to be green or red.
Status intents
With an action
Radix requires alt text on an action: a screen-reader user cannot race the timeout, so it must describe a durable route to the same outcome.
Burst · cap
Four stay on screen. A market order against a thin book fills in partials, and an uncapped queue buries the market bar.
07

Directional numbers

answers the Phase 1 caveat

This is where the colorblind risk flagged in Phase 1 gets resolved. Delta always renders an explicit sign and Side always renders a word, so direction survives with the color removed. The sign is not an option on the component — it cannot be turned off.

Delta
+4.96−2.310.00+2.48%−0.14%+1284.50 USD
Side
LONGSHORTLONG
Num
205.0918485368.61204.96
Grayscale
+4.96%−2.31%LONGSHORT← still readable with hue removed
08

The seam

signature primitive

A perpetual future is opposing pressure held in balance — longs against shorts, tethered to spot by funding. The most characteristic moment in the product is the middle of the order book, where bids pressing up meet asks pressing down and the last price sits on the join. Seam generalises that moment: two quantities grow toward each other and meet at a point that is the ratio. Information, not ornament — the balance is readable before any number is.

Bids 67%33% Asks
205.09
Long OIShort OI
+0.0142%
Margin usedAvailable
19%
BalancedBalanced
—
PriceSize
205.0641.48
205.0412.20
205.0141.48
205.09+2.48%
205.006.39
204.987.35
204.96245.09
67%33%
09

Composed

atoms under load

A real order ticket assembled only from the atoms above — no bespoke styles. This was the check that the primitives held together before anything was built on them; they did, and the organisms now exist. Three are on this page — the global header at the top, the ladder in The seam, and this ticket. The rest live in the terminal: market bar, order book, trades feed, chart and account tables.

Balance2521.00
USD
SOL
Order value971.51