Design SystemPlayground

The Playground

Every primitive that composes the LI.FI Playground — the shell that frames it (rails, stage, view-rail, Theme Composer FAB) and the swap / bridge / limit widget it hosts: modes, layout, cards, pickers, quotes, orders, and the price chart. The live interactive build is the Playground app; this is the catalog. Press ⌘K to search the whole system.

Composition
Playground composition
The Playground is the shell that frames the widget — a left customization rail, the widget stage in the center, an optional right variants rail for live A/B comparison, and the Theme Composer FAB in the bottom-right corner. The cards below document the playground-specific pieces; the shared app-shell chrome (side-nav, drawer handle, command palette, FAB) is documented on Components and linked from the map. The widget primitives the stage hosts are nested in the groups that follow.

Playground anatomy

The whole composition — left rail, widget stage, variants rail, and the Theme Composer FAB. Each region links to its primitive.

This map mirrors the live build at playground.html. The shell is a fixed left rail + a scrolling canvas that holds the widget stage; an optional variants rail docks on the right, and the shared Theme Composer FAB floats bottom-right. The left and right rails are playground-specific (documented here); the side-nav, drawer handle, command palette, and FAB are shared with Portal and documented on Components.

Schematic, not to scale — click any region to open its documentation.

Composition
Playground rail
Floating glass panel pinned to the page edge — a drill-down customization sidebar for the swap widget. A multi-screen container that composes a head, sections, setting rows, mini theme cards, and a footer action strip into a single 384 px column. Two cards below: the shell anatomy, then the multi-screen drill interaction contract.

Playground rail .playground-rail

The shell — a fixed-position glass panel that hosts the rail's head, screens, sections, and footer. Composes the universal .panel for its surface chrome.

The mock below is the rail's actual root-screen markup from playground.html, with two surgical changes: position: fixedposition: relative on the rail and handle (so they live inside this documentation viewport instead of floating over the catalog page), and JS hooks (id attributes, data-drill attributes, click handlers) stripped. Every visible part is a real composed primitive — Brand lockup, Setting row, Theme card, Action bar, Drawer handle. Theme cards in the live rail are populated by JS from localStorage; the mock hardcodes three.

Live composition from playground.html. Two surgical changes for the static mock: position: fixedposition: relative overrides on the rail and handle, and stripped JS hooks (ids, data-drill attributes). All visible chrome is real .playground-rail__* CSS from swap.css.

Multi-screen drill data-screen · data-screen-id

Drill-down contract — sibling screens inside the shell, one visible at a time, toggled by a single data attribute. Footprint stays stable.

Root screen mock — the rail's top-level menu. Clicking a drill row (highlighted below) sets data-screen="layout-height" on the rail, swapping the visible .playground-rail__screen for the matching sub-panel; the back arrow restores data-screen="root". No layout reflow — the shell footprint is identical because every screen renders inside the same fixed-height container.

data-screen="root"

Click the highlighted row → data-screen="layout-height".

Composition
Variants rail
Right-docked glass panel that hosts Variant cards for live A/B comparison. Workshop tooling for design feedback sessions — click a card, the live widget swaps to that option, decision happens in front of the room. Mirrors the playground's left rail mechanics with two flipped defaults: default-collapsed (opt-in) and drawer-handle on the rail's left edge.

Variants rail .ds-doc-variants-rail

Glass panel · slide-in from right · hosts a labeled group of .ds-doc-variant-card items. Consumer wires positioning and the variant-switch handler.

A contained rendering of the rail at its natural width (384 px). The live primitive is position: fixed and slides in from the viewport's right edge — see it in flight in the playground. The drawer-handle in the preview is decorative; in production it docks at the rail's left edge and shares the slide transform.

Modes & Layout
Modes & Layout
The widget's mode set and structural frame — Bridge / Swap / Limit / Gas / Private modes, presentation, dual layout, and the .ui-card shell. Every widget primitive carries the .ui- tier-3 prefix; the live build is playground.html.

Widget modes — Bridge · Swap · Limit · Gas · Private

Five modes share one .ui-card shell — each flexes vocabulary, picker route, and side panel. Private (Houdini-routed) also requires a receiving wallet.

Four modes, four compositions. Each silhouette below renders the mode in isolation — the tab strip is omitted so the mode-specific anatomy reads cleanly. The tab strip itself lives in the form's .screen-header--main when the playground's flow drill is set to "Default" (see playground.html).

Bridge · any token, any chain

Bridge

Send
0 Select
Receive
0 Select

Both sides fully independent — any token on any chain → any token on any chain. No chain mirror, no symbol mirror. The unconstrained mode.

Swap · token → token, same chain

Swap

Send
0 Select
Receive
0 Select

Both sides snap to the same chain on every pick. Standard tandem chip (token + chain badge) on both sides.

Limit · trade at a target price, same chain

Limit

When ETH is worth USDC
2227.17
Market +1% +5% Custom
Sell
0 Select
Buy
0 Select
Expires in 7 days

Market price

on Ethereum
ETH
Ethereum
$2,280.50
2.07%6h 2.47%24h

Adds limit-price card + expires-in row to the form. Side panel swaps from quote list to market price + chart. Slot labels reframe to Sell / Buy.

Gas · top up native gas on a destination chain

Gas

Send
0 Select
Receive
0 on Arbitrum ETH

Gas is the only mode with a chain-only picker route — the receive chip stays a tandem, with the token auto-locked to the destination chain's native gas (ETH on Arbitrum, POL on Polygon, BNB on BSC, …). The chain list is limited to chains with a native-gas avatar.

Private · any token, any chain — via Houdini Swap

Private

Send
0.5 ETH
Receive
1,121.57 USDC
Receiving wallet
Private
1,121.57 $1,120.85-0.49%Houdini Swap

Route

Network cost
$2.87
Bridge fee
$2.55
Price impact
-0.49%
Max. slippage
0.30%
Min. received
1,117.87 USDC
1 ETH ≈ 2,242.46 USDC Estimated network fee, paid in the source chain’s native token. $2.87 Estimated time from signature to settlement on the destination chain. 10m

Bridge's any-token / any-chain mechanics, routed via Houdini Swap (privacy aggregator). A single route, so the quote renders as a compact inline .ui-quote-card in the form — after the receiving wallet — and the separate route panel is suppressed; its header chip reads shield + “Private” (not “Best return”). Once connected, the .ui-destination-wallet-card is always shown and a receiving address is mandatory: the zero-state shows an inline address input (type / paste, or “Select” to open the picker) with a “Required” warn chip beside the label, and the Review CTA stays disabled until it's filled.

Presentation — View rail · Single

How the swap widget exposes its flows — a 2-tier View rail (Simple / Advanced), or a title-only Single lock.

The widget's flows are exposed two ways, chosen from the playground rail's Presentation drill (body[data-presentation]): View rail (a 2-tier left rail — Simple / Advanced — that drives a segmented .seg tab bar in the form header) and Single (one widget type, title only). The same buttons drive applyMode in either presentation — only the chrome differs. Switch between them in playground.html. (Replaced the former Mode + Navigation drills in May 2026; the 4-mode icon rail and the standalone Tabs presentation with its .seg-menu overflow chevron were retired — the split-tab primitive itself lives on at .seg-menu for future overflow modes.)

View rail · .widget-stage__view-rail + .side-nav--rail

Two tiers on the panel surface (--surface-raised + --elev-3). Simple (shuffle) → Adaptive layout, tabs Bridge · Private · Gas; Advanced (candlesticks) → Dual layout, tabs Swap · Bridge · Limit.

Dual layout — form + persistent side panel

Persistent two-panel layout. The right panel shows the route list (Swap/Bridge/Gas) or the chart (Limit), with an honest idle placeholder.

Dual is the persistent two-panel layout (set from the Layout drill, or via the View rail's Advanced tier). The form + quick-settings sit in the left column; an always-visible panel fills the right. Unlike Adaptive — where the panel slides in only when a quote is ready — Dual's panel is a fixed grid column, so it (a) follows the canonical mode→panel mapping every layout uses, and (b) needs an honest idle state when there's nothing real to show yet. The three panel states are below; live two-panel switching across Swap / Bridge / Limit lives in playground.html.

Swap / Bridge / Gas · route list

Receive

Best return
99.74 $99.71Across
1 USDC ≈ 1.00 USDC $0.18 14s
Fastest
99.61 $99.58Stargate
1 USDC ≈ 1.00 USDC $0.21 9s

The full .ui-quote-list lives here — relocated to the aside like Adaptive, not collapsed into the form's quotes drill. Each row is a .ui-quote-card; the header carries a refresh-timer when routes are live.

Limit · market-price chart

Market price

on Ethereum
ETH
Ethereum
$2,280.50
2.07%6h 2.47%24h

.ui-token-info + .ui-price-chart — market reference, independent of trade state, so the panel renders the chart even before an amount is typed (you read the price to set a limit). The header offers an expand affordance instead of a refresh-timer.

No amount yet · idle placeholder

Receive

Enter an amount to compare routes

Swap / Bridge / Gas with no amount entered. Because Dual's panel is persistent, it shows the canonical .empty-state — never the chart, never faked routes. In production it carries [data-routes-idle] and is gated off data-quotes-state; the live route list replaces it the moment an amount makes a quote real.

Widget shell .ui-card

Outer container of the swap widget. Two-tier elevation hosts stacked sub-cards; data-state swaps inner content (form / picker / review / tx-progress).

Exchange

Send 25% 50% MAX
100 USDC
Receive
0.0449 ETH
Cards & Fields
Cards & Fields
The amount-entry surfaces — amount pair and amount cards, the destination-wallet card, card-title labels, and the quick-settings + settings-flow controls.

Amount pair .ui-amount-pair

Composes two .ui-amount-cards with a swap-direction FAB anchored on the seam. The FAB tracks the joint regardless of card heights.

Default · matched heights

Send 25% 50% MAX
100 USDC
Receive
0.0449 ETH

Asymmetric heights · FAB tracks the seam

Send 25% 50% MAX
100 USDC
Receive
0 ETH

Disconnected · both chips placeholder, “Select” label, meta hides

Send
0
Receive
0

Wallet-disconnected default. Both cards in .ui-amount-card--zero (derived values fade), both chips in .avatar-tandem--placeholder with “Select” copy, balance / fiat captions hidden.

Card label .ui-card-label

The widget's single-source card-title label — every card's top label reads at one treatment via the shared --ui-card-label-* tokens.

One treatment for every widget card-title label, single-sourced from --ui-card-label-*. The four below are the real consumers — identical size, weight, ink, tracking, and line-box. Change a token once and all of them follow.

Amount card · .ui-amount-card__label

Send

Limit price card · .ui-limit-price-card lead

When ETH is worth

Destination wallet card · .ui-destination-wallet-card__label

Receiving wallet

Received card (execution flow) · .ui-card-label

Received

Amount card .ui-amount-card

Send / Receive sub-card inside the widget shell — header + amount-with-token-chip + footer. Default surface: --surface-card + --elev-1, one elevation tier above the .panel shell. Composes .chip-avatar, .chip, .avatar.

Send variant · user input on the amount · hover for quickpicks

Send
USDC

Receive variant · read-only amount

Receive
0.0449 ETH

Zero state · .ui-amount-card--zero · no wallet, no typed amount

Send
0 USDC
Receive
0 ETH

Insufficient balance · data-state="error" · slide-in + icon swap

Send
USDC

Destination wallet card .ui-destination-wallet-card

Chosen send-to-wallet destination on the form — wallet avatar + ENS / address + clear ×. Replaces the footer wallet trigger.

Live, the card is display:none until .ui-card[data-destination-set] (a confirmed destination) — or, in Private mode, body[data-widget-mode="private"][data-playground-wallet-connected] (the receiving wallet is mandatory there). The demos below force display:flex so each state renders. The zero-state mirrors .ui-amount-card__body — an inline address input (left) + a trailing action (right) — with a “Required” warn chip beside the label.

Zero state · empty — “Select” opens the picker

Receiving wallet Required

Zero state · address entered — clear × replaces “Select”

Receiving wallet Required

ENS destination · resolved name over the short address

Receiving wallet

Pasted address · no ENS — short address leads, “External wallet” below

Receiving wallet

Quick settings .ui-quick-settings

Pre-CTA summary cluster of deep-link rows. Mirrors live values from the settings menu so users review intent before acting.

Each row is a .setting-row--sm .setting-row--drill with a data-quick-link pointing at a sub-screen of the canonical .ui-settings flow — click navigates the user INTO that setting's full screen, where the value can be changed. The right-column value is mirrored live from the underlying setting (data-settings-value), so the cluster reads as a summary the user can both review AND act on without opening the full menu first. The cluster is mode-gated, and Swap and Bridge carry DIFFERENT compositions — two sibling blocks keyed by data-quick-settings-mode, only the one matching the active mode shows. Swap runs 3 rows (route type / exchanges / slippage); Bridge runs 4 (route priority / exchanges / bridges / slippage). Limit is gated out entirely. The per-tab demos below force visibility via inline style on each wrapper (production gates on body[data-layout="dual"][data-widget-mode="swap"] and …[data-widget-mode="bridge"] respectively).

Compositions per tab

Swap tab · Route type · Exchanges · Max. slippage

Bridge tab · Route priority · Exchanges · Bridges · Max. slippage

Bridge runs its OWN 4-row block (data-quick-settings-mode="bridge") — Route priority (not Route type) plus a Bridges row, the two axes a cross-chain route exposes that a same-chain swap doesn't. Same gate, separate markup; only the block matching the active mode shows.

Limit tab · no cluster

In Limit mode the cluster is gated out — the .ui-orders-panel and the .ui-limit-price-card occupy the pre-CTA space, and Limit's price + expiry axes don't share the route‑type / exchanges / slippage vocabulary. The rows stay in the DOM (display: none); the mode selector flips visibility.


Icon visibility · orthogonal toggle

Icons-on opt-in · data-icons="on"

Default is icons-OFF (the resting state shown per tab above). Each row keeps its .setting-row__icon in the DOM so the toggle is pure CSS — the icon column collapses by default and reclaims its 20 px slot when data-icons="on" (or body[data-quick-settings-icons="on"]) is set. The Workshop "Show icons" card that drove the body-level gate was cleared June 2026 (icons-off is the settled default); both gates stay live CSS capabilities — per-instance consumers reach for the scoped attribute.

Settings flow .ui-settings

Scoped inner state machine. Lets one outer .ui-card state host a main list + N sub-screens via data-settings-screen.

Both demos host the same .ui-settings wrapper at 368 px (the swap widget's native width). The wrapper's data-settings-screen attribute is the only difference between the two cells — flip it and the visible [data-settings-content] swaps without re-mounting any DOM.

Main screen · data-settings-screen="main"

Settings

Hide small balances

Sub-screen · data-settings-screen="appearance"

Appearance

Pickers
Pickers
Token and chain selection — the token picker, its split-pane layout, and the chain-selection panel.

Token picker — full screen .ui-token-picker

Full picker screen composing .screen-header, .card-avatar-grid, .search, and stacked .token-list-module blocks inside .ui-card[data-state="picker"].

Picker screen at the canonical 416 px .ui-card width, in the picker state. Chain shortcuts sit above the search; the body stacks one or more .token-list-module blocks — here, Popular. Click any chain tile to filter the list; click the active tile to clear; click +60 to drill into the full .card-avatar--more chain picker.

Default · no chain filter active

Send token

Popular

  • ETHEthereum
  • USDCEthereum
  • USDTEthereum
  • DAIEthereum
  • WBTCEthereum
  • ARBArbitrum
  • OPOP Mainnet

Cold-state preview: All-networks tile is active (no chain filter), the full Popular module shows tokens across every chain, the body scrolls inside the panel's fixed height.

Arbitrum filter active · list narrows to one chain

Send token

Popular

  • ARBArbitrum
  • ETHArbitrum
  • USDCArbitrum
  • USDTArbitrum
  • DAIArbitrum

Filtered state: Arbitrum tile carries .is-active with the 1 px accent rim, the All-networks tile drops back to rest, the body re-renders to Arbitrum-only tokens.

Split pane .split-pane

Auxiliary card that slides in beside a primary container. Hosts the swap picker's chain filter and future route detail panels.

Chain selection panel .split-pane--chain-filter

The token picker's network filter in two compositions: a compact mini sidebar (Adaptive) and a balanced regular panel (Dual).

One node (.split-pane--chain-filter), two compositions, switched by layout via picker-render.js → applyChainPanelComposition(). Both filter the token list identically — click a network to narrow, click All networks to clear. Only the scale (search tier · row tier · avatar size) and the header differ. (Rows are normally JS-rendered; these demos are static at each composition's scale.)

Mini — Adaptive sidebar · .split-pane--chain-filter

320 px (--w-4) · .search--sm (32 px) · default-tier rows w/ .avatar--sm (32 px) · no header

Regular — Dual receive column · .split-pane--chain-filter--regular

416 px (--w-7, matches the picker) · .search (40 px) · .list-item--lg rows w/ default .avatar (40 px) · 64 px row · directional "From network" / "To network" header (40 px)

Quotes & Orders
Quotes & Orders
Route comparison and limit orders — the limit-price card, quote cards (full and mini), quote metrics, inline preview, and the orders panel + row.

Quotes

Limit price card .ui-limit-price-card

Limit-mode price anchor — "When BASE is worth" label + invert, value input, percent-preset chip row with an off-market alert past 10 %.

Default denomination — data-denom="base-per-quote"

When ETH is worth

Zero state · .ui-limit-price-card--zero · no token picked

When ETH is worth Limit price

No token picked yet (the playground cold-load). Mirrors .ui-amount-card--zero: price dims to --text-faint at constant weight, the label swaps to the static “Limit price”, and the invert chip + preset chips hide (no market rate to reference). The card stays for context.

Quote card .ui-quote-card

One offer in the routes list. Body collapses to amount + provider; chevron expands the fee breakdown.

Route comparison · tagged + untagged

Best return
0.041151 $92.707.27%1inch

Route

Network cost
$7.22
Provider fee
$0.04
Price impact
-7.27%
Max. slippage
0.30%
Min. received
0.041028 ETH
1 USDC ≈ 0.000412 ETH Estimated network fee, paid in the source chain’s native token. $7.22 Estimated time from signature to settlement on the destination chain. 11s
Cheapest
0.041245 $92.917.05%Odos
1 USDC ≈ 0.000412 ETH Estimated network fee, paid in the source chain’s native token. $7.01 Estimated time from signature to settlement on the destination chain. 12s
0.040673 $91.498.51%Bitget
1 USDC ≈ 0.000407 ETH Estimated network fee, paid in the source chain’s native token. $8.29 Estimated time from signature to settlement on the destination chain. 14s

Private mode · shield chip, single route

Private
1,121.57 $1,120.85-0.49%Houdini Swap

Route

Network cost
$2.87
Bridge fee
$2.55
Price impact
-0.49%
Max. slippage
0.30%
Min. received
1,117.87 USDC
1 ETH ≈ 2,242.46 USDC Estimated network fee, paid in the source chain’s native token. $2.87 Estimated time from signature to settlement on the destination chain. 10m

Private mode surfaces one Houdini route and renders it as a compact inline card in the form (after the receiving wallet); tagDisplay()’s private branch swaps “Best return” for the shield “Private” chip. Shown expanded — the inline card keeps its chevron and expands in place to reveal the route breakdown (unlike the Compact inline teaser, which strips the chevron and drills to the full list).

Mini quote card .ui-quote-mini-card

Limit-mode route picker. Slim sibling of .ui-quote-card — provider icon + name + gas cost only, no amount / time / conversion / impact.

Three routes · first card selected

Quote metric pill .ui-quote-metric

Shared "icon + value" route-metric primitive. Two tone modifiers (--fee · --eta); consumed by both .ui-quote-card footer cells and .ui-quote-mini-card rows.

.ui-quote-metric--fee · gas-cost cell

Estimated network fee, paid in the source chain’s native token. $7.22

.ui-quote-metric--eta · execution-time cell

Estimated time from signature to settlement on the destination chain. 11s

Inline quote preview .ui-quote-inline

In-form route surface — shows only the selected .ui-quote-card. In Compact it's a teaser that drills to the full list; in Private it's the only quote surface, so it keeps its chevron and expands in place.

Compact · teaser + drill (chevron stripped)

Best return
553.86 $553.67 1.69% Odos
1 ETH ≈ 2,215.43 USDC $9.04 11s

Compact: the card is the live .ui-quote-card.is-active with its expand chevron stripped (read-only teaser). Clicking anywhere drills to the full routes list.

Private · keeps the chevron, expands in place

Private
1,121.57 $1,120.85 -0.49% Houdini Swap

Route

Network cost
$2.87
Bridge fee
$2.55
Price impact
-0.49%
Max. slippage
0.30%
Min. received
1,117.87 USDC
1 ETH ≈ 2,242.46 USDC $2.87 10m

Private: single Houdini route, no drill. The chevron stays and toggles .is-expanded in place (wired by attachInlineToggleDelegate() on the .ui-quote-inline wrapper). No "Show all quotes" button.

Receive panel .ui-receive-panel

The receive-column frame: a Receive header above the route list (.ui-quote-list) in Swap / Bridge / Gas, or the market chart in Limit.

Swap / Bridge / Gas · the frame wraps the route list

Receive

Best return
0.041151 $92.707.27%1inch

Route

Network cost
$7.22
Provider fee
$0.04
Price impact
-7.27%
Max. slippage
0.30%
Min. received
0.041028 ETH
1 USDC ≈ 0.000412 ETH Estimated network fee, paid in the source chain’s native token. $7.22 Estimated time from signature to settlement on the destination chain. 11s
Cheapest
0.041245 $92.917.05%Odos
1 USDC ≈ 0.000412 ETH Estimated network fee, paid in the source chain’s native token. $7.01 Estimated time from signature to settlement on the destination chain. 12s

The frame is the constant; its body is a mode-gated slot. Here, the Swap / Bridge / Gas state — a screen-header--main titled “Receive” (with the .refresh-timer in its trailing slot) above the .ui-quote-list, each row a selectable .ui-quote-card. In Limit the same frame instead holds .ui-token-info + .ui-price-chart; in Dual with no amount entered, a routes-idle .empty-state. .ui-receive-panel itself is a pure composition marker — it carries no CSS of its own; the surface, elevation, and radius all come from .panel.ui-card.

Orders

Orders panel .ui-orders-panel

Limit-mode receive-column panel (a top-level frame, not an inner card) below Market Price. A 9-column .data-table--orders of the persona's limit orders — chain, pair name, amounts, limit, market, fill %, expiry, kebab. Scrolls horizontally via the .table-scroll primitive; the chain column pins.

Rich 9-column table · chain pins + scrolls when narrow

Orders

Chain Pair Sell Buy Limit Market Filled Expires
EthereumEthereum
ETHUSDC
0.800.80 ETH · $1,244
1,264.00
1,580
1,555
0%
6 days
ArbitrumArbitrum
USDCETH
1,500
0.9804
1,530
1,555
37%
3 days
BaseBase
BTCUSDC
0.05
2,970.00
59,400
59,319
100%
OptimismOptimism
LINKUSDC
800
6,800.00
8.50
7.21
0%
Expired
PolygonPolygon
UNIETH
1,200
2.04
0.00170
0.00154
0%
Cancelled

All nine columns at once; the table is wider than the 640 px card so the body scrolls horizontally (drag or shift-scroll to reveal the kebab). The identity is a pinned Chain column (the chain each order runs on — we support many) + a scrolling pair-name column: the fixed-width chain mark stays put while the variable-width SELL → BUY name scrolls with the values. The Expires column doubles as the lifecycle signal: active orders show the countdown, expired orders read “Expired” in muted grey, cancelled orders read “Cancelled” in --danger; filled orders stay blank because the 100% success ring already telegraphs it. (The retired dedicated Status column lived here as a separate .chip tier until June 2026 — the column-merge is the alternate cancelled-state integration.) The kebab here is a static placeholder — the live card opens a real .dropdown menu (see Markup).

Orders row

One order row — a single fixed column composition rendered through the shared renderRow from shared fixtures, the same renderer the playground uses.

The .ui-orders-panel table row is a single fixed composition — the 9 columns below, in COLUMN_ORDER, at --text-body (16px) type, the orders-table base look; the fill column is a plain % label. It's rendered live through window.LifiOrdersList.renderRow(order) from shared fixtures — the same renderer the playground uses, so the catalog can't drift from the product. (The row used to be a ROW_RECIPES registry with a default / compact A/B picker; both the picker and the registry were retired June 2026 once it dropped to one recipe — CLAUDE.md → Named-recipe registry.)

9 columns · --text-body labels

Transactions
Transactions
Post-quote transaction surfaces — modify an open order, the shared transaction summary, and the terminal result state.

Modify order modal .ui-modify-card

Stepped cancel-and-replace flow from the Orders kebab — edit → cancel → place; repeat skips the cancel. Pair locked.

The demos are static snapshots of the three modify-mode steps — the live stepped modal opens from the Orders-card kebab (“Modify limit” / “Repeat order”) in playground.html, prefilled from the clicked order. Repeat runs the same machinery as a 2-step flow (no cancel step). Shown for a partial order (45 % filled).

Step 1 · Edit terms

Step 2 · Cancel order — destructive confirm

Step 3 · Place order — receipt of the edited terms

Steps 2 + 3 confirm real transactions — the cancel signature lands before the place step, so there is no Back past step 2 in modify mode. Repeat mode shows a 2-disc stepper (Edit terms · Place order) and keeps Back on the place step.

Transaction summary .ui-tx-summary

The shared from→to flow card — every slot optional. Serves Review, the execution recaps, Activity, and Transaction details.

One card, two screens. The Activity list renders .ui-tx-summary--interactive items (the whole card opens Transaction details); the Transaction-details top card renders the base, non-interactive variant plus the rate + gas __foot. The chevron expands the Route inline in both. Composes .list-item, .avatar tandems, and .detail-list.

Route expanded · .ui-tx-summary

March 31, 2026 6:49 AM
4
$4.00 · USDC on Base
4
$4.00 · 0% · USDC on Arbitrum
Route Eco
Network cost
<$0.01
Price impact
0%
Exchange rate
1 USDC ≈ 1 USDC
Estimated time
0s
1 USDC ≈ 1 USDC <$0.01

Execution status .ui-tx-status

The widget's execution screen — hero mark + status copy + progressive step checklist. data-tx-outcome drives running / success / partial / failed.

One pane, four outcomes — data-tx-outcome on the .ui-tx wrapper picks the hero mark (80 px .spinner ring while running, .spot-icon-4xl --round tone disc on outcome), the lower card (from/↓/to pair vs Received), and the footer pair (See details + Done vs Remove + Retry). Steps are .status-row rows appended as the execution advances. Try it live on the playground — ⌥-click the Review CTA forces the failed path, ⇧-click forces partial.

Success · data-tx-outcome="success" — disc + steps + Received + See details / Done

Swap successful
USDC spending approved
Swap transaction confirmed
Sent to wallet: 0x92E5…EE74
Received
0.0447
$99.28 · ETH on Arbitrum

Failed · data-tx-outcome="failed" — danger disc + desc + failed step + Remove / Retry

Transfer failed

Please check the block explorer for more information or try again later.

USDC spending approved
Swap transaction failed
Market Data
Price chart
Live candlestick price chart — timeframe selector (1D / 1W / 1M / All) above a canvas with an annotated reference line for the user's limit price. Canvas-rendered by TradingView's Lightweight Charts library (Apache 2.0, ~35 KB); every paintable surface — candles, grid, axes, crosshair, font — resolves to a var(--*) token so the chart retints when the palette changes and flips automatically when the theme changes. Pairs with .ui-token-info above to form the full "what is this token, what's it worth, where has it been" composition; the spot price + deltas live in the token-info row, the chart carries the historical visualization. First primitive in the Market Data group; sibling chart types (line / area / depth / mini) compose the same timeframe + canvas chrome with a different series renderer.

Price chart .ui-price-chart

Token candlestick chart — timeframe quick-zoom (1D / 1W / 1M / All) above a Lightweight Charts canvas with limit-price annotation.

Every chart below is live — drag to pan, scroll to zoom, hover for the crosshair. Click a timeframe to re-range. The dashed accent line is the canonical limit-price annotation (set inline here at $2,253 since the playground form isn't on this page). Spot price + percent deltas have moved to .ui-token-info — see the Token info card below for the paired composition.

Market Data
Token info
Self-contained token snapshot — tandem avatar (token glyph + chain badge), ticker · chain name, spot price, and N percent-delta chips across time windows. Designed for reuse across any context that needs "what is this token, what's it worth, where has it been" at a glance — the Limit-mode Receive panel today, plus future token-info overlays (hover cards, popovers, sticky headers, portfolio rows). Three composable slots — __identity, __price, __deltas — let each consumer include only the rows it needs.

Token info .ui-token-info

Self-contained token snapshot — identity row + spot price + percent-delta chips. Composable slots; reuse in overlays and sticky headers.

Default — all three slots

on Ethereum
ETH
Ethereum
$2,112.40
0.07%5m 0.03%1h 0.45%6h 0.72%24h

Compact — identity + price, single 24h delta

on Ethereum
USDC
USD Coin
$1.0001
0.01%24h

Identity only — spot lives elsewhere

on Ethereum
WBTC
Wrapped Bitcoin
Checkout
Checkout widget
A partner-embeddable pay-in flow that takes an end user from "I owe X" to a confirmed on-chain deposit, funded by one of four sources (Pay from Wallet · Transfer Crypto · Connect Exchange · Deposit with Cash). It's a distinct widget type — a single-column, guided multi-screen flow — not an exchange mode, so it doesn't share the swap widget's two-column form+routes layout. Reuses ~80% of existing primitives; the genuinely-new layer is the flow panel, the per-method engine, and a few screen-specific compositions.

Checkout flow panel .ui-checkout

Host-agnostic guided pay-in flow — renders inline or in a modal; the funding source picked on the entry screen selects the sequence.

Static snapshot of the entry screen (Choose funding source). The full interactive flow — token/currency picker, connect wizard, amount entry, deposit address, the Deposit-with-Cash (Transak) path (fiat entry → the fixed-brand Transak sub-modal → pipeline checklist → completion), plus the cash error/refund states — runs live in the Playground (set Widget = Checkout in the rail). LI.FI screens wear a centered .screen-header (back · title · close) + a "Powered by LI.FI" footer; the Transak sub-modal wears its own brand (see Rules).