Design SystemComponents

Component Catalog

Every DS primitive with a live preview up front; markup, specs, and rules sit one tab behind each card. Filter the sidebar by audience, or jump to a chapter within long sections.

Components
Action bar
Bottom-action region for any container surface — modals, drawers, bottom sheets, side panels, full-page flows, and the swap widget’s footer. Default lays buttons horizontally at equal width (iOS / widget pattern); a .btn-icon child stays square (auto-detect) so a primary + trailing icon reads as lead CTA + affordance; --stack flips to vertical with primary on top; --footer adds panel-footer chrome (top hairline + bottom-pinning) for peer-button strips with no primary. Universal primitive (May 2026 v13, v16, v17).

Action bar .action-bar

Bottom-action region. Three variants: default (equal-width row), --stack (vertical), --footer (panel-footer peer strip).

Default — horizontal, equal-width

Single button — fills the row

Lead primary + trailing icon — icon-only stays square

.action-bar--stack — vertical, primary on top

Three actions — equal third-widths

.action-bar--footer — panel-footer peer strip, no primary

Panel content above the footer…

Components
Alert
Inline tone-tinted message banner for page-level system notices, widget panel status, and modal body warnings. Four status tones (--info default, --success, --warn, --danger) consume the existing semantic token triplets (bg / border / fg) so the surface auto-flips light/dark with the rest of the system. Three sizes on the canonical --sm / default / --lg ladder. Sibling of Notification: same atomic composition (leading spot-icon + content + optional actions + dismiss), different placement contract — alerts sit in page flow at full container width; notifications float over content with a max-width cap and slide-in animation. Pick by placement, not message tone.

Alert .alert

Inline tone-tinted banner. Four tones; .alert--sm in widget panels, .alert--lg for hero CTAs. Sibling of .notification.

Small — .alert--sm · single-line for widget panels and modal bodies

Solana mainnet is now routable.

Quote locked in at 0.04% slippage.

Quote expires in 8 seconds.

RPC timeout — switching to backup provider.

Default — .alert · title + body, page-level

New chain supported

Solana mainnet is now routable. 40+ tokens available on day one.

Route confirmed

Stargate via Polygon → Arbitrum settled in 12s with 0.04% slippage.

High slippage detected

This route shows 1.8% price impact. Consider reducing trade size or waiting for liquidity to rebalance.

Approval failed

Spend allowance for USDC was rejected. Reconnect your wallet and try again.

Large — .alert--lg · hero banner for page-level CTAs

Widget SDK v2.4 released

Adds onRouteSelected event hooks, custom theming via CSS variables, and three new chains. Migration is non-breaking — see the changelog for opt-in flags.

Cross-chain swap settled

100 USDC on Polygon converted to 99.94 USDC on Arbitrum through Stargate. Final gas: $0.42. Receipt sent to your connected wallet.

MEV protection unavailable on this route

The chosen RPC doesn’t support private mempool submission. Continue anyway, or switch to a protected route from the list below.

Allowance below required amount

USDC allowance on Ethereum is 50 USDC but the swap requires 250 USDC. Approve the higher allowance or reduce the trade size.

Composition — with actions · with list · no dismiss

Quote expiring soon

Settlement window closes in 12 seconds. Refresh to lock in the current rate or accept the original quote.

Transaction rejected

The swap could not be submitted for these reasons:

  • Insufficient ETH balance to cover gas on Ethereum
  • Allowance below the required amount on the source token
  • Slippage tolerance exceeded between quote and execution

Persistent confirmation

No dismiss control — for permanent banners that should stay visible until the underlying condition clears (e.g. “Test mode enabled”).

Components
Avatars
Plated brand-mark and identity tile — chains, tokens, bridges, DEXs, wallets, social, LI.FI sub-products, plus initials and profile pictures. Six sizes (24/32/40/48/64/96), three shapes, an optional alpha ring, and three composition primitives — overlap stack (with +N overflow tile), token+chain tandem (corner badges for "X on Y"), and an avatar+name label pair. Cluster wrappers (.avatar-stack, .avatar-tandem) set --avatar-size at the wrapper so children inherit. Promoted to tier-2 in May 2026 once production consumers turned up — see Avatars library for the full set of source SVGs and Avatar list for the canonical row pattern.

Avatar .avatar

Plated SVG mark or letter monogram in square / rounded / circle. Six sizes; ring, stack, tandem, label, initial modifiers compose freely.

Sizes — xs 24 · sm 32 · default 40 · lg 48 · xl 64 · 2xl 96

Ethereum (24px) Ethereum (32px) Ethereum (40px) Ethereum (48px) Ethereum (64px) Ethereum (96px)

Shapes — square 4px · rounded 25% · circle 50%

Arbitrum (square) Arbitrum (rounded) Arbitrum (circle)

Ring modifier — 1 px inset alpha border, opt-in for low-contrast plates

Ethereum (no ring) Ethereum (with ring)

Overlap stack — counts of 2 / 3 / 4 at default size

Ethereum Arbitrum Ethereum Arbitrum Optimism Ethereum Arbitrum Optimism Base

Stack overflow — .avatar-stack__more for "+N more" truncation

Ethereum Arbitrum Optimism +5 Ethereum Arbitrum Optimism +12 Ethereum Arbitrum Optimism +99

Token + chain tandem — base avatar + corner badge(s)

USDC on Ethereum USDT on Arbitrum USDC from Ethereum to Arbitrum USDC on Arbitrum via Eco

Dual-badge variants (source + destination, token + chain + provider) are documented options — reserved. Product flow rows carry at most ONE badge (the chain); see Rules.

Tandem sizes — --xs 24 · --sm 32 · default 40 · --lg 48 · --xl 64 · --2xl 96

USDC on Ethereum (24px) USDC on Ethereum (32px) USDC on Ethereum (40px) USDC on Ethereum (48px) USDC on Ethereum (64px) USDC on Ethereum (96px)

Placeholder modifier — unselected slot, single + tandem

Pair with a “Select” label in the consumer chip — the empty form is the affordance. Never glyph-fill the disc.

Initials — monogram from a label, deterministic tone

Up to 2 letters (first + last word; single-token labels → 1). data-initial + a hash of the label pick the glyph and one of 12 --spectral-* tones, so the same label is the same colour on every surface.

Initials — 12 spectral tones (.avatar--tone-112)

AR ML SC DK PN TB WZ YS JK RA LM EN

Lightness is normalised to a deep, white-text-safe band in both themes — white glyphs clear WCAG AA (5.1:1+) on every tone.

Initials — sizes · --xs 24 · --sm 32 · default 40 · --lg 48 · --xl 64 · --2xl 96

VR VR VR VR VR VR

Rides the same six-tier --avatar-size ladder as the mark, tandem, and placeholder variants — same modifiers, same heights (24 / 32 / 40 / 48 / 64 / 96). The glyph scales with the disc (font-size: calc(--avatar-size × 0.4)).

Initials — --flat · --tone-neutral

LF SY

--flat drops the radial for a solid fill (crisper in dense table cells). --tone-neutral is the muted plate for non-human entities (System, pending invites).

Avatar + name — inline pair for tables, chips, list-row leads

Ethereum Ethereum Arbitrum Arbitrum USDC USD Coin
Components
Badge
Small floating indicator anchored to a corner of an icon, button, or avatar — route counts on a notifications bell, “NEW” on a chain marker, an unread dot on a wallet button. Distinct from .chip (a label in flow); .badge attaches as an ornament. Wrap the host in .badge-host for positioning context. Tones echo the .chip vocabulary (success / warn / danger / info / neutral) with brand accent as the default. Three size tiers — default 20px, .badge--lg 24px, .badge--dot 12px — deliberately sit off the system 32/40/48/56 sizing ladder because badges are corner-anchored ornaments, not type-aligned controls.

Badge .badge

Solid-fill indicator clipping a host’s corner. Three sizes, four anchors, five tones, plus a content-less dot variant.

Content — number · overflow · text · dot

4 99+ NEW

Sizes — default 20 · lg 24 · dot 12

4 12

Tones — brand (default) · success · warn · danger · info · neutral

4 4 4 4 4 4

Anchors — top-right (default) · top-left · bottom-right · bottom-left

4 4 4 4

Hosts — text button · icon button · square avatar · circular avatar

4 3 Ethereum NEW Base NEW
Components
Button System
Systematic button library with 4 brand-accent types (Primary, Secondary, Neutral, Tertiary), 2 semantic types (Destructive, Success), a borderless Link variant, 3 abstracted accent colors (Accent 1, 2, 3), filled and outline styles, 4 sizes (sm / default / lg / xl), and an optional trailing icon. Neutral is the workhorse tone — the most-used variant outside Primary, sibling of .chip-neutral in the tone vocabulary. Accent colors are theme-configurable via CSS custom properties.

Primary — Filled & Outline

Brand-coloured action buttons with optional outline. The default for primary CTAs across marketing and product surfaces.

Disabled · gated & busy

Button variants — secondary, neutral, tertiary, semantic, link

Lower-weight action buttons + workhorse neutral tone + semantic destructive/success + borderless link variants. All compose onto .btn-*.

Secondary .btn-secondary

Neutral .btn-neutral · workhorse tone · theme-adaptive currentColor overlay

Tertiary .btn-tertiary · ghost

Semantic · destructive & success

Link .btn-link · borderless

With trailing icon · .btn-with-icon

Icon-only · sizes (xs / sm / md / lg)

Inline links · .ds-doc-link

By submitting this form you agree to our Privacy Policy and Terms of Service.

Read the integration guide or explore our SDK documentation.

© 2026 LI.FI. Privacy · Terms

Sizes — sm / default / lg / xl

Three progressive ladders, all on tokens — heights 32 / 40 / 48 / 56, labels 12 / 14 / 16 / 18, radii 8 / 12 / 16 / 20.

.btn-sm — 32px tall · 8/20 padding · 12px label · 8px radius
default .btn-primary — 40px tall · 12/24 padding · 14px label · 12px radius
.btn-lg — 48px tall · 12/32 padding · 16px label · 16px radius
.btn-xl — 56px tall · 16/40 padding · 18px label · 20px radius

Lift modifier .btn-lift

Opt in to the hover lift + glow. Buttons are static by default — reach for this on marketing CTAs, rarely in product UI.

Hover both rows to compare. The top row is the default — a bare button holds its position on hover, paints only the bg / color wash, no movement, no glow. The bottom row composes .btn-lift: it adds the -1 px to -2 px lift (by variant) plus the soft cast-shadow glow. Static is the right default for product UI; the lift is mostly a marketing affordance.

Justify modifier .btn-justify

Label at the leading edge, arrow at the trailing edge. Composes with .btn-block + .btn-with-icon for the full-width CTA shape.

Every button below composes .btn-justify with .btn-block (full width) and .btn-with-icon (the trailing →). The label sits at the leading edge, the arrow at the trailing edge; the base gap holds them apart, so the label can't slide under the icon at any length.

Default · .btn-primary .btn-block .btn-with-icon .btn-justify

Explicit arrow-right child · renders identically to the default ::after

Secondary · .btn-lg (48 px) · long label clears the arrow

Accent tints · .btn-accent2 · .btn-accent3

Label button + icon .btn-with-icon

Label button with leading and/or trailing icons by DOM position; bare, it auto-appends a trailing arrow. Icon-only sibling: .btn-icon.

The label button reveals icons by DOM position: an <svg> before the label sits left, after sits right, both shows both. With no <svg> at all, .btn-with-icon auto-appends a trailing arrow (the library arrow-right as a currentColor mask). Padding stays label-optimized (asymmetric) — unlike the icon-hugging .btn-icon.

Trailing arrow · bare .btn-secondary.btn-with-icon

Leading icon · <svg> before the label

Leading + trailing · two <svg> (auto-arrow suppressed)

Swap-direction FAB .ui-swap-arrow

Direction-toggle FAB — tokenized for size, icon-size, hover-size; canonical consumer .ui-amount-pair.

Default · .ui-swap-arrow · 28 px button · 12 px icon · opaque page surface

Bare primitive, no modifiers — what you get out of the box.

FAB tier · .ui-swap-arrow--lg.ui-swap-arrow--glass · 32 px / 16 px / glass

The FAB tier modifiers. --lg re-pins the size tokens (28 → 40, 12 → 16); --glass fills the --swap-arrow-extra-shadow slot with the Panel Surfaces shadow recipe and swaps to the glass surface.

In context · .ui-amount-pair > .ui-swap-arrow

Real .ui-amount-card markup at the playground's 368 px width — true mask cutout, cards become transparent at the FAB.

Send 25% 50% MAX
100 USDC
Receive
0.0449 ETH

Cards rendered at 368 px (the same width they render at inside the playground's 416 px .panel.ui-card shell, after subtracting its 24 px padding). The cutout is a real mask-image on each card — same technique as .avatar-tandem badge cutouts. The cards are truly transparent at the FAB's footprint, so whatever surface sits behind them shows through (here, the demo wrapper at --surface-raised; in the real consumer, the .panel.ui-card's panel surface). The mask radius reads from --swap-arrow-size + --swap-arrow-cutout on the pair, so the cutout always matches the FAB geometry.

Components
Chips
Compact status indicators and category labels. Pills for static labels, delta indicators for percentage changes, themed variants for scope / brand / token contexts. Renamed from "Badges" in May 2026 — the badge name is reserved for the future circular-count indicator.

Pills

Chip .chip

Universal pill primitive — nine tones, four sizes, filled or outline. Optional .chip-icon slot on either side of the label.

60+ Chains Expired Active AI-Powered Agentic Route Found Pending Price updated Rejected
60+ Chains Expired Active AI-Powered Agentic Route Found Pending Price updated Rejected
default · 24h md · 32h lg · 40h xl · 48h
Mainnet Live Pending Open Filter Verified

Avatar chip .chip-avatar

Pill primitive with a leading .avatar slot — token chooser, chain chip, partner chip, wallet chip; sizes the avatar by chip tier via the cascade.

Size ladder · --sm / default / --lg / --xl · click to select

Tones · neutral / accent1 / accent2 / outline

Borderless · .chip-avatar--borderless · hover to reveal

No chrome at rest — only the avatar + label show. The tone fill reveals on hover (and press / focus), surfacing the pill’s footprint exactly as a filled chip-avatar would. Pairs with a <button> host, where the hover IS the affordance.

Single avatar (no chain badge) · chain pickers, partner chips

Selected · .is-selected · matches .list-item--selected

Placeholder · unselected picker, “Select” copy

Token + chain pickers use .avatar-tandem--placeholder; chain-only pickers use the single .avatar--placeholder. The chip's aria-label carries the affordance — the placeholder discs are decorative.

Locked · .chip-avatar--locked · read-only value, inert

ETH USDC Ethereum

Shows a committed value the user can’t change in context — the canonical case is the Modify-limit-order pair. Transparent fill + hairline outline + muted label; the avatar stays full-color so the token reads. Inert (pointer-events:none). NOT the disabled-control recipe (uniform 0.4-opacity dim).

Editable chip .chip-edit

Toggles between a "click to edit" label and an editable input. Composes any chip tone.

Default tone — .chip-neutral

Tone variants — click to edit any

Delta indicators

Delta chips .chip-delta-pos · --neg · --neutral

Numeric % change indicators. Composes with .chip; three tones (pos / neg / neutral) and four size tiers anchored to the type scale.

0.0% +12.4% −3.1% +$128.4M −421 users
+12.4% · default +12.4% · md +12.4% · lg +12.4% · xl
Standard KPI
$1.24B
Larger tile
$842M
Hero tile
$18.4B
Banner / print hero
$21.6B

Bare chips .chip--bare

Frameless chrome modifier — drops bg, border, padding, min-height. Sibling to .chip--outline on the chrome axis (filled / outline / bare). Canonical use case: inline price-readouts where the chip natively carries a muted time-window via .chip-window and shouldn't read as a separate UI element.

Bare chips render as tone-coloured inline text — no fill, no outline, no row-height. Each chip natively bundles arrow + value + an optional muted time-window via the chip-family .chip-window sub-slot. No outer pair-wrapper needed; the chip IS the cluster.

Price readout — 5m · 1h · 6h · 24h

0.15%5m 0.1%1h 0.53%6h 0.65%24h

Sizes — default 12 · md 14 · lg 16 · xl 18 px

0.15% default
0.15% md
0.15% lg
0.15% xl

Tones — .chip-delta-* + every .chip tone

+12.4% −3.1% 0.0% Settled Reverted Pending Mainnet

Themed

Components
Chip shortcuts
Cluster wrapper for chip-based preset-value pickers — 25/50/75/MAX percent shortcuts in a swap amount card, slippage presets in settings, fee tiers anywhere a finite set of preset values speeds up entry. Composes the canonical .chip family; sizes come from .chip--md / .chip--lg per child; the wrapper auto-adapts gap via :has() and offers an opt-in --reveal-on-hover staggered fade-in. Universal tier-2 primitive — usable on any surface with a paired numeric input.

Chip shortcuts .chip-shortcuts

Wraps .chip children as one-tap preset-value shortcuts. role="group" + aria-label carry the semantic; the chip family provides each item.

Sizes — via .chip--* children

Sizes come from the chip family, not a wrapper modifier. The wrapper detects the largest chip inside and bumps gap via :has().

Default · .chip · ~24 px (today's swap-widget look)

Medium · .chip--md · ~32 px

Large · .chip--lg · ~40 px (hero amount inputs)

Variable count

Cluster scales naturally to any number of children. Same wrapper handles 3, 5, or 6 items — no count-specific CSS.

3 items · slippage presets

5 items · gas-fee tiers

6 items · finer-grained percent shortcuts

Reveal on hover .chip-shortcuts--reveal-on-hover

Opt-in staggered fade-in from the right — chips hidden at rest; hover the parent to reveal one by one, rightmost first, 30 ms staggered.

Hover the swap-card mock below — the percent shortcuts fade in one by one from the right. Tab into the input to trigger the reveal via keyboard.

Send

$100.00 · Balance 2,645.15

Components
Code chip
Universal inline-code chip. A filled mono pill that every <code> on the site renders as automatically — no container class needed. Tier-2 universal: used in marketing prose, dashboard captions, product surfaces, and catalog docs. Promoted from tier-1 .ds-doc-class in May 2026 v10; the bare <code> element selector became the universal trigger in the same pass.

Code chip .code-chip

Filled mono pill — no border. Every <code> on the site renders as a chip; explicit .code-chip for non-<code> hosts. Two override contexts: label (quieter) and eyebrow (louder).

Any inline <code> — class refs like .btn-primary, modifiers like --space-16, file paths like shared.js, token names — auto-renders as a chip. No container class required; the bare <code> element selector is the universal trigger.

Brand button .btn-primary

Inside .ds-doc-card__label, the chip uses a quieter override (0.72em, weight 500, muted) so the noun phrase leads.

In-page · .eyebrow-numbered

Inside .ds-doc-eyebrow, the chip uses a louder override (1em) and the shared rule cancels parent uppercase + tracking so the class name stays lowercase.

Components
Drawer handle
Drawer-pull tab for any side-docked panel — playground rail today; future FAB drawer, side sheet, settings drawer use the same shape. Glass surface matches the panel it docks against. Promoted from .ui-rail-handle to a universal primitive in May 2026 v15.

Drawer handle .drawer-handle

28 × 72 tab with a chevron. Universal owns chrome only — consumer wires positioning and the state class that flips the chevron.

Three states of the same primitive. Default chevron points left (expanded — click pushes the panel away); when the consumer adds its state class, the chevron rotates 180° to point right (collapsed — click pulls the panel back). The handle is a sibling of the panel, not a child — that's how it can stay flush against the panel's edge during a translate transition.

Rest

Hover

Flipped · collapsed

Components
Empty state
Centered placeholder for a panel, card, or region that has no content to show — no token picked, no results, nothing connected. Composes the canonical .spot-icon (the consumer picks the glyph + size) over four composable slots: the icon, an optional .empty-state__title, the supporting .empty-state__message, and an optional .empty-state__action for a CTA. Omit the slots you don't need — icon + message is the minimal shape. The wrapper grows and centers, so dropping it into any flex / grid parent with height vertically centers the content (the Limit-mode .ui-price-chart “Select a token” placeholder is the reference consumer). The icon reads muted by default — this is “nothing here”, not an alert. It shares its centered-stack recipe with the .state-block sibling (full-color marks, natural height — for prompts and statuses rather than absent content). For content that's arriving rather than absent, reach for .skeleton instead.

Empty state .empty-state

Centered placeholder for a panel or card with no content — a spot icon over optional title, message, and action slots.

Icon + message

Select a first token to see its market price

Icon + title + message

No price data

Pick a token to load its live market price and 24h change.

Icon + title + message + action

No transactions yet

Your swap and bridge history will show up here.

Tinted icon · tone override

Couldn't load routes

We hit an error fetching quotes.

Components
Error detail
Machine-voice diagnostic echo — the .alert family's code-bearing sibling. A tinted callout carrying verbatim machine output (an RPC revert echo, a webhook response body, a failed-request excerpt) below a tone-bearing header that already announces the failure. Chrome derives from the alert recipe — the same --a-* tone slots, the same borderless pastel surface and --card-radius envelope — while the content speaks mono, because the payload is code, not prose. Deliberately carries no .spot-icon: it's tonal detail, not a standalone alert message (the meaning lives in its own text, never in the tint alone).

Error detail .error-detail

Tinted callout echoing verbatim machine output below a failure header — the alert family's surface with a mono code voice.

Two echoes at production scale — an on-chain revert (the Portal withdraw-fees error state, the reference consumer) and a webhook delivery failure. The component carries its own tinted contrast, so it sits bare on the pane.

Error · Insufficient_gas

execution reverted: gas estimation failed at block 218682389

Error · Delivery_failed

POST https://api.rabby.io/webhooks/lifi returned 500 after 3 retries · request id 0x3594…793b5

Severity is the component's identity — the slots pin to the danger triplet. A future warn / info echo rebinds the --a-* slots the way .alert--<tone> does; never hand-pick colours.

Components
Eyebrow
A small uppercase kicker that sits above a heading, definition row, or demo to provide context. Universal primitive — same shared base (12px · 700 · uppercase · wide tracking) shows up above page section titles, inside hero headers, above variant rows in catalog Preview panes, and as a label above cards and tiles. Two typeface voices — mono for architecture / API / pane labels, sans for editorial use-case kickers — and a small set of color recipes differentiate the six family members.

In-page eyebrow .eyebrow-code · .eyebrow-word · .eyebrow-numbered

Uppercase kicker above in-page section titles and definitions. Mono code · sans word · numbered chapter / slide marker.

.eyebrow-code — developer / API sections, mono voice

Foundations
Enable trading on your app

.eyebrow-word — editorial use-case kickers, sans voice

Use case — Real World Assets
Access every tokenized RWA

.eyebrow-numbered — chapter / slide / card number + label, both muted by default

01 Model Tiers
Four models, one decision

.eyebrow-numbered--accent — number painted in --accent-primary, label stays muted

02 Repo Reality
Three repos, three pacing modes

.eyebrow-numbered with inline tone overrides — designer-controlled accent pairing

03 Task Playbook
Patterns that actually scale

Hero eyebrow .header-eyebrow

Mono kicker above marketing and DS-page H1. Single label, two-color split, or breadcrumb form via the --breadcrumb modifier.

.header-eyebrow — single label

Catalog v2.0

.header-eyebrow + .header-eyebrow-accent — two-color label

Use Case Wallets

.header-eyebrow--breadcrumb — visual path, used on every DS sub-page hero

Design System Components

Pane eyebrow .ds-doc-eyebrow

Quiet mono kicker above demos inside catalog Preview panes. Pair with .ds-doc-eyebrow-row to inherit the breathing rhythm.

Primary action · .btn-primary

Components
Floating Action Button
A fixed-position circular icon button, anchored to the bottom-right corner of the viewport. Use to expose "page-level tools" — a popover menu, a fullscreen search, an assistant — without eating nav real estate. Pairs naturally with .menu inside a .ds-doc-menu-panel popover, but the button itself is independent — wire it to any trigger behaviour you need.

Floating action button .fab

Fixed circular trigger anchored bottom-right of product surfaces. Composes onto .btn-primary.btn-neutral.btn-icon.btn-lg.btn-circle.

Click to toggle .is-open — the SVG rotates 45°.

Components
Icon button
The icon-hugging button: a square (or circular) hit target centering a single glyph. Distinct from the label button (.btn-with-icon), which carries label-optimized padding and optional leading/trailing icons. Four size tiers (xs 24 / sm 32 / default 40 / lg 48); composes .btn-borderless and .btn-circle; always set an aria-label.

Icon button .btn-icon

Icon-hugging button: square by default, circular with .btn-circle, chrome-free with .btn-borderless. Four tiers (xs 24 / sm 32 / default 40 / lg 48); always set an aria-label.

Sizes — xs 24 · sm 32 · default 40 · lg 48

Colour weight · default reads at --text-primary · .btn-muted opts into --text-muted

Default — .btn-icon.btn-borderless.btn-circle (sits at --text-primary, matches label weight)
Muted — .btn-icon.btn-borderless.btn-circle.btn-muted (sits at --text-muted; lifts to primary on hover)

Default (neutral) hover vs. accent-tinted hover via .btn-tertiary

Neutral — .btn-icon.btn-borderless.btn-circle (hover = 8% black/white wash)
Accent-tinted — .btn-tertiary.btn-icon.btn-borderless.btn-circle (hover = LI.FI blue wash)

In context — screen header (paired with a .seg--borderless mode picker)

Components
Form Controls
Unified .input, .select, and .textarea controls. Inputs share the same size scale as buttons and segmented controls (sm / md / lg) so they line up in the same row. Chrome uses neutral surface elevation via the --field-* token family — readable across every Theme Composer palette, accent is reserved for the focus ring. Two label positions (stacked above the field / inline inside the chrome) and an opt-in --tinted modifier when brand affinity is intentional (hero forms, onboarding).

Variant matrix

Three sizes × two label positions × an optional --tinted modifier. Pick size by neighbour, position by role.

VariantSizeLabelUse when
.input.input--smsm — r12 · 9×14 · 0.82rem · ~36 px (matches btn-sm, seg-sm)Stacked — .form-label.form-label--smFAB panels, sidebar settings, drill-down editors.
.inputmd (default) — r14 · 12×16 · 0.9rem · ~43 pxStacked — .form-labelMarketing forms, account forms, full-width forms on their own surface.
.input.input--lglg — r18 · 16×20 · 1rem · ~54 pxStacked — .form-label.form-label--lgHero sign-up bars, prominent single-field forms.
.field-inline.field-inline--smsm — same footprint as stacked --smInlineDense inline editing, table-cell editors, filter rows.
.field-inlinemd (default)InlineLabel doubles as the field's identity (card meta, modal rows, swap pairs).
.field-inline.field-inline--lglgInlineHero amount entry, prominent currency / token pickers.
+ .input--tinted
+ .field-inline--tinted
Modifier — applies to any sizeEitherBrand affinity is intentional and the field sits on a neutral surface.
+ .field-inline--has-trailModifier — reserves a right-edge slot for .field-trailInlinePassword show/hide, clear-field, unit toggles.

Text & numeric

Text input .input

Single-line text field with stacked label and optional hint. Default size matches btn-md.

Connected: 0x1a2b...8f9e
Select the chain you want to receive on

Select .select

Native select with the same chrome as .input. Pair with .form-group for label + helper.

Textarea .textarea

Multi-line text field. Auto-grows on input; min-height comes from inline style or --lg modifier.

We'll get back to you within one business day.

Number input type="number"

Native numeric input — accepts min, max, step. Pair with .field-prefix / .field-suffix for currency or units.

1–100, integer step.

Sizes & label positions

Compact — stacked label .input--sm

Tighter density for FAB panels and drill-down editors. Add --sm to any control + label pair.

Inline label — comfortable .field-inline

Label sits inside the field chrome at the top of the padding. Whole wrapper is the focus target.

Inline label — compact .field-inline--sm

Same pattern, tighter box. For table-cell editors, inline rename UI, dense filter rows.

Large — stacked & inline .input--lg

Hero-size fields. Use only when the field IS the call to action — sign-up bars, single-purpose forms.

Modifiers

Tinted modifier --tinted

Default chrome is neutral so inputs stay readable across every palette. --tinted opts in to brand affinity.

Password — trailing icon --has-trail · .field-trail

Right-edge button slot inside .field-inline. Canonical use: password show / hide. Glyph swap driven by aria-pressed.

Invalid state --invalid · aria-invalid

Validation modifier — danger-tinted border and focus ring. Pair with .form-error and aria-describedby for the message.

Enter a valid email address.

Read-only state :read-only

Native state — value visible and selectable but not editable. For derived values (computed totals, generated keys) shown in-form.

Read-only — copy with ⌘C.

Leading icon --has-lead · .field-lead

Left-edge icon slot — mirror of --has-trail. For non-interactive glyphs (search magnifier). Use .field-prefix for text addons.

Selection

Checkbox .checkbox

Boolean control. Native <input type="checkbox"> wrapped in a label; chrome reads --field-* tokens.

Switch .switch

Universal on/off toggle. Three sizes paired 1:1 with form controls, four accent modifiers, and a settings-row composition.

Three sizes shown. Each switch is a real <label> wrapping a real <input type="checkbox"> — keyboard, screen reader, and form submission all work with zero JS. The on-state track is painted with --accent-primary, so the theme composer retints every switch on the page automatically.

sm · 34 px row
default · 42 px row
lg · 54 px row

Playground preferences

Universal pattern — .setting-row hosts a label, optional description, and any control on the right (switch, input, select, button group). Pair sizes 1:1 between the row and the control.

Auto-refresh quotes
Repoll route data every 15 seconds while the form is open
MEV protection
Route through private mempools when available
Refresh interval
How often to repoll quotes
Show test networks
Sepolia, Holesky, Mumbai, and other staging chains
Auto-execute approvals
Approve token spend without an explicit confirmation step
Reduce motion
Tabular numerals
Compact density

Composition

Setting Row .setting-row

Universal settings-row primitive — mirrors .ui-amount-card surface (--surface-card + --elev-1 + 16 px radius). Three sizes locked to the system ladder (40 / 48 / 56).

Three row contracts coexist on the same primitive — pick by intent. Settings shape hosts a label + optional description with any control on the right (switch, select, input, button group). Drill shape renders as a <button> with icon + label + optional value, opening a sub-screen on tap — the whole row is the affordance, signalled by a hover lift (no chevron). Expand shape (--expand) is a collapsible section — a head button with a live value summary + rotating chevron over an in-card .reveal drawer. All three share the same chrome (canonical UI-card surface, locked heights, hover rules) so consumers don't drift.

Hover any row — background mixes 3 % --accent-primary into --surface-card, shadow steps to --elev-2. Same canonical UI-card recipe as .ui-amount-card. Click navigates (in this demo, the data-drill handler is omitted). Row 3 omits the value column — the auto grid track collapses naturally, no modifier needed.

Refresh interval
MEV protection
Route through private mempools when available
Slippage tolerance
Max % the price can move before reverting
Display currency

Three of these rows host the simplest controls (native <select>, switch, input). The fourth shows the dropdown-as-control composition — use this when the option list deserves a styled panel (recently-selected check, descriptions per option, theming) rather than the native OS picker. Inside a .setting-row reach for the default .dropdown__trigger (filled card), not --bare; pair with .dropdown__panel--align-end so the panel hugs the row's right edge without clipping. Active option carries a check rendered from the canonical Lucide icon at /icons/check.svg.

Across
Stargate
1inch

A row can lead with an .avatar (provider / token / chain logo) instead of a glyph — the playground's Bridges & Exchanges toggles are the live consumers. Auto-detected via :has(> .avatar), so no modifier is needed; the avatar's presence restores a leading column even when --no-icon is present. Pair the avatar tier to the row tier so it fills the slot — --lg row hosts .avatar--sm (32 px), default hosts .avatar--xs (24 px). Use alt="" — the __label already names the provider.

Drawer content — controls, tile grids, slider fields. The drawer owns padding so the collapser stays bare.

The __value summary can hold .swatch--xs dots — the Theme Composer’s Colors head shows the four seed colours this way.

Closed rows still read their state — the stack doubles as a one-screen overview.

Every head above is live — click to toggle the drawer. The third row contract: expand discloses controls in place (.setting-row__head button over the universal .reveal drawer, all inside one card). The rotating chevron is the open/closed STATE indicator — drill rows stay chevron-free. The Theme Composer’s section stack is the canonical consumer.

Form anatomy & rules

The whole form-control family — wrappers, labels, controls, modifiers — and the rules that keep them consistent.

ElementClassRole
Field group.form-group · .form-group--smVertical rhythm wrapper — gives the label + control stack a consistent margin-bottom (20 px default, 10 px compact).
Field row.form-rowTwo-column grid for side-by-side groups. Collapses to one column under 600 px.
Stacked label.form-label · .form-label--sm · .form-label--lgBlock label above the control. Three sizes match the three control sizes (0.66 / 0.82 / 0.9 rem).
Control (stacked).input · .select · .textarea + --sm / --lg / --tintedChrome derived from --field-* tokens. Neutral by default; --tinted opts in to accent.
Inline wrapper.field-inlineOwns the chrome (background, border, radius, focus ring). Uses :focus-within so the ring lights up when any child focuses.
Inline label.field-labelUppercase / 0.7 rem / muted caption at the top of the wrapper. pointer-events: none — clicks fall through to the control.
Inline control.field-input · .field-select · .field-textareaBare control — inherits typography, zero padding / border / background. The wrapper is the visual chrome.
Trailing slot.field-inline--has-trail + .field-trailOptional right-edge slot for an icon button. Wrapper reserves right-padding (30 / 36 / 42 px for sm / md / lg).
Muted helper.muted.muted--fieldHelper copy below a stacked control. Inline <a> auto-styles as .ds-doc-link.

Form card .form-card

Container panel for complete forms. Subtle accent-tinted background, rounded corners, generous internal spacing.

Get in touch

Error message .form-error

Below-field validation message — sibling of .muted. One shows at a time per row. Wire via aria-describedby.

3–32 characters. Learn more.

Too short — 3 characters minimum.

Text prefix & suffix .field-prefix · .field-suffix

Inline text addons on the input baseline — "$", "https://", ".com", "%". Wrap input + addons in .field-control.

Color field .color-field

Colour value I/O — swatch trigger (native picker) + editable mono input accepting HEX, rgb() or oklch() + copy + format chip.

Live — click the swatch for the native picker, type or paste a value (#FF5500, rgb(255, 85, 0), oklch(67.6% 0.217 39) all parse), cycle the display format, copy. Invalid input flashes the danger ring and reverts.

Default · seeded #2335CE

Standalone instances self-wire via data-color-field="<hex>" and fire a bubbling color-change event on commit.

Search with clear --has-lead + --has-trail

Composed pattern — magnifier in the lead slot, clear-X in the trail slot. The same primitives, paired.

Components
Kbd
Inline-flex micro-primitive that renders a keyboard shortcut as a compact key cap. Sizing mirrors the .btn ladder's arithmetic (padY × 2 + line-box = height) — heights step +4 (16 / 20 / 24), every value a --space-* or --text-* token. Flat surface, no border: one color-mix(--text-primary 10%, transparent) auto-flips light/dark. Pair with anything that has a keyboard accelerator — .search-trigger chips (⌘K), .menu-item shortcut rows, modal footer hints (Esc / ↵), command-palette result rows.

Kbd .kbd

Keyboard-hint chip on a flat mono fill — sizes step +4 (16 / 20 / 24) via the same arithmetic the .btn ladder uses.

Sizes

.kbd--sm (16 px)
.kbd default (20 px)
.kbd--lg (24 px)

Single keys

Min-width keeps single-character chips (K, /) from squishing next to a wider neighbor.

Clusters — thin space between, never +

In context — inline hints

Press to open the command palette.

Use to navigate, to select, to dismiss.

Components
Lists
Universal stacked-row primitive. .list-item composes three slots — lead (icon, avatar, status dot, checkbox), body (title + optional description), trail (chevron, value, switch, action) — into a row that stacks vertically inside a .list container. Reach for it whenever you have N rows that should read as one continuous list: directories, contacts, activity feeds, simple settings, drill navigation, selection lists. For domain-specific rows that need their own chrome, semantics, or animation, the existing primitives stay where they are — .menu-item, .setting-row, .detail-list, Token row (composition over .list-item). The cross-reference card at the bottom of this section maps each pattern to its canonical home.

Anatomy

List item .list-item

Slot-based row primitive. Compose __lead + __body (title and optional desc) + __trail; stack inside a .list.

Single-line

  • Ethereum
  • Arbitrum
  • Polygon

Two-line

  • Connected wallet
    0x1a2b…8f9e · MetaMask
  • Slippage tolerance
    Auto · usually 0.50%
  • Route preference
    Cheapest · 23 paths checked

Lead content recipes .list-item__lead

Lead is a positioning wrapper, not a sizing constraint. Drop any sized primitive inside; the grid's auto column aligns rows automatically.

  • Plain SVG
    Default — drop a <svg> straight into the slot
    20×20
  • Spot icon tile
    .spot-icon — tile + tinted glyph; sizes lg/xl/2xl/3xl
    32×32
  • Spot icon — large, toned
    .spot-icon-lg + .spot-icon--info for accent-tinted tiles
    40×40
  • VR
    Avatar — initials or photo
    .avatar + --circle — 40px default; --sm/--md/--lg/--xl
    40×40
  • Status dot
    Inline 8×8 span on a semantic colour — --success / --warn / --danger
    8×8
  • Checkbox / radio
    Native form control — wraps the row in <label> for click-anywhere selection
    ~16×16

Content variants

Avatar list .list-item + .avatar

Directory pattern — avatar in lead, name + role in body, action button in trail. Drop the trail for read-only rosters.

  • PA
    Philipp Zentner
    CEO · philipp@li.fi
  • AB
    Arjun Bhuptani
    Founder · Connext (partner)
  • VR
    Vilen Rodeski
    Director of Design · LI.FI

Activity feed .list-item + .list-item__meta

Status dot in lead, action label in body, timestamp in trail via __meta. Pair with .list-section for Today / Yesterday grouping.

  • Swap settled · 1.25 ETH → 4,237 USDC
    Ethereum → Arbitrum · via Across
    2m ago
  • Quote refreshed
    23 paths · best 0.94% slippage
    14m ago
  • Allowance approval pending
    USDT on Polygon · waiting for signature
    1h ago
  • Route reverted
    Insufficient liquidity on Optimism
    3h ago

Token row .list-item + .avatar-tandem

Universal token list row across product widgets — Swap, Perps, Earn. Composition recipe over .list-item--lg pinned to --space-72; no new class.

Picker context · address on hover

  • on Ethereum
    ETH
    Ethereum 0xC02a…6Cc2
  • on Ethereum
    USDC
    USD Coin 0xA0b8…eB48
  • on Ethereum
    DAI
    Dai Stablecoin 0x6B17…1d0F

Portfolio context · balance in __trail

  • on chain
    ETH
    Ether 0xC02a…6Cc2
    4.0 $9,240.00
  • on chain
    USDC
    USD Coin 0xaf88…a831
    5,000.00 $5,000.00
  • on chain
    ETH
    Ether 0x82aF…3496
    2.0 $4,620.00
  • on chain
    SOL
    Solana So111…11112
    30.0 $4,200.00
  • on chain
    USDC
    USD Coin 0x8335…dF913
    3,000.00 $3,000.00
  • on chain
    ETH
    Ether 0x4200…0006
    1.0 $2,310.00
  • on chain
    USDC
    USD Coin 0x0b2C…d39d85
    2,000.00 $2,000.00
  • on chain
    OP
    Optimism 0x4200…0042
    1,000.0 $1,200.00

Selection

Selection list .list-item + checkbox · radio · switch

Checkbox/radio in lead, switch in trail. Markup composition handles every shape — no new modifier classes needed.

Checkbox in lead

  • MEV protection
    Route private mempools when available
  • Auto-confirm under 0.5%
    Skip confirm dialog for low-slippage quotes
  • Show test networks
    Sepolia, Goerli, Mumbai in chain picker

Radio in lead (single-select)

  • Fastest
    ~2 min via Hop, slightly higher fee
  • Cheapest
    ~12 min via Across, lowest fee
  • Balanced
    ~6 min via Stargate, medium fee

Switch in trail

  • Email notifications
    Settled swaps, weekly digests
  • Push notifications
    Browser + mobile when wallet connected

Grouping & states

Container chrome .list--card · --separated · --inset

Pick at most one container modifier. --card wraps the whole list as one surface; --separated adds hairline dividers; --inset adds vertical breathing inside a panel.

.list (default — flat)

  • Ethereum
    $2,847
  • Arbitrum
    $1,204
  • Polygon
    $487

.list--separated

  • Ethereum
    $2,847
  • Arbitrum
    $1,204
  • Polygon
    $487

.list--card .list--separated

  • Ethereum
    $2,847
  • Arbitrum
    $1,204
  • Polygon
    $487

Card list .list--cards

Each item is its own card — surface, border, shadow, padding. For selection lists; composes with .list-item--interactive + .is-selected.

  • Ethereum
    L1
  • Arbitrum
    L2
  • Base
    L2

Hover a card for the accent wash; the selected card (Arbitrum) carries the 1 px accent rim. Default tier — rows land at 64 px with a 40 px lead.

Section header .list-section

Uppercase mono eyebrow between item groups. Breaks long lists by category — A/B/C in directories, Today/Yesterday in activity.

  • Today
  • Swap settled · 1.25 ETH → 4,237 USDC
    2m
  • Quote refreshed · 23 paths
    14m
  • Yesterday
  • Bridge settled · 5,000 USDC → Polygon
    19h
  • Allowance approved · USDT on Polygon
    22h
  • Older
  • Wallet connected · MetaMask
    3d

Nested list .list-item--nested

Zero-JS collapsible parent + indented children using <details> / <summary>. The trail chevron rotates 90° on open.

Chains
  • Ethereum
    31
  • Arbitrum
    12
  • Polygon
    8
Tokens
  • USDC
    All chains
  • USDT
    All chains
Wallets
  • MetaMask
  • Rainbow

States — interactive · selected .list-item--interactive · --selected

Add --interactive to render the row as <a> / <button> with hover + focus chrome. Add --selected for persistent state (current page, current chain).

Chain list module .chain-list-module

Labelled block of chain rows. Use as a flat network filter or stack 2+ for Active / Popular / Testnets surfaces.

Canonical 20-chain network filter — data-list-variant="popular"

This is the canonical chain list as shipped on the swap widget. Reference this directly when building any chain-selection surface that needs the standard top-20 set; copy the markup verbatim, swap the data-list-variant if needed, and let the consumer's JS handle filtering.

With head — sectioned chain picker (Active / Popular split)

Active

3

Token list module .token-list-module

Labelled block of token rows. Stack 2+ inside a picker body for Featured / Portfolio / Popular surfaces.

Canonical picker order — data-list-variant="portfolio" first, then "popular"

Your tokens

$13,860.00
  • on Ethereum
    ETH
    Ethereum0xC02a…6Cc2
    4.0 $9,240.00
  • on Arbitrum
    USDC
    Arbitrum0xaf88…a831
    5,000.00 $5,000.00

Popular

  • on Ethereum
    USDT
    Tether USD0xdAC1…1ec7
  • on Ethereum
    LINK
    ChainLink Token0x5149…8CA1
  • on Solana
    SOL
    SolanaSo11…1112

Domain primitives

Domain-specific list rows — when not to use .list-item

Cross-reference for domain-specific list-row primitives. Reach for these when the row needs behaviour .list-item intentionally lacks.

Description list .detail-list

Term + supporting content rows for fees, settings, spec sheets — with sizes, alignments, dividers, embedded controls.

A semantic <dl> primitive for "term + supporting content" rows. The default layout (term LEFT, desc RIGHT, tabular-nums on value) covers fee breakdowns and quote summaries. Three alignment modifiers, three sizes, three chrome variants, and a desc slot that accepts any control.

Network cost
$0.03
Provider fee
$0.01
Integrator fee
$0.05
Price impact
-1.94%
Max. slippage
Auto
24h yield
+$12.40
Min. received
0.00043 stETH
Network cost
$0.03
Provider fee
$0.01
Max. slippage
Auto
Network cost
$0.03
Provider fee
$0.01
Max. slippage
Auto
Network cost
$0.03
Provider fee
$0.01
Max. slippage
Auto
Account name
Acme Treasury Operations
Plan
Enterprise — volume tier 3
API key
li_live_8f3c9a4e2b…
Created
12 March 2025
Status
Active
Auto-refresh quotes
Repolls route data every 15 seconds while the form is open. Pauses when the tab is in the background.
MEV protection
Routes through private mempools when available. Adds ~80ms latency in exchange for sandwich resistance.
Slippage tolerance
Maximum percentage the price can move between quote and execution before the route reverts.
Auto-refresh quotes
Repoll every 15 seconds
$0.05/ refresh
MEV protection
Private mempool routing
Included
Priority gas
Bumps tip when network is congested
+$0.18
Network cost
$0.03
Provider fee
$0.01
Integrator fee
$0.05
Total fees
$0.09
Network cost
$0.03
Provider fee
$0.01
Integrator fee
$0.05
Bridge fee
$0.02
Total fees
$0.11
Costs
Network cost
$0.03
Provider fee
$0.01
Output
Min. received
0.00043 stETH
Price impact
-1.94%
Auto-refresh quotes
Repoll every 15 seconds
Subscription
Activerenews May 12
Default chain
API key
li_live_8f3c…
Components
Marker
Small rounded tile with a sequential character — number (1), letter (A), or zero-padded number (01) — centered inside. Sibling of .spot-icon: same size ladder and tone slot, different content contract. Use it as a row anchor in variant-comparison rails, a step bullet in proposals, or a numbered feature anchor in marketing decks. Zero-padded form is the default for decks and proposals — the leading 0 keeps "09 / 10 / 11" and "19 / 20 / 21" column-aligned across the rail, and reads as branded and intentional rather than generic. Pairs size-for-size with .spot-icon so mixed rows align on a shared baseline.

Marker .marker

Sequential-anchor disc with tabular-nums character. Five sizes scale tile + radius + type; pairs size-for-size with .spot-icon.

Sizes — default 32 · .marker-lg 40 · .marker-xl 48 · .marker-2xl 56 · .marker-3xl 64 (px)

default .marker (32 px)
32 size · 12 type · 8 pad
.marker-lg (40 px)
40 size · 14 type · 12 pad
.marker-xl (48 px)
48 size · 16 type · 16 pad
.marker-2xl (56 px)
56 size · 18 type · 20 pad
.marker-3xl (64 px)
64 size · 24 type · 24 pad

Content — three forms (numbers · letters · zero-padded)

Plain numbers — fine for short ≤9 sequences; the 10 widens visually because "10" has two characters while "1–9" have one
Letters — alternate sequence, sub-items below a numbered parent
Zero-padded — the default for marketing decks and proposals. Tabular-nums keeps the leading 0 column-aligned across the rail; 09 → 10 and 19 → 20 transitions stay visually clean instead of widening

Tone variants .marker--success · .marker--info · .marker--warn · .marker--danger · .marker--neutral

Five semantic tones drive both the character color and a 14 % background tint from a single --marker-tone slot. Default tone is var(--accent-primary); modifiers swap the slot.

default
var(--accent-primary)
--success
var(--success)
--info
var(--info)
--warn
var(--warn)
--danger
var(--danger)
--neutral
var(--text-secondary)

Active-card recipe — alpha at rest, tinted on .is-active

Canonical pattern for cards with a leading marker. Both states stay in the tinted fill mode; the contrast is hue — alpha (neutral) at rest, tinted (brand accent) on selected.

A row of four markers — one active, three inactive — showing the alpha→tinted hue shift. Inactive markers sit in the alpha tone (neutral ink wash + secondary-ink character); the active marker carries the tinted tone (brand-accent wash + accent character). Both stay in the marker's natural tinted fill mode — only the tone slot swaps. Pair with the card's accent rim + label tint and the selection signal reads cleanly without reaching for the louder solid-fill escalation (reserved for hero anchors). Reference consumer: .ds-doc-variant-card.

Active · tinted
14 % --accent-primary + accent char
Inactive · alpha
14 % --text-secondary + neutral char
Inactive · alpha
14 % --text-secondary + neutral char
Inactive · alpha
14 % --text-secondary + neutral char

Shape & fill variants .marker--round · .marker--solid

Two optional modifiers. --round flips the rounded-square tile to a perfect circle. --solid swaps the 14 % tinted fill for a solid tone with contrast text. Compose with any size and tone.

Shape — rounded square (default) vs. .marker--round

top row · .marker (rounded square)  ·  bottom row · .marker--round (perfect circle)

Fill — tinted (default) vs. .marker--solid

top row · default 14 % tint  ·  bottom row · .marker--solid (full tone fill, white character)

Hero composition — solid + round, deck-anchor recipe

Components
Muted
Universal primitive for "muted, smaller, non-data informational line" — the line that says we'll never share your email below a form field, Connect wallet to see balance inside an empty card, Powered by LI.FI beside an action, Sign in to see your transaction history inside an empty-state hero. One typography, four placement modifiers. Renamed from .hint in the May 2026 v20 promotion (the prior v11 had promoted .form-hint.hint; v20 aligns the name with the visual property — muted ink — replacing the older tone-name reading).

Muted .muted

Muted helper / placeholder copy. 12px caption, weight 400, ink 40% via --field-placeholder. Four placement modifiers: --field, --slot, --block, base.

.muted.muted--field · form-row helper

We'll never share your email. Read the Privacy Policy.

.muted.muted--slot · inside a card slot

Send
0 USDC

.muted.muted--block · standalone block under a heading

No transactions yet

Sign in to see your transaction history across every chain LI.FI supports. Sign in.

.muted base · typography only, host owns spacing

Powered by LI.FI
Components
Notifications
Floating toast messages for ephemeral feedback — success / info / warn / danger tones, four layout variants (default / compact / with actions / split), six placement options on the .notification-stack wrapper. Slides in from the corner; auto-dismiss is consumer-driven via the .is-active.is-dismissing class transition.

Notification .notification

Default layout — mark + title + message + close. Four status tones (--info default, --success, --warn, --danger) drive the mark color.

Four status tones, stacked. The .is-active class is set inline here so the slide-in transition resolves to the visible state for the catalog (in production a JS controller adds it on mount).

Route confirmed

Stargate via Polygon → Arbitrum. Settled in 12s with 0.04% slippage.

New chain available

Base mainnet is now supported. 30+ tokens routable on day one.

High slippage tolerance

1.2% on this trade — review the route summary before confirming.

Transaction rejected

Insufficient gas on Arbitrum. Top up ETH and retry, or pick a different route.

Layout variants .notification--compact · --with-actions · --with-avatar · --split

Four layout modifiers cover the canonical patterns — stack with status tones like --success.--with-actions.

Address copied to clipboard

Update available

A new version of the Widget SDK was published. Restart your dev server to load it.

Vilen mentioned you

in cross-chain-team · “Can you review the new gas-estimation flow before standup?”

New version of the SDK

v2.4.1 is available with a faster route-aggregation engine.

Top to bottom. --compact drops to a single-line toast for non-blocking confirmations ("Saved", "Copied"). --with-actions stacks two secondary buttons beneath the message for non-modal decisions. --with-avatar swaps the status mark for a profile chip on mention / DM-style notifications. --split moves actions to a full-width footer with a hairline divider for binary-decision UIs where the notification IS the entire decision surface.

Stack .notification-stack

Fixed-position wrapper that holds notifications in one of six corner placements without blocking clicks underneath.

Six placement classes. The viewport below maps the stack positions onto a faux-page rectangle — top-right is the system default; bottom-left is the recommended alternate when the FAB occupies the bottom-right corner.

Page surface
top-right (default)
top-center
bottom-right
bottom-left
bottom-center

Live API window.Notifications

Imperative push / dismiss controller — auto-creates the stack, queues at 3, handles slide-in. Buttons below fire real notifications.

Click a button. The notification slides in from the top-right corner of the viewport, auto-dismisses after 5s, or click the X to dismiss immediately. Stack tier them: clicking 4+ buttons in a row will dismiss the oldest to keep the queue at 3.

Open DevTools to see the API in action: Notifications.show({ tone: 'success', title: 'Hello', message: 'World' }) returns the notification id.

Components
Numerics
Animated numeric counters. The first primitive is .num-count — a value slot that tweens between numbers as data-value updates. Use anywhere a number changes live: spot prices, balances, on-chain counters, KPI tiles. Format (currency / percent / integer / signed) is inferred from the initial text content; flash modes are opt-in.

Number counter .num-count

Animated numeric value. Writes to data-value trigger a tween from the displayed number to the new target.

Every counter below is live — click Tick to set a new data-value and watch the tween. The displayed number is always interpolated from the currently displayed value, not from the stale start.

Currency · inferred from $X,XXX.XX

$2,284.58

Percent · inferred from X.XX%

2.26%

Integer · inferred from X,XXX

19,584,217

No flash · data-flash="none" · default

$2,284.58 Use when a sibling delta chip already signals direction.

Bright flash · data-flash="bright" · no hue shift

$2,284.58 KPI hero surfaces — ink lift + halo, no green/red.

Hue flash · data-flash="hue" · success / danger by direction

$2,284.58 Use only where no sibling delta chip carries direction — avoids double-signaling.
Components
Option card
Horizontal single-select tile — wireframe preview on the left, title + 1-line description on the right. Reach for this when picking one option from N where each option benefits from a caption — drill-in settings, "pick a mode" panels, onboarding flows, layout / variant / mode / height pickers.

Option card .option-card · .option-card-grid

Single-select horizontal tile — preview + title + description. Columns, accent tone, and preview size are per-grid var overrides.

Three live pickers from the playground rail, all built on the same primitive — the Layout stack and Presentation picker (1-column default), and the Corners presets (shown here at a 2-column override). .option-card-grid is the container, .option-card is the tile. .is-active marks the chosen card (single-select — consumer-managed). The grid defaults to a 1-column stack; widen it via --option-card-grid-cols.

Default · 1-column · Layout

Two-column · --option-card-grid-cols · Corners

Presentation · 1-column

Tile · .option-card--tile · dense choice grids

Panel 32 · cards 16 · buttons 16.

Descriptions are suppressed on tiles — the ACTIVE choice’s description renders as one .muted caption under the grid (state-reactive, less repeated noise). Canonical consumer: the Theme Composer’s Corners / Panels / Charts sections.

Components
Pagination
Page-control primitive for tables, lists, search results, and any other paged surface. Sibling of .data-table — extracted so future product surfaces consume the same vocabulary instead of rolling their own. Two sizes on the canonical ladder (.pagination--sm 32 px · default 40 px), a compact variant for narrow widgets, and an optional .pagination__info caption for "Showing N of M".

Default .pagination

40 px controls. Prev / Next flank a row of page-number buttons; the active page tints with the accent tone and carries aria-current="page".

Sizes .pagination--sm · .pagination

Two-tier ladder. --sm at 32 px pairs with .data-table--sm; default 40 px pairs with the default table.

Compact · .pagination--sm · 32 px

Default · .pagination · 40 px

Compact .pagination--compact

Drops the page-number buttons; shows "‹ current / total ›" only — for narrow widget surfaces and mobile drawers where 5+ buttons can't fit.

With info caption .pagination__info

Caption sibling reads "Showing 1–10 of 287" — for product surfaces where the row-count context matters; sits opposite the controls.

Showing 1–10 of 287 transactions

Components
Panel
Universal floating-panel surface primitive — the official recipe for every panel-shaped chrome across the system. Live consumers: the playground rail, the swap widget shell, the receive aside, the modal, and the Portal’s 44 panels. Opaque raised surface (--surface-raised) + floating-tier shadow (--elev-3), 16 px radius, token-driven padding + gap, flex column. The primitive sets surface chrome only — width, position, and z-index belong to the consumer; density is tuned via the --panel-* tokens, not new CSS. A formal three-slot model (June 2026) gives every panel an addressable .panel__header / .panel__body / .panel__footer — additive slots that compose with the existing content primitives (screen-header for the head, .action-bar for the footer) rather than replace them. Composes cleanly under any product class: .panel.ui-card, .panel.playground-rail, .modal.panel, .panel.portal-panel. Every new panel-shaped component (drawer, sheet, command bar, future popover-tier surfaces) MUST compose this primitive rather than hand-roll its own surface chrome.

Panel .panel

Single panel surface recipe consumed by every floating chrome in the system — opaque raised fill, --elev-3 shadow, 24 px padding.

Bare panel · .panel · pure surface recipe (no extra consumer styles)

A bare panel renders the opaque raised surface, the 16 px radius, and 24 px padding on every edge. Drop any content inside.

Section label

Content stacks naturally via the panel's flex column + 12 px gap.

Composed with a product class · .panel.ui-card (swap widget shell)

Exchange

Widget content sits in the same surface as the rail — one visual language across every panel consumer.

Three-slot model · .panel__header · .panel__body · .panel__footer

Orders

Open limit orders

The body scrolls; the header and footer stay pinned. With a bounded panel height the overflow engages — here the body is the only scroller.

ETH → USDC · 1,200 · filled 37%

USDC → WBTC · 6,000 · filled 62%

USDC → OP · 1,000 · filled 100%

ARB → USDC · 2,400 · filled 100%

Header pairs with .screen-header; footer hosts an .action-bar; the body owns the scroll. --ruled adds the header hairline.

Surface variants · .panel--elevated (default) · .panel--flat

Elevated

Floats with the --elev-3 cast shadow. The default — for chrome + overlays (rail, dropdowns, modals) and any few-panel surface.

Flat

Sits flush with a hairline, no shadow. For content panels stacked in a canvas at scale (the Portal’s ~44 panels).

Flat flips all three surface tokens — fill to the content tier (--surface-card, white-capable), shadow off, hairline on. Flip a whole canvas by setting the --panel-* tokens on its wrapper (.portal-canvas), or per-theme via the Theme Composer’s Panels section.

Live references in the codebase

  • .panel.ui-card — swap widget shell (both Send/Receive form and Receive aside on playground.html).
  • .panel.playground-rail — left-edge customization rail on the playground.
  • .modal.panel — the universal modal shell (May 2026 v13). Canonical modal + headerless variant both compose .panel for surface chrome; .modal itself adds only the modal-specific constraints (size, max-height, slide-up animation, positioning context for the floating close).
  • Glass-by-class siblings.dropdown__panel, .ds-doc-menu-panel, .cmd-palette__panel still register with the Glass material via :where(…); they're floating chrome over content where translucency is intentional. .panel is the opaque sibling. New surfaces choose by intent — bare .panel for opaque shells, the glass-by-class set for translucent overlays.
Components
Panel Controls
Unified .ds-* components shared across all FAB control panels (Theme Composer, Globe, Logo Stack, Reactive Coins). Every control uses palette tokens so it adapts to theme changes and Theme Composer edits automatically.

Panel surface & header .ds-doc-menu-panel · .screen-header.screen-header--compact

Frosted-glass surface for FAB control panels in the catalog. Borderless — halo + shadow define the edge; sticky compact-tier screen-header on top.

Globe

Spin speed0.5×
Axial tilt23.5°
Grid lines

Panel sub-controls

Compact FAB-panel controls — control row, color picker, divider, section label, button row, slider. Preset rows: see #swatch.

Opacity0.75×
Fill color
Above the divider
Below the divider

FAB navigation menu .menu-item

Navigation cards inside the catalog FAB panel — icon + title + description; .is-current marks the active surface.

FAB button .fab

Fixed-position circular trigger for the catalog FAB panel. Bottom-right; toggles the menu on click.

Fixed bottom-right · toggles the panel menu on click
Components
Radio
Single-select form family. The .radio atom + .radio-group wrapper handle list-style selection — sizes ladder, with-description shape, color-picker swatch, settings-row composition. .radio-card extends the family to selectable cards for plan / route / network pickers. Sibling form controls: .checkbox, .switch, .seg.

Radio & radio group .radio · .radio-group

Single-select control. Three sizes mirror .checkbox. Wrap a group in <fieldset class="radio-group"> for accessible semantics.

Sizes

Stacked group

Slippage tolerance

Inline group

Gas priority

With description

Bridge route

Color picker

Theme accent

Inside a settings row

Default chain
Used when no chain is specified by the route
Default chain

Invalid state

Quote source

Pick a quote source to continue.

Radio card .radio-card

Selectable card with corner-check indicator. Use for 2–4 rich options — plan picker, route picker, network picker.

Three-up with descriptions

Plan

Compact density

Network

Stacked vertical

Bridge route
Components
Refresh timer
Composite primitive that combines an .spinner.spinner--progress.spinner--alpha (countdown) with an indeterminate .spinner.spinner--primary (refreshing) plus a click-to-refresh affordance. Drives the "auto-refresh every N seconds → swap to spin → reset" rhythm used in the swap widget's Receive header today and reusable by any surface that polls (Review state, session-expiring countdowns, etc.). Consumer wires the fetch via the refresh-timer:expired event; controller restarts the countdown when fresh data lands.

Refresh timer .refresh-timer

Composes a spinner with countdown + click-to-refresh. Two states — countdown (alpha arc fills 0 → 1) and refreshing (primary indeterminate spin).

States — countdown · refreshing

[data-state="countdown"] · 20%

[data-state="countdown"] · 50%

[data-state="countdown"] · 90%

[data-state="refreshing"]

In context — Receive panel header (.screen-header__trailing slot)

Receive

Components
Search trigger
A flat-fill <button> that opens a search modal. Input-shaped layout (leading icon, placeholder-style label, optional trailing .kbd hint), but the chrome is button-aligned — mirrors the .btn size ladder, no border, no focus ring outside keyboard nav. The canonical consumer is .cmd-palette; any modal-launcher surface (dashboard search, marketing site search) can compose the same primitive.

Search trigger .search-trigger

Flat-fill button that opens a search modal — leading icon, placeholder-style label, optional .kbd hint. Mirrors the .btn ladder, not the .input ladder.

Sizes — sm · default · lg

.search-trigger--sm (35 px) · default (46 px) · .search-trigger--lg (50 px) — uniform padding 8 r12 · 12 r16 · 12 r20 (same value on every side). Side pad matches pad-Y so the trailing .kbd reads with an equal halo of space all around. Lg shares default's 12 px side pad — the lg trigger grew +4 px and the .kbd--lg also grew +4 px, so the gap math lands at the same token. Pair with the same-tier .kbd--sm / .kbd / .kbd--lg hint chip. Hover the trigger to see the icon's tinted halo — Read A from the May 2026 design review.

Scope-active state

[data-active-scope] paints a quiet accent-tinted background (10% rest, 16% hover) when the linked modal is scope-filtered. The scope name itself isn't broadcast on the trigger — readers open the modal to read and edit it. The live consumer is the catalog's .cmd-palette.

Components
Screen header
The universal chrome line at the top of any bounded surface — [back / avatar] [title (+ subtitle)] [trailing]. One core component, composed everywhere: the swap widget's screens and pickers, modal title bars, the Theme Composer, and every Portal panel head all wear this same primitive — none forks its own. It's a tier-2 universal class (no .ui- prefix; promoted off .ui-screen-header in May 2026 because modals span marketing + product). Every slot is optional; CSS adapts the grid via :has() so markup intent matches visual layout. Pairs with .panel__header when it sits at a panel's edge — .screen-header owns the title/back/trailing grid; .panel__header adds the panel-edge behavior (sticky, ruled, header→body spacing). See the Panel composition card below. Two-tier navigation contract — every surface has ONE Main screen (entry point, no back) and one or more Sub-screens (reached from Main, always include a back arrow that returns to Main); no nested sub-sub-screens. Slots accept any composable primitive — the title takes an <h2> (optionally with a __subtitle), a .seg--borderless mode picker, or an __avatar identity lead; the trailing slot takes icon buttons, a .btn-neutral action, status chips, or a .spinner. The close-X variant __back--close dismisses a modal wrapper. Not the big page title at the top of a Portal view — that's a separate .portal-page-head primitive.

Anatomy

Screen header .screen-header

Three optional slots — back / title / trailing. Author includes only what the screen needs; CSS adapts the grid via :has().

Review

Navigation variants

Main screen .screen-header--main

The widget's entry-point screen — no back button. Title slot accepts a static <h2> or a .seg--borderless mode picker.

Static title · <h2 class="screen-header__title">

Exchange

Tabbed mode picker · .seg.seg--borderless in title position

Connected-state trailing · power icon disconnects the wallet

Expandable panel · expand-panel control in trailing

Market price

Sub-screen .screen-header

Default variant for any non-Main screen — back arrow + centered --text-body-xl title + optional trailing slot. Used by Review, Progress, Completed, and detail screens. The back arrow always returns to Main, regardless of depth.

Back + centered title

Review

Back + title + trailing

Exchange details

Confirmed

Close (X) instead of back — __back--close

Settings

In-progress · .spinner spinner--sm in trailing

Review

Empty __trailing div — title still centered

Send token

Plain — title only

Centered title, no back, no trailing — just include __title in markup. Used by the swap widget's "Receive" quotes panel.

Receive

Size & surface

Size & surface variants

Modifiers orthogonal to the nav variants — a 32 px compact tier, a frosted sticky head, and a quiet tag-style title.

Compact tier (32 px) · .screen-header--compact

Slippage

Quiet title (tool identifier) · .screen-header--quiet

Material Composer

Compact runs a 32 px row with a 16 px title (vs the default 40 px / 20 px) and a .btn-sm back button. .screen-header--sticky-glass is a scroll behavior — it pins the head and frosts it as content scrolls under, so it reads correctly only in a bounded scrolling panel; it's shown live in the FAB glass sub-panels (Globe, Logo Stack, Reactive Coins).

Compositions

Composition — widget screens morph

Same primitive drives every screen in a widget flow. Markup intent matches visual layout at every step — slot presence drives the grid.

Exchange · main screen

Exchange

Receive · plain

Receive

Review · sub-screen

Review

Progress · sub + status

Exchange

Pending

Completed · with subtitle

Exchange details

Arbitrum · 2 min ago

Confirmed

Settings · modal-wrapper · close (X)

Settings

Main screen · tabbed mode picker

Review · sub + spinner (quote refresh)

Review

Composition — identity avatar lead .screen-header__avatar

A large identity avatar leading the title: the profile / org-header pattern. An alternative to the back-slot lead; left-aligns the title.

Org header · logo lead

Ledger

With subtitle · monogram lead

Rabby Wallet

Growth · 3 integrations

With trailing action

Kast

The avatar slot defaults to --avatar-size: var(--space-64) (64 px / xl); set --avatar-size on the element for lg (48) or 2xl (96). The lead avatar is the identity (any .avatar — a logo <img> or a monogram); the title left-aligns automatically.

Panel composition .panel__header · .screen-header

Panel-edge composition — .panel__header owns the edge behavior (sizing, ruled hairline, sticky), .screen-header owns the title grid. The Portal & Theme Composer pattern.

Portal panel · .panel__header.screen-header--main + subtitle + neutral action

Volume by chain

Last 30 days

Base $21K
Optimism $10K
Arbitrum $7K

The header wears two classes on one <header>: .panel__header (panel-edge behavior) + .screen-header (the title grid). It's the same primitive as the swap widget's screen heads — composed, never forked.

Components
Segmented Control
Pill-shaped option switcher for mutually exclusive choices. Shares the button system's Geist font, border-radius scale, and accent tokens so it pairs naturally alongside buttons in toolbars and forms.

Base

Segmented control .seg

Pill-shaped option switcher for mutually exclusive choices. Aligned to the Sizing Ladder — sm / default / lg / xl pair concentrically with buttons at the same tier.

.seg-sm (32 px)
default (40 px)
.seg-lg (48 px)
.seg-xl (56 px)

Paired with buttons — heights align per size

.seg-sm + .btn-sm (32 px)
default .seg + default .btn-secondary (40 px)
.seg-lg + .btn-lg (48 px)
.seg-xl + .btn-xl (56 px)

Content shapes

Beyond plain text labels, a tab can carry an icon or an avatar. Icon tabs (below) take a Lucide <svg> — beside the label, or label-less via .seg--icon-only. Avatar tabs — a token / chain / persona identity — auto-square the cell and need the transparent track, so they're demoed under the borderless variant. Both shapes are content-detected (:has()), never an opt-in modifier.

Icon tabs <svg> · .seg--icon-only

Drop a Lucide outline <svg> into any .seg-item. Icons size proportionally to the label (14 / 16 / 18px) and inherit currentColor, so active and hover states tint the glyph automatically.

Icons come from the project's Lucide v1.8.0 icon library. Inline the SVG directly so currentColor + the proportional sizing rules apply — don't use <img src>.

Icon + label

.seg-sm (32 px) — 14 px icon
default (40 px) — 16 px icon
.seg-lg (48 px) — 18 px icon

Icon only — add .seg--icon-only

.seg-sm.seg--icon-only (32 px) — 14 px icon · 7/9 padding
default.seg--icon-only (40 px) — 16 px icon · 10/12 padding
.seg-lg.seg--icon-only (48 px) — 18 px icon · 14/16 padding

Block + icon-only — full-width row

Chrome & layout

Borderless variant .seg--borderless

Drops the track rail — only the active item's fill carries selection. Active swaps to --alpha-subtle so light-mode contrast holds without the rail recess.

Sizes — sm 32 · default 40 · lg 48 · xl 56

.seg-sm.seg--borderless (32 px)
.seg.seg--borderless (40 px)
.seg-lg.seg--borderless (48 px)
.seg-xl.seg--borderless (56 px)

Composition — icons · block · paired with a button

.seg--borderless + icons
.seg--borderless.seg--block — fills parent width
Paired with a sibling .btn-sm — heights ladder up cleanly

Avatars — drop an .avatar-tandem into a .seg-item, the cell goes square

No --avatar modifier — the seg-item auto-detects an .avatar-tandem (or lone .avatar) child via :has() and switches to a square cell sized to the borderless tier's height. Use when each option is a token / chain / persona identity rather than a label (e.g. the chart-token switcher in the swap widget's receive panel).

.seg-sm.seg--borderless (32 px) + .avatar-tandem--xs
.seg.seg--borderless (40 px) + .avatar-tandem--sm
.seg-lg.seg--borderless (48 px) + .avatar-tandem (40)

Raised variant .seg--raised

Borderless tabs with a raised card-pill active tab + lighter neutral hover (the web-app top-nav idiom). Composes on .seg--borderless.

A raised card-pill active tab (--surface-card + --elev-1) plus a lighter neutral hover on a transparent track, full-pill radius. Element-agnostic, shown as both a site-nav bar and an in-place view switcher. Demos sit on a recessed well so the white pill reads (as it does on the playground canvas).

Large (nav) tier · .seg-lg.seg--borderless.seg--raised · site nav

Default tier · .seg.seg--borderless.seg--raised · view switcher

Layout modifiers --block · --hug · --scroll · --equal

Four composable modifiers handle long labels, narrow parents, or oversized slots. Default already truncates with ellipsis.

Default — truncation with ellipsis (parent is 240px wide)
.seg--block — fills parent width
.seg--hug — sizes to labels, leaves empty space in the parent (parent shown as 420 px)
.seg--scroll — horizontal scroll, hidden scrollbar (scroll right →)
.seg--equal — strict equal-width (ignores label length)

Trailing dropdowns

Overflow .seg-overflow

Surplus tabs collapse behind a ··· trigger that opens a menu. The sibling answer to --scroll when the option set keeps growing — reach for it past ~4 tabs in a fixed-width container.

The ··· trigger is live — click to open the menu via the Popover API. Here the active mode (Gas) lives inside the overflow, so the trigger carries the active fill and the menu entry shows the checkmark.

Active mode collapsed into overflow

Menu tab .seg-menu

Split trailing tab — the label half activates the current option, the chevron half opens a menu to swap which option the slot holds.

Every tab below is live. The trailing tab is split: click Limit (the label half) to activate it like a normal tab; click the chevron to open the menu. Picking DCA swaps the tab's label and activates it — the slot is a swappable "third alternative".

Split-tab menu · .seg-menu

States

States — focus & disabled

Hover and active states ship by default. Focus-visible and disabled (via disabled or aria-disabled) are first-class.

Focus ring — Tab to the track, then ← / → to navigate
Disabled — via disabled or aria-disabled="true"
Components
Skeleton
Loading-state placeholder. Renders a token-tinted shape with a subtle shimmer sweep — the universal "content is on the way" signal that holds the slot's layout open so nothing jumps when data arrives. Four shape modifiers (block / --text / --circle / --pill) compose into any consumer; text-line variants share the sm / default / lg / xl ladder. Sibling of Spinner — reach for Skeleton when new content is arriving (lists, cards, page bodies); reach for Spinner when existing UI is mid-action (signature pending, refreshing, polling).

Skeleton .skeleton

Universal loading-state primitive. Token-tinted shape with always-on shimmer; four shapes compose into any slot the data will land in.

Shapes — block · --text · --circle · --pill

.skeleton · block
.skeleton--text · 1em
.skeleton--circle · 40 px
.skeleton--pill · 24 px

Text sizes — --sm 12 · default 16 · --lg 18 · --xl 24

.skeleton--sm · 12 px
.skeleton--text · 16 px (1em)
.skeleton--lg · 18 px
.skeleton--xl · 24 px

In context — quote card & amount card placeholder compositions


Quote-card composition — chip + tandem avatar + amount + meta + footer row
Amount-card composition — label + amount + token-pill + fiat / balance footer
Components
Sliders
Horizontal range control following Apple's Human Interface Guidelines. Track fills from the minimum value to the thumb using the active accent color, so every slider instantly reflects the Theme Composer. Use for continuous values (opacity, volume-like settings, scale) and — with tick marks — stepped values. Never use a slider for audio volume (use a system volume control).

Slider .slider

HIG-style continuous range control on product surfaces. Three sizes, three accents; track fills in the active accent.

Default — primary accent

Opacity 50%

With min / max icons (HIG pattern)

Font size 16px

Accent variants

Accent 2 (secondary) 70
Accent 3 (tertiary) 35

Sizes

Small (compact / sidebar) 25
Default 60
Large (comfortable touch target) 85

Slider field .slider-field · input.slider-value

Labeled control (label + readout above the range) whose readout is an EDITABLE numeric input — type, paste or arrow-step for precision.

Both readouts below are live — drag the range, or click the number and type (Enter commits · ↑/↓ steps · Shift×10 · Esc reverts).

Default tier · editable readout + unit

Card radius px

Compact tier · .slider-field--compact — dense multi-control panels

Chroma

Discrete & disabled .slider--discrete

Tick-marked stepped variant for preset choices, plus the disabled state for read-only or locked settings.

Discrete — tick marks

Quality preset Medium
Low Medium High Max

Disabled

Locked setting 40
Components
Spinner
Loading indicator family. Three types share one .spinner wrapper API and a 4-tier size ladder (sm / default / lg / xl, 16–32 px). The circular ring is the default and carries the full vocabulary — sizes, tones, label, vertical orientation, and a determinate --progress arc. Dots and bars are alternate types documented for completeness; neither is in active production use today.

Circular .spinner + .spinner__ring

Default loading gesture. Four sizes, five tones, optional label and vertical orientation, plus a determinate --progress arc — reach for this in ~99 % of cases.

Sizes — sm 16 · default 20 · lg 24 · xl 32

.spinner--sm · 16 px
.spinner · default 20 px
.spinner--lg · 24 px
.spinner--xl · 32 px

Tones — currentColor (default) · primary · secondary · tertiary · alpha

currentColor
.spinner--primary
.spinner--secondary
.spinner--tertiary
.spinner--alpha

Progress — .spinner--progress + style="--spinner-progress: 0..1"

0 % · alpha
25 % · alpha
50 % · alpha
75 % · alpha
100 % · primary

With label — .spinner--with-label + .spinner__label

Loading routes… Routing quote · ETH → ARB Settling cross-chain transaction

Vertical — .spinner--vertical for empty states & full-screen loaders

Finding routes… Settling cross-chain

No routes available

Searching alternatives…

Dots .spinner--dots

Three bouncing dots — alternate type for inline text rows where the ring feels heavy. Not in active production use today.

Sizes — sm 16 · default 20 · lg 24 · xl 32

.spinner--sm · 16 px
.spinner--dots · default 20 px
.spinner--lg · 24 px
.spinner--xl · 32 px

With label — .spinner--dots.spinner--with-label

Fetching wallet balances

Bars .spinner--bars

Three scaling bars — alternate type reading as "working / processing" rather than a generic load. Not in active production use today.

Sizes — sm 16 · default 20 · lg 24 · xl 32

.spinner--sm · 16 px
.spinner--bars · default 20 px
.spinner--lg · 24 px
.spinner--xl · 32 px

With label — .spinner--bars.spinner--with-label

Awaiting signature
Components
Spot Icon
Small rounded tile with an SVG icon centered inside — the focal mark that makes a menu row feel clickable, an action card actionable, a settings row interactive. The icon paints in the brand accent over a subtle alpha-tinted surface; seven sizes scale tile + icon + radius together, from a dense 32 px row tile up to a 96 px empty-state / hero anchor.

Spot icon .spot-icon

Universal icon-tile — accent glyph on alpha surface. Seven sizes (32 → 96 px); menu rows, action cards, hero blocks, empty states.

default .spot-icon (32 px)
32 size · 16 icon · 8 pad
.spot-icon-lg (40 px)
40 size · 20 icon · 12 pad
.spot-icon-xl (48 px)
48 size · 24 icon · 16 pad
.spot-icon-2xl (56 px)
56 size · 28 icon · 20 pad
.spot-icon-3xl
64 · 32 · 24
.spot-icon-4xl
80 · 40 · 28
.spot-icon-5xl
96 · 48 · 32

Tone variants .spot-icon--success · .spot-icon--info · .spot-icon--warn · .spot-icon--danger · .spot-icon--neutral

Four semantic tones drive both the icon color and a 14% background-tint from a single --si-tone token; --neutral is a quiet grey plate + primary-ink glyph for non-semantic icons. Compose with any size modifier.

--success
var(--success)
--info
var(--info)
--warn
var(--warn)
--danger
var(--danger)
--neutral
8% --text-primary

Shape variant .spot-icon--round

Optional shape modifier — flips the rounded-square tile to a perfect circle. Composes with any size and tone.

top row · .spot-icon (rounded square)  ·  bottom row · .spot-icon--round (perfect circle)
Components
State block
Centered focal-mark stack for a panel, modal, or region that needs to communicate a STATE or a PROMPT — a connect-wallet prompt, a “Waiting for {wallet}” status, an onboarding nudge, an error, a success confirmation. A focal mark (an .avatar, a .spot-icon, or a .spinner) sits over an optional .state-block__title, the supporting .state-block__message, and an optional .state-block__action — all centered, with generous top + bottom breathing (40 px block padding) built in so the composition never crowds the content above or below it. Omit the slots you don't need; the gaps cascade so an omitted slot never leaves an orphan. Sibling of .empty-state, which shares this exact recipe and adds two deltas of its own — it FILLS its region (vertically centering in a sized panel / canvas) and MUTES the focal icon (“nothing here”, not an alert). The wallet-connect wizard (ecosystem intro + “Waiting for…” connecting screen) is the canonical consumer.

State block .state-block

Centered focal-mark stack — avatar / icon / spinner over optional title, message, and action slots; built-in vertical breathing room.

Avatar + message

MetaMask supports multiple chain ecosystems. Select which one to connect to.

Avatar + title + message

Connection approved

Your wallet is linked — routing quotes across 30+ chains now.

Avatar + title + message + action

Connect a wallet

Connect to view balances and get route quotes.

Trailing spinner · connecting

Waiting for MetaMask

Click connect in your wallet popup. Don't see your wallet? Check your other browser windows.

Spinner focal · loading

Finding the best route across bridges…

Icon focal · .empty-state sibling

No transactions yet

Your swap and bridge history will show up here.

The focal mark is the first child — an .avatar (any size; --2xl 96 px for hero prompts), a .spot-icon, or a .spinner. A trailing spinner (cell 4) sits in the action slot, 20 px below the message. For a muted “nothing here” placeholder that fills + centers its region, reach for the .empty-state sibling.

Components
Status dot
Small colored circle without content. Sibling of .spot-icon (the tile-with-content primitive); .status-dot covers the cases below its 32-px floor where there's no glyph room. Step bullets, presence indicators (online · offline · busy), unread notification marks, "saving / saved" indicators, activity-feed timeline bullets, settings-completeness rails.

Status dot .status-dot

Three sizes (xs / default / lg), five tones, plus a step-state vocabulary shared with .spot-icon.

Sizes — xs · default · lg, filled accent

.status-dot--xs · 8 px

.status-dot · 12 px

.status-dot--lg · 16 px

Tones — info · success · warn · danger · neutral

Quote pending
Route settled
High slippage
Failed
Idle

Step state — complete · current · upcoming (same API as .spot-icon)

Complete
Current
Upcoming

In context — five real consumer surfaces

Where .status-dot earns its keep: avatar presence dot, unread notification mark, activity-feed timeline, settings-completeness rail, dirty-state indicator.

Presence dot on avatar

EM
Ema · vitalik.ethOnline
JL
Jia · 0x9aF…3B71Offline · 2h ago

Unread notification mark

Notifications 3
Activity
Wallets

Activity-feed timeline

Bridged 1.2 ETH to Arbitrum 2m
Quote requested · USDC → ETH 5m
Sign rejected in wallet 8m
Wallet connected 12m

Settings completeness rail

Connect wallet DONE
Verify email DONE
Pick default chain NOW
Set slippage preferences SOON

Dirty-state indicator

UNSAVED
0.5%
SAVED
Fastest

Halo modifier .status-dot--halo

Soft outward wash that spotlights ONE dot in a row — changelog timelines, live indicators, "you are here" markers. Tone follows --sd-tone.

Sizes — xs · default · lg with halo, filled accent

.status-dot--xs.status-dot--halo

.status-dot.status-dot--halo

.status-dot--lg.status-dot--halo

Tone follows --sd-tone — same five variants

Live · accepting bids
All routes operational
Settlement delayed
Bridge offline

Canonical use — changelog timeline · halo marks the active milestone

Releases
Plan and ship cross-chain bridge releases directly from LI.FI.
May 12, 2026
Widget SDK v4
New event hooks for route selection and quote refresh callbacks.
May 5, 2026
Solana support
Native bridging between Solana and EVM chains via Mayan and deBridge.
Apr 28, 2026
MEV protection
Route-level MEV settings — opt-in private mempool for sensitive swaps.
Apr 21, 2026

Pulse modifier .status-dot--pulse

Animated counterpart to --halo. An outward sonar-ping loops on a 1.6 s cadence — use when the motion itself is the signal (live arrivals, real-time alerts).

Sizes — xs · default · lg with pulse, filled accent

.status-dot--xs.status-dot--pulse

.status-dot.status-dot--pulse

.status-dot--lg.status-dot--pulse

Tone follows --sd-tone — same five variants

Quote refreshing
Route settling now
Waiting on wallet
Failed · retrying

Canonical use — live status bar · pulse marks the single in-flight signal

Ethereum bridge Operational
Arbitrum settlement · retrying Live
Solana bridge (via Mayan) Operational
Cosmos IBC (paused for maintenance) Paused

Status row .status-row

Single confirmation step row with [icon] [label] [optional action]. Used in widget completion screens and any tx-confirmation flow.

USDC spending approved
Bridge transaction confirmed
Sent to wallet: 0×92E5c…AEE74
Bridge transaction failed
Bridging to Ethereum
Swapped to stETH
Components
Swatch
A 1:1 color-display surface used anywhere the system needs to render a single colour — the palette in Colors, the picker swatches in Theme Composer, the strip previews in Theme cards, plus marketing brand-guide pages, prose tables, decks, and the Colors tabs on individual component cards. Ten variants — five sizes (--xs / --sm / default / --lg / --xl) × shape modifiers (--circle / --square / --strip) × chip-wrapped variants (.color-chip family) × strip container (.swatch-strip) × the new --state variant for component state-colour reference. Foundations / Colors consumes this primitive to display the brand palette; it does not own it.

Swatch — three templates + a size ladder for every color-display surface

The unified color-display catalog. Three canonical templates lead — .swatch--label-inside (name + hex on the tile, for brand identities), caption-below (.swatch--lg, tile + name / hex / token for engineer grids), and .swatch-ramp (tonal scales + the surface ladder). Beneath them, the size ladder (--xs / --sm / default / --lg / --xl), shape modifiers, chip-wrapped variants, and the --state reference row. Every tile reads the content-aware --swatch-edge — a % of the ink that adapts per mode, never a strong white border.

One color-display primitive across the system — from a 16 px inline dot to a 200 px hero brand card. The three canonical templates below unify the larger swatch representations across Foundations, the Brand Book, and marketing spreads; the size-ladder + chip variants beneath them remain for compact reference. Every tile reads the content-aware --swatch-edge (a % of the ink) so its edge is visible in both modes without a strong white border — toggle the theme to see it adapt.

Template 1 — Inside label · .swatch--label-inside · name + hex on the tile · brand identities

Surface
Ink#0C0E2E
Action
Sapphire#405CCF
Signature
Pink#F7C2FF
Surface
Paper#FAFBFF
Text
Slate#5C6070

Template 2 — Caption below · .swatch--lg · tile + name / hex / token · engineer-facing grids

Ink
#0C0E2E--lifi-ink
Sapphire
#405CCF--lifi-sapphire
Pink
#F7C2FF--lifi-pink
Slate
#5C6070--text-secondary

Template 3 — Tonal ramp · .swatch-ramp · scale header + steps; --surfaces adds hex below

Ink#0C0E2E
1F0F3FF2DADDEA3BEC3D949CA3C25757DA66535B817363D6181F234290C0E2E
Sapphire#405CCF
1E5F3FF2CCDDFF3AFC3F948BA2E45627BCA6405CCF71F2F9780F007C90E005C
Pink#F7C2FF
1FDEEFF2F7C2FF3D4BAD84BA96C059C6CA4677497F7572B5E83A13409200026
Surfaceswhite → black · ● = brand palette
WhiteFFFFFFPaperFAFBFFSlate5C6070Card262C55Raised161A38Ink0C0E2EBlack000000

Hero brand card · .swatch--xl · 200 px · brand identity + marketing hero palettes

Primary
Brand anchor
Blue
--accent-primary oklch(68% 0.220 268) #5F8AFF
Secondary
Gradient sidekick
Lavender
--accent-secondary oklch(86% 0.095 315) #E9BFFC
Tertiary
Spot accent
Violet
--accent-tertiary oklch(75% 0.150 290) #AD9CFF

Reference card · .swatch--lg · 120 px · catalog default + brand-guide spreads

Success
--success oklch(72% 0.17 161) #00C481
Danger
--danger oklch(64% 0.22 25) #F53C41
Warn
--warn oklch(72% 0.16 85) #D29A00
Blue
--accent-primary oklch(68% 0.220 268) #5F8AFF

Compact card · .swatch (default) · 80 px · dense indexed grids (spectrum, semantic-pair)

Coral
--spectral-1 oklch(0.72 0.20 20) #FF8C7A
Amber
--spectral-3 oklch(0.82 0.18 80) #FFB454
Green
--spectral-5 oklch(0.78 0.22 140) #5EEAA0
Cyan
--spectral-7 oklch(0.78 0.18 205) #42E2F5
Blue
--spectral-9 oklch(0.70 0.22 265) #7B86FF
Magenta
--spectral-11 oklch(0.72 0.24 325) #D472FA

State swatch · .swatch--state · 80 px · component state-colour reference for Colors tabs

Six fields layered onto the default tier: an inline source-classification flag (inherited / override / convention), the interaction state name, the canonical token or color-mix recipe, the scoped CSS selector that consumes it, and a one-sentence rationale. Use exclusively inside a card's Colors tab to document a component's interaction states (hover, selected, focus, active, disabled, pinned).

Inherited
Hover
var(--overlay-hover) .list-item--interactive:hover
System-wide neutral overlay (~4–5% near-black/white, theme-aware). No scoped override.
Inherited
Selected
color-mix(in oklch, var(--accent-primary) 12%, transparent) .list-item--selected, .list-item.is-selected
System-wide default for every .list-item consumer. Palette-aware via --accent-primary. Three flag tones (inherited / override / convention) exist for cases where a context needs to deviate — this swatch demonstrates the typical inherited case.
Convention
Pinned indicator
var(--lifi-sapphire) .ui-token-list .list-item.is-pinned [data-pin]
SVG fill: currentColor; the visible blue pin is the state indicator at rest.

Picker dot · .swatch--sm + --circle + --interactive · 32 px · palette pickers + presets

Hover the dots — each glows in its own colour, not a hardcoded accent.

Inline dot · .swatch--xs · 16 px · in-prose color marks (marketing + product docs)

Use in prose to mark a token reference: the primary, secondary, tertiary, success, danger, and warn tokens.

Token chip · .color-chip · pill + dot + mono label · documentation tables, anatomy rows

--accent-primary --accent-primary-mid --accent-secondary --accent-tertiary --text-muted --success --text-primary --ink-400

Compact dot chip · .color-chip--swatch-only · pill + dot, no label · dense table cells

Alpha-token preview · .color-chip--on-dark / --on-light · low-alpha overlays

--alpha-04 --alpha-12 --alpha-06 --alpha-14

Gradient swatch · pass a gradient via --swatch-color · brand-mark tiles

Brand gradient
linear-gradient(135deg, ...) primary → secondary
Tri-accent
linear-gradient(3-stop) primary → secondary → tertiary
Radial
radial-gradient(...) tertiary → primary
Conic
conic-gradient(...) 3-accent loop

Any CSS background value works as --swatch-color — linear, radial, conic, multi-stop gradients all render. The on-tile label colour (--swatch-on-color) falls back to the parent's text colour on gradient values, so keep on-tile labels short or override via inline style.

Palette strip · .swatch-strip + .swatch--strip · theme card previews + composer thumbnails

Default 36 px height. Override via --swatch-strip-h (200 px strip is 24 px).

Swatch editor .swatch-edit

Expandable colour-token editor — collapsed shows the swatch + hex; expand to reveal the L/C/H sliders or a direct-assign field.

A first-class colour-token editor built on the universal .reveal drawer. The head is the always-visible row — swatch chip, name, current value, chevron; the drawer is a slot the consumer fills (the Theme Composer drops L/C/H sliders in Auto mode, a .color-field assign in Manual mode). Reads one tier lighter than the composer's framed sections: a row in a list, not a card. Click a head to expand it.

Lightness44
Chroma0.230
Hue268
Lightness75
Chroma0.140
Hue315
Lightness100
Chroma0.000
Hue268
Components
Table
Tabular layout for product UI — dashboards, swap routes, settings, leaderboards, ledgers, embeddable widgets. The .data-table base composes existing DS primitives (.avatar, .chip, .status-dot, .btn-sm, sparkline, progress) inside cells — never invent table-specific replacements for those primitives. Three row-height tiers, six style modifiers, two sticky modes, sortable / selectable / expandable interaction, and a sibling .pagination control. Use across product, dashboard, and marketing comparison surfaces.

Anatomy

Data table .data-table

Base shape — muted normal-case thead, 16 px sans tbody, optional tfoot for totals. No monotype by default; right-align the action column.

PropertyValueToken
Row height56 pxvar(--space-56)
Header weight400--fw-regular
Cell border1 pxvar(--border-subtle)
Hover bgtonalvar(--surface-sunk)
Total4 rules

Sizes

Size ladder .data-table--sm · .data-table--md · .data-table · .data-table--xl

Four tiers on the 4-grid — 32 / 40 / 56 / 64 px rows. Header ladder steps 10 / 12 / 14 / 16; --sm's 10 px header forces ALL-CAPS (the one exemption to the normal-case voice — legible at micro size).

Compact · .data-table--sm · th 10 caps / td 14 · 32 px

AssetBalanceValue24h
ETH1.2400$3,520.21+2.1%
USDC2,841.20$2,841.200.0%
ARB412.30$487.51−1.4%

Cozy · .data-table--md · th 12 / td 16 · 40 px

AssetBalanceValue24h
ETH1.2400$3,520.21+2.1%
USDC2,841.20$2,841.200.0%
ARB412.30$487.51−1.4%

Default · .data-table · th 14 / td 16 · 56 px

AssetBalanceValue24h
ETH1.2400$3,520.21+2.1%
USDC2,841.20$2,841.200.0%
ARB412.30$487.51−1.4%

Spacious · .data-table--xl · th 16 / td 18 · 64 px

AssetBalanceValue24h
ETH1.2400$3,520.21+2.1%
USDC2,841.20$2,841.200.0%
ARB412.30$487.51−1.4%

Modifiers

Striped .data-table--striped

Zebra rows on tbody. Right for dense ledgers and long lists where the eye needs help tracking across many columns.

#Tx hashFromToAmountStatus
28410x8f…a12c0x91…44e20x7b…29011.42 ETHConfirmed
28400x4e…0b8d0x91…44e20x33…ff10820 USDCConfirmed
28390xa2…91c40x91…44e20x05…d7220.04 ETHPending
28380x77…22410x91…44e20x12…aa01412 ARBConfirmed
28370xc1…ee320x91…44e20x88…66442,400 USDCFailed

Bordered .data-table--bordered

Vertical + horizontal cell grid. Right for spec sheets and comparison tables where columns carry independent meaning.

ChainNativeGas (gwei)FinalityStatus
EthereumETH24.1~12 minLive
ArbitrumETH0.10~15 sLive
BaseETH0.05~2 sLive
PolygonPOL30.2~2 minLive

Borderless .data-table--borderless

No row borders. Right inside cards that already provide chrome, or paired with --striped where the stripe carries the row separation on its own.

SettingValue
Slippage tolerance0.50%
Transaction deadline20 min
Gas priceAuto
MEV protectionOn

Numeric alignment .data-table--align-numeric

Default-right-align every cell except the first label column. Right for PnL / ledger / accounting where every column is a number.

AssetQuantityAvg costMarketPnLPnL %
ETH1.2400$2,820.10$3,520.21+$868.14+24.8%
USDC2,841.20$1.00$1.00$0.000.0%
ARB412.30$1.42$1.18−$98.95−16.9%
OP220.00$2.10$2.34+$52.80+11.4%
Total+$821.99+11.6%

Rich rows .data-table--rich

Opt-in geometric-alignment chassis so avatars, text, chips, and meters land on one centre line per row. Pairs with the .data-cell family; promoted from the Orders table.

TokenPairBalanceValueStatus
ETH
ETH
1.2400
$3,520.21
Confirmed
USDC
USDC
2,841.20
$2,841.20
Pending
ARB
ARB
412.30
$487.51
Failed

Avatar, bold token, tabular numbers, and a status chip all land on one centre line — the geometric centring .data-table--rich adds over the base vertical-align model.

Cell content

Cell vocabulary — kitchen sink

Every cell-level visual reaches for an existing DS primitive — avatar, chip, status dot, mono numerics, sparkline, progress, action button.

TokenChainBalancePrice · 7dAllocationStatus
ETHETH Ethereum 1.2400
Active
USDCUSDC Arbitrum 2,841.20
Active
ARBARB Arbitrum 412.30
Watching

Rich cell content .data-cell · .data-cell-text

The flex cell wrapper plus one text chassis for rich rows. Weight and ink are the only variant axes; size is a slot.

Base · .data-cell-text

1,264.00

Bold · .data-cell-text--bold — the key number / identifier

1,580

Secondary · .data-cell-text--secondary — active countdown

6 days

Muted · .data-cell-text--muted — market price / expired

$3,555

Danger · .data-cell-text--danger — cancelled / failed terminal state

Cancelled

Interaction

Sortable headers th.is-sortable

Add .is-sortable to any th and pair with aria-sort. The chevron icon rotates and accent-paints to indicate the active sort column and direction.

Token Balance Value 24h
USDC2,841.20$2,841.200.0%
ETH1.2400$3,520.21+2.1%
ARB412.30$487.51−1.4%

Balance column is sorted descending. Click another header to switch.

Selectable rows tr.is-selected

Checkbox lead column for bulk operations; selected rows tint with the accent tone and survive hover. Pair with the bulk action bar.

Tx hashToAmountStatus
0x8f…a12c0x7b…29011.42 ETH Confirmed
0x4e…0b8d0x33…ff10820 USDC Confirmed
0xa2…91c40x05…d7220.04 ETH Pending
0x77…22410x12…aa01412 ARB Confirmed

Expandable rows .data-table__detail-row

Chevron in the lead cell flips when the row is expanded; a sibling detail row with colspan renders the expanded content directly below.

RouteBridgeEst. timeOutput
ETH → USDCAcross~8 s2,841.20 USDC
Source
Optimism · 1.2400 ETH
Destination
Arbitrum · 2,841.20 USDC
Bridge fee
$0.42
Gas estimate
$1.18
Price impact
0.02%
ETH → USDCStargate~45 s2,839.80 USDC
ETH → USDCHop~2 m2,836.45 USDC

Sticky

Sticky header .data-table--sticky-head

Header stays pinned while tbody scrolls — the parent must be a scroll container with a fixed max-height. Scroll the demo to see it engage.

#TokenBalanceValue
1ETH1.2400$3,520.21
2USDC2,841.20$2,841.20
3ARB412.30$487.51
4OP220.00$514.80
5MATIC1,440.10$863.66
6LINK88.50$1,234.65
7UNI120.40$967.21
8AAVE12.10$1,442.32
9SOL28.00$4,210.00
10WBTC0.0420$2,940.00
11DAI1,200.00$1,200.00
12FRAX800.00$800.00

Sticky first column .data-table--sticky-col

First column stays put when the table scrolls horizontally. Pairs with .ds-doc-table-wrap. Scroll the demo right to see it engage.

TokenEthereumArbitrumOptimismBasePolygonBSCAvalanche
ETH1.24000.42000.11000.0820
USDC2,841.201,200.00580.00312.101,440.00880.00220.00
USDT412.00220.00120.00340.001,920.0088.00
DAI1,200.00

Scroll affordance .table-scroll

JS-driven horizontal-scroll wrapper for wide tables: edge fades, a < > nav that auto-hides when the table fits, and an optional pinned first column. The richer sibling of .data-table--sticky-col.

Live — scroll the table (drag, shift-scroll, or the < > arrows). The first column pins, edges fade to signal more content, and the arrows disable at each end. shared.js → initTableScroll() wires every .table-scroll on the page automatically.

TokenEthereumArbitrumOptimismBasePolygonBSCAvalanche
ETH1.24000.42000.11000.0820
USDC2,841.201,200.00580.00312.101,440.00880.00220.00
USDT412.00220.00120.00340.001,920.0088.00
DAI1,200.00

States

Loading skeleton

Compose a loading state from .skeleton bars inside cells. Preserves column widths and row heights so the layout doesn't jump on settle.

TokenBalanceValue24h

Empty state .data-table__empty

Single full-width tbody row with colspan matching the header count. Centered, muted text. Pair with a primary CTA when there's an obvious next action.

Tx hashFromToAmountStatus
No transactions yet

Your transaction history will show up here once you complete your first swap or bridge.

Pagination

Table + pagination — full footer

Pairs the .data-table with the sibling .pagination primitive. Caption on the left (row-count info), controls on the right.

#Tx hashAmountStatusTime
28410x8f…a12c1.42 ETHConfirmed2 min ago
28400x4e…0b8d820 USDCConfirmed8 min ago
28390xa2…91c40.04 ETHPending14 min ago
28380x77…2241412 ARBConfirmed22 min ago
28370xc1…ee322,400 USDCFailed35 min ago

Showing 1–5 of 287 transactions

Actions

Row actions — kebab menu

Trailing kebab cell opens a .menu with row-scoped actions. Right-align the action column; never expose more than one menu trigger per row.

DestinationLast usedTotal volume
AcrossAcross 3 days ago 4.20 ETH
StargateStargate 1 wk ago 1,200 USDC
HopHop 2 wk ago 820 USDC

Bulk action bar

When rows are selected, an action bar surfaces above the table with the count + scoped actions. Author as a sibling div, not a table cell.

2 selected of 4 transactions

Tx hashAmountStatus
0x8f…a12c1.42 ETHConfirmed
0x4e…0b8d820 USDCConfirmed
0xa2…91c40.04 ETHPending
0x77…2241412 ARBConfirmed

In context

Dashboard — analytics ledger

Compact tier · striped · sortable — fits inside a dashboard card next to charts. Composition: avatar + chip + status-dot + sparkline.

Partner Chain Volume (7d) Trend Routes Status
UniswapUniswapEthereum$284.1M12,841Live
AcrossAcrossMulti$182.7M8,210Live
StargateStargateMulti$98.4M4,420Degraded
HopHopL2$42.1M2,140Live
Total$607.3M27,611

Playground — route comparison

Default tier · expandable rows · inline action button. The shape the swap widget uses today — pick a route, expand to inspect fees + gas.

RouteBridgeEst. timeFeeOutput
ETH → USDCAcross~8 s$0.422,841.20 USDC
ETH → USDCStargate~45 s$0.672,839.80 USDC
ETH → USDCHop~2 m$1.122,836.45 USDC
ETH → USDCConnext~3 m$1.842,834.10 USDC

Widget — embeddable compact

Compact + borderless inside a card. Fits the LI.FI widget canvas (~360 px wide) — three columns, no chrome, status-dot for instant scan.

Recent activity

ETHETH1.42
USDCUSDC820
ARBARB412
POLPOL1,440

Panel

Table panel .table-panel

Frames a data table in a panel — a .screen-header above a .table-scroll-wrapped table, edge-bled to the panel rim. The generalised Orders-panel chrome.

Routes

BridgeOutputFeeTimeStatus
Across
1,264.00
$1.20
~2 min
Best
Stargate
1,261.40
$2.80
~4 min
Alt
Hop
1,259.10
$3.10
~6 min
Alt

Header title + a rich table, edge-bled so the first column lines up under the title and the scroll affordance reaches the panel rim.

Components
Theme composer panel
The one container the Theme Composer editor (theme-editor.js → ThemeComposer.mount) renders into across all three of its host surfaces — the FAB (floating), the Portal Settings → Theme modal, and the playground rail (flush). The editor carries no chrome of its own; .theme-composer IS the chrome, so reuse means import the panel, not rebuild it three times. Composes .panel + its .panel__header / __body / __footer slots: the header sits in flow, the body is the clip-and-host scroll region + the editor’s mount target, an optional footer pins per-theme actions. Width is published as --theme-composer-width (default --w-9 = 480 px) so the Colors section breathes — the retired 288 px FAB glass sub-panel clipped it. Two variants: --floating (FAB — anchored bottom-right with a fade+slide) and --flush (rail — chromeless + 100 % width so it never nests a second surface inside the rail’s own panel). Source of truth: design/theme-composer.md.

Theme composer .theme-composer

The shared panel the Theme Composer editor mounts into — one fixed-width, clip-and-host scroll model across the FAB, Portal, and rail.

Default · .theme-composer · header · scrollable body · footer

LI.FI 1.0

Panel 32 · cards 16 · buttons 16.

The live editor renders the swatch-strip picker + shared L/C/H trio here.

The live editor (ThemeComposer.mount) renders the section stack (Colors · Semantic states · Charts · Corners · Panels · Spacing) into this body — .setting-row--expand cards in a .setting-stack--tight. The body scrolls; the header and footer stay pinned.

Composes .panel + the three slots. Fixed 480 px (--theme-composer-width); capped to 340 px tall here so the body scroll engages.

Flush · .theme-composer--flush · chromeless — the host owns the surface

Edit theme

Inside a host that already provides the panel surface, header, and scroll (the playground rail), the composer sheds its own chrome — no shadow, no border, no padding, 100 % width — so it never nests a second surface.

The rail (itself a .panel) hosts the flush composer — the surface you see is the rail’s, not a second one.

Components
Theme toggle
The canonical site-wide light/dark switch. An icon-only circular button that crossfades a sun and a moon glyph; shared.js → initThemeToggle binds every instance on the page, flips <html data-theme>, persists the choice to localStorage["lifi-theme"], and re-syncs aria-pressed across all toggles. Used in three contexts: the site / catalog navbar, the playground rail head, and the playground theme-edit screen-header. Composes the button family (.btn-icon.btn-borderless.btn-circle) — there is no bespoke toggle primitive.

Theme toggle .theme-toggle

Site-wide light/dark switch. Crossfades a sun (light) and moon (dark) glyph; the glyph shows the mode you're currently in.

The toggles below are inert documentation mocks — initThemeToggle skips any instance inside a .ds-doc-viewport-pane so clicking one won't flip the catalog. They reflect the catalog's current theme; use the real toggle in the navbar above to watch the crossfade.

Glyphs — current-mode convention: sun in light mode · moon in dark mode

.icon-sun — light mode .icon-moon — dark mode

Compositions — navbar .btn-tertiary · rail head .btn-neutral

Navbar Rail head
Components
Domain Components
Token chips and chain badges are domain-specific components used throughout the swap and bridge UI. Token chips show a colored icon with the ticker; chain badges use dark backgrounds with colored dots.

Token chips & chain badges .token-chip · .chain-badge

Domain primitives for swap / bridge UIs on product surfaces. Token icon + ticker, or chain dot + name.

Ξ
ETH
$
USDC
$
USDT
A
Arbitrum
B
Base
P
Polygon
Components
Tooltip
Small floating label for terse explanations on hover or keyboard focus. Wrapper-based pattern so the show-state is pure CSS — no JS required. Four positions, optional arrow, accessible by default (role="tooltip" + aria-describedby). Use for icon-only buttons that need a name, dense data cells that need clarification, and any control whose purpose isn't already legible from its label.

Tooltip .tooltip

Floating label on hover or focus. Wrap the trigger in .tooltip-wrap so the show-state cascades via CSS — no JS.

Hover any trigger — or tab into it from the keyboard — to see the tooltip slide and fade in. Six triggers cover every position and modifier in one frame; the button text labels each variant.

Default position Slides in from the side Below the trigger Other side Maximum price movement allowed before the swap reverts. 0.5% is a safe default for most pairs; raise it for low-liquidity tokens. Pure label, no caret
Cards
Cards — product UI surfaces

Cards are the product-side card family — neutral, dense, used inside the signed-in app and dashboards. Variants below extend the primitive with accents, behaviour, and stat layouts.

  • Audience-led naming. .card = product UI surfaces · .tile = marketing splash surfaces. Same family, two nouns picked by where it ships.
  • Shared chrome with tiles. --surface-raised fill, --elev-1 shadow, 4 % currentColor hairline. Hover bumps to --elev-2.
  • Diverge on density. Body-scale type, tighter padding, no accent fill by default. Accent treatments are explicit variants (.accent-card, gradient, halo).
  • Spec. design/components/foundations.md → Tile vs Card — audience-led naming.
Cards
Accent cards
Tier cards with accent-coloured borders, glow, and feature checklists. Canonical use is pricing — three variants drive the tier hierarchy (recommended → standard → secondary).

Accent card .accent-card

Tier card with accent border + glow + feature checklist. Three variants drive the hierarchy — canonical use is pricing.

Primary .accent-card--primary

Enterprise
Cross-Chain Execution
Route any asset across 60+ chains with a single API call. Optimized for speed, cost, and reliability.
  • Best-price routing across 30+ bridges
  • Sub-second execution on supported routes
  • Enterprise-grade reliability and uptime

Secondary .accent-card--secondary

Pro
AI-Native Trading
Let AI agents execute cross-chain swaps autonomously with built-in safety rails and intent resolution.
  • MCP server and OpenAPI integration
  • Natural language intent parsing
  • Configurable guardrails and limits

Tertiary .accent-card--tertiary

Free Tier
Standard
Start instantly with best-price swaps, backed by deep liquidity — all through our widget, SDK, or API.
  • Up to 200 requests per minute
  • Technical support via help.li.fi
  • Flexible monetization tools

Card glow utility .card-glow

Opt-in 3-layer shadow recipe — accent halo for any card. Used on pricing tiers, hero callouts, featured product cards in marketing grids.

Primary
Default — accent-primary halo. Use for recommended tiers, featured cards, or the anchor card in a row.
Secondary
Accent-secondary halo. Pairs with secondary callouts — product filters, sibling tiers.
Tertiary
Accent-tertiary halo. Use sparingly as the third member of a three-up grid.
Components
Action Card
A drop-in doorway affordance — icon + title + description + trailing arrow — for opening a tool, jumping to a page, or launching a modal. Rendered as a <button> or <a> depending on destination. Every visual property lives on a --ac-* CSS custom property so consumers can tweak per-instance without forking the class. Three sizes, five tones, two layouts — compose what you need.

Action card .action-card

Marketing CTA card — icon + title + body + arrow. Three sizes, two tones (neutral / accent), full-card link variant.

Cards
Card behavior
Every card variant uses one shared two-state recipe — accent border on hover, soft halo on active. No lift, no pseudo-element gradients. Behavior modifiers and state classes live here; surface mechanics in design/components/foundations.md → Tile vs Card — audience-led naming.

Card interaction — hover & active

Every card variant uses one shared two-state recipe — accent border on hover, soft halo on active. No lift, no pseudo-element gradients.

Rest
Surface colour, subtle border. No accent tint.
Hover
Background warms by 6 %, border steps up to --accent-primary-mid.
Active
Background at 7 %, soft 18 px-blurred accent halo via box-shadow.
Cards
Destination cards
Navigation / hub card — heading, a 16:9 preview frame (a theme-swappable screenshot or any media), a short bullet list, and a full-width justified CTA. Extends .tile, so the surface, hover / active accent wash, focus ring, and the --tile-accent system all come for free. Built for hub / lobby / landing index pages; first consumer is the Launchpad (site root).

Destination card .destination-card

Hub / lobby card on .tile — title, 16:9 theme-swappable preview, bullet summary, justified CTA. Accent binds per card via the tile API.

Hub cards — accent 1 · accent 2

Padding 32 · card radius 32. The outward formula min(--button-radius + padding, --space-32) would compute 16 + 32 = 48, but the 32 px over-round ceiling clamps it to 32 — so a large padding doesn't balloon the corner. Frame + CTA hold the 16 px seed; the CTA re-asserts it since the default text-button tier is now 12. Light / dark shot swaps with the theme.

Cards
Feature cards
Icon + title + description tiles for marketing feature grids. Connected — first/last get rounded outer corners; hover highlights the border. Compat-aliased to .tile (see #tile-primitive).

Feature cards .feature-card

Icon + title + description tiles for feature grids. Connected — first/last get rounded outer corners; hover highlights the border.

Best Price Execution

Real-time routing across bridges, DEXs, and solvers guarantees the best rate on every trade.

Audited & Battle-Tested

Multiple independent audits, a $2M+ bug bounty, and 100M+ transfers processed.

Save 100s of Dev Hours

One SDK replaces five separate bridge and DEX integrations. All handled.

Always-On Monitoring

Live dashboards, webhook alerts, and full transaction observability from quote to confirmation.

Cards
Connect / identity row
An identity-led row — avatar, name over wallet address, and a trailing balance or action — built from the universal .list-item + .list--cards primitives. Not a new card type: the same primitive renders the wallet-connect picker rows, the connected-status footer card, and the modal's provider / ecosystem rows. (Replaced the retired bespoke .persona-connect-card, June 2026.)

Connect / identity row .list-item + .list--cards

Avatar + name / address + a trailing balance or action. A list-item composition, not a bespoke card.

Picker rows · .list-item · .list--cards

Kim — Shrimp
Kim 0x4a3d…2f1b
$412.50
Finn — Fish
Finn 0x8c91…7a02
$18,640
Dale — Whale
Dale 0x2e76…91da
$3,284,000
Mabel — Stablecoin Maxi
Mabel 0xb019…55c4
$248,300

Whole row is the tap target. The selected row (Dale) carries .is-selected → a 1 px accent rim + soft fill. All rows use the uniform brand-primary tone (the .list--cards default); --list-card-tone stays available to tone-code other selection lists.


Connected status · default .list-item · trailing action

Vilen — Hodler
Vilen 0x7C3B…c4B6

The connected-status footer card: a single-item .list--cards, an avatar tandem (portrait + chain badge) in the lead, and an inline disconnect button in the trail.

Cards
Specialised cards
Named consumers of the .card recipe with their own anatomy. Currently: .brand-card (composite protocol / brand profile), .card-avatar (avatar quick-pick tile — chains, tokens, brands, personas, wallets). Add a new specialised variant here when its anatomy is non-trivially different from the base; consider promotion to its own section once it grows multiple sub-variants. Tile-shaped layouts (standalone, connected-grid) live in the Tiles group.

Brand cards .brand-card

Composite card for protocol / brand profiles. Eyebrow + title link, hex chips, narrative + inline source, brand-coloured selected state.

Card avatar .card-avatar

Mini-card pairing an avatar (chain, token, brand, persona) with the canonical UI card recipe. Fills its .card-avatar-grid column — square when packed, a centered-mark rectangle when few tiles stretch to fill the row.

Small · .card-avatar--sm (48 px tile / 32 px avatar)

Default (56 px tile / 40 px avatar)

Large · .card-avatar--lg (64 px tile / 48 px avatar)


Rest

Selected · .is-active

All networks · .card-avatar--cluster — sm 48 · default 56 · lg 64

Overflow · .card-avatar--more


Composition — .card-avatar-grid · 2-row wrap with overflow

Leading .card-avatar--cluster tile (active when no chain filter is set), followed by the first N chains in canonical chains.json order, terminated by a .card-avatar--more overflow tile that opens the full chain-list drill via setCardState(card, 'chain-picker').

Fill — a customised 3-chain config stretches to fill

3–5 tiles below one row's capacity stretch into wide centered-mark rectangles (the per-tile width stays under --card-avatar-cell-max, so the cap doesn't bind). Cluster included. Same fixed height; the --gap-card-sm gap is unchanged.

Cap — 1–2 tiles center instead of ballooning

When a row holds only 1–2 tiles, each caps at --card-avatar-cell-max (120 px) and the row centers — a lone mark never stretches across the full panel. Override --card-avatar-cell-max: 1fr to opt back into unbounded fill.

Cards
Stat cards
Single-KPI tiles — eyebrow, value, unit, footer. Compose with sparkline, delta, leading icon, prior-period chip, or supporting description. The marketing-surface counterpart, .stat-tile, lives in the Tiles group.

Stat .stat-card

Single-KPI tile — eyebrow, value, unit, footer. Composes with sparkline, delta, leading icon, prior-period chip, or supporting description.

Default tile variants · sizes · tones · composition
Adoption
84%
Components
214
Tokens
1,482
A11y score
94/100
Brand presets
12
Open issues
12
With prior-period comparison · .stat-card-prev
Active wallets
71,897
With description · .stat-card-desc
Avg quote latency
214ms

Median across the last 10k routes — well under the 500ms ceiling for sub-second checkout.

Brand-icon row · .stat-card--row + .spot-icon-xl
Total wallets
71,897 122
Avg quote success
58.16% 5.4%
Avg click-through
24.57% 3.2%
Components
Theme Card
Selectable card for picking a saved brand theme. Renders the theme's swatches, its name, and a description. The primary action is selection — clicking anywhere on the card applies the theme. The secondary action is drill-down editing — a pencil button dispatches a theme-card-edit event; the consumer is expected to open a dedicated editor (name, description, and seed controls all live there — never inline on the card). Ships in two layout variants (--row, --sample) that share one anatomy, behaviour, and API.

Theme card .theme-card

Selectable card for picking a saved brand theme on Marketing surfaces (Theme Composer, theme picker modals). Two variants — row + sample.

Compact row .theme-card--row

LI.FI 1.0 Sapphire · Pink · Ink
Axelar Orange · Blue

Full sample .theme-card--sample

Cards
Wallet mount
Product-surface state wrapper that toggles between a "Connect wallet" CTA and a compact status card showing the active persona. Hosts both affordances in one mount slot so consumers (playground rail, Portal footer, future widget headers) don't re-wire the modal trigger. The connected status card is a connect / identity row — the universal .list-item + .list--cards composition, not a bespoke class.

Wallet mount & status card .ui-wallet-mount · .list-item

Two states: a connect CTA when disconnected; connected, a status card showing the identity pair (portrait + chain badge).

Empty — no persona active

Connected — persona active

Dale — Whale
Dale 0x2e76…91da

Connected — console connect, no provider

Dale — Whale
Dale 0x2e76…91da
Cards
Wallet connect flow
The playground's multi-step connect wizard — persona → provider → ecosystem → connecting — all inside one modal. A composition over existing primitives (.modal, .screen-header, .list-item in .list--cards, the .avatar-tandem identity pair, .spinner) with no class of its own. “Multichain” providers add the ecosystem step, which re-scopes the picker.

Wallet connect flow

Multi-step connect wizard: persona → provider → ecosystem → connecting. Composed from modal, wallet cards, and .list--cards.

The flow is live in playground.html — click “Connect wallet” in the rail and walk the steps. The demos below are static silhouettes of each step, built from the real classes.

Step 1 · Persona — .list-item · .list--cards

Dale — Whale
Dale 0x5274…6d32
$2,469,023

Step 2 · Provider — .list--cards

  • MetaMask
    Multichain
  • WalletConnect
    QR Code

Step 3 · Ecosystem — .state-block + .list--cards

MetaMask supports multiple chain ecosystems. Select which one to connect to.

  • Ethereum
  • Solana
  • Tron

Step 4 · Connecting — .state-block

Waiting for MetaMask

Click connect in your wallet popup. Don’t see your wallet? Check your other browser windows.

On completion the modal dismisses and the rail shows the connected identity pair (portrait + chain badge).

Tiles
Tiles — marketing splash surfaces

Tiles are the marketing-side card family — bigger padding, optional accent fill, splash-scale typography. Used on public pages, hero sections, brand surfaces, and decks.

  • Audience-led naming. .tile = marketing splash surfaces · .card = product UI surfaces. Same family, two nouns picked by where it ships.
  • Shared chrome with cards. --surface-raised fill, --elev-1 shadow, 4 % currentColor hairline. Hover bumps to --elev-2.
  • Diverge on splash. Larger padding/radius, splash-scale type, optional accent-tinted fill via data-accent. Materials add glass/gradient/halo treatments.
  • Spec. design/components/foundations.md → Tile vs Card — audience-led naming.
Tiles
Brand tiles
Marketing-grade tile with a saturated solid fill, one hero text element, and an optional attribution slot. Fifteen fills — three brand (primary / secondary / tertiary) plus twelve spectrum hues. The bright-tier complement to .content-tile's quiet paper / glass / halo materials.

Brand tile .brand-tile

Saturated marketing-moment tile. Four sizes on the same 4 px ladder as .content-tile; default fill is primary.

Sizes — sm (160 px) · default (240 px) · lg (320 px) · xl keynote (480 px)

Routes settle in 2.4s

Median across 40+ chains

.brand-tile--sm · 160 px

"One quote, every chain — the router does the rest."

MetaMask

Integrated 2023

.brand-tile · default 240 px

"The Widget SDK cut our integration from six weeks to a weekend."

Phantom

Cross-chain swap launch

.brand-tile--lg · 320 px

Q3 Recap

The fastest route, every chain.

14M routes settled. 99.97% delivery rate. 3.2-second median execution.

.brand-tile--xl · keynote 480 px

Slot compositions — minimal · chip · metric · bullets · ruled · multi-line

Built for cross-chain.

Minimal — quote only
New

Solana & Bitcoin live

Native bridging on both chains

Chip + quote + meta
$80B+

Routed cross-chain since 2021

Metric display + meta

Widget SDK ships with

  • 40+ chains, 30+ bridges
  • MEV protection on every route
  • Theming via design tokens
Heading + bullets

"We picked LI.FI because the router just works."

Coinbase Wallet

Partner since 2022

Quote + ruled attribution

"The fastest cross-chain UX we've shipped. Our power users felt it on day one."

Series B partner · Q2 2026

Multi-line quote + meta

Fills — data-fill

Fifteen solid fills: three brand (palette-driven) then twelve spectrum hues (palette-stable). Foreground auto-derives from bg lightness.

Brand — primary · secondary · tertiary

Three brand fills consume --accent-* tokens, so they ride the Theme Composer — swapping presets recolours every tile. Foreground auto-flips via oklch(from …) when the bg's lightness crosses 0.65.

"Best router in Web3."

— The Block, Q1 2026

primary

Now on 40+ chains

Bitcoin & Solana joined March 2026

secondary

$80B+ routed

Lifetime cross-chain volume

tertiary

Spectrum — coral · orange · amber · lime · green · teal

Twelve spectrum fills consume --spectral-1..12. Palette-stable by design — marketing collateral holds its hue across preset switches. Listed warm → cool around the colour wheel.

"Felt magical."

Rabby, on cross-chain swaps

coral

High-energy launch

Product reveal · Q2 2026

orange

$32B on Ethereum

24h volume, March 2026

amber

"Wallets love it."

30+ wallets via SDK

lime

99.97% delivery

Across all 40+ chains

green

14M routes settled

Lifetime, cross-chain

teal

Spectrum — cyan · sky · blue · violet · magenta · pink

3.2s median

Quote to settlement, p50

cyan

Drop-in widget

npm install @lifi/widget

sky

"Cross-chain, solved."

Annual report · 2026

blue

Editorial spotlight

Long-form case studies

violet

Solana & Bitcoin live

Native bridging, no wrapped tokens

magenta

One quote, every chain

40+ chains · 30+ bridges

pink

Composition recipes

Six common marketing shapes — testimonial, partner, anchor, stat, launch, keynote. Each pairs a fill with a slot combination.

Six shapes — testimonial · partner · anchor · stat · launch · keynote

"The Widget SDK cut our integration from six weeks to a weekend."

Phantom

Cross-chain swap launch

Testimonial — quote + ruled attribution

Powering swaps in 30+ wallets

Including MetaMask, Phantom, Rabby, and Coinbase Wallet.

Partner spotlight — claim + avatar stack

One quote, every chain.

40+ chains · 30+ bridges · 1,000+ tokens

Section anchor — claim + stats line

Lifetime Volume

$80B+

Routed cross-chain since 2021

Across Jumper, the Widget SDK, and direct API integrations

Stat hero — eyebrow + metric + label
Live now

Solana & Bitcoin go native

Native bridging on both chains — no wrapped tokens, no extra steps.

Solana · Bitcoin

Cross-chain routing live

Announcement — chip + claim + attribution

Q3 Recap

The fastest route, every chain.

14M routes settled. 99.97% delivery rate. 3.2-second median execution.

Keynote opener — eyebrow + display quote + meta · .brand-tile--xl
Tiles
Content tiles
Flexible-content tile primitive — eyebrow, chip, title, paragraph, bullets, and small/big numbers in any order. Four sizes, four materials, brand + spectrum accents. The marketing-and-deck counterpart to the strict .stat-tile.

Content tile .content-tile

Container for rich-content compositions. Four sizes ladder on the 4 px grid; default material is paper for prose legibility.

Quote · sm

Best route in 2.4s

Median across 40+ chains

Liquidity · default

Live

Cross-chain routing

One quote across 40+ chains and 30+ bridges. The router picks the cheapest path automatically.

Updated 24h ago

Widget SDK · lg

Drop-in swap widget

Embed full cross-chain swaps in any web app — chain list, token list, route picker, settlement all included.

  • 40+ chains, 30+ bridges, 1,000+ tokens
  • MEV protection on every route
  • Custom theming via design tokens

npm install @lifi/widget

Lifetime Volume · xl

$80B+ Routed cross-chain since 2021

Across Jumper, the Widget, and direct API integrations — settled through 40+ chains and counting.

Materials — data-material

Same content through the four canonical materials. Default is paper; gradient is the section spotlight (max one per section).

Paper

14M routes settled

Across Jumper, the Widget SDK, and direct API integrations.

Q3 2026

Glass

14M routes settled

Across Jumper, the Widget SDK, and direct API integrations.

Q3 2026

Gradient

14M routes settled

Across Jumper, the Widget SDK, and direct API integrations.

Q3 2026

Halo

14M routes settled

Across Jumper, the Widget SDK, and direct API integrations.

Q3 2026

Accents — data-accent

Brand accents ride the Theme Composer; spectrum bands stay stable across switches for categorical variety.

Primary · brand

Routing

Cross-chain quotes across 40+ chains.

Secondary · brand

Settlement

Median 3.2-second execution.

Tertiary · brand

Reliability

99.97% delivery rate.

Ethereum · spectral-3

$32B

24h volume

Arbitrum · spectral-6

$8.4B

24h volume

Optimism · spectral-9

$4.1B

24h volume

Base · spectral-12

$2.8B

24h volume

Composition recipes

Four common content shapes — pick by content density. Mix slot helpers with existing primitives like .chip and .metric-display.

MEV Protection

On by default

Private mempool routing

Every quote routes through a private mempool by default — no sandwich attacks, no front-running, no extra config.

Available on all 40+ chains

Median Execution

3.2s From quote to settlement

Across cross-chain swaps on the LI.FI router, p50 over the last 30 days.

What's included

Widget SDK ships with

  • 40+ chains, 30+ bridges, 1,000+ tokens
  • MEV protection on every route
  • Custom theming via design tokens
  • Wallet connect, in-app history, status tracking

npm install @lifi/widget

Q3 Recap

The fastest route, every chain

14M routes settled. 99.97% delivery rate. 3.2-second median execution.

Tiles
Identity tiles
Identity tiles for any subject — persona pickers, contributor cards, partner POCs, customer profiles. Three layouts on one class family: .identity-tile renders a compact head + bio + metric foot suited to 3-up / 4-up grids; .identity-tile--detail expands the head with a subtitle + action slot and swaps the body for a 2-col grid of named metadata blocks — the single hero subject view; .identity-tile--org is the person-led organization card — a leader portrait/monogram + company-logo badge (the .avatar-tandem) with a headline-volume foot. All three extend .tile, so per-tile brand accents flow through --tile-accent-light / --tile-accent-dark exactly the way .brand-card does.

Identity tile .identity-tile

Avatar + role + name with optional bio, metric, named blocks, and tag set. Compact form for grids; --detail for a single hero subject view; --org for a person-led organization card.

Compact · .identity-tile

Shrimp Kim — Shrimp persona portrait

Kim

First-time DeFi user, mostly stables. Bridged her first $500 from Coinbase to Base last month.

Net worth $655
Whale Dale — Whale persona portrait

Dale

ETH maxi, cold-storage discipline. Most of his stack hasn't moved in years.

Net worth $2,469,023
Stablecoin Maxi Mabel — Stablecoin Maxi persona portrait

Mabel

Operations-minded — keeps cash on multiple chains, fluent with stablecoin rails.

Net worth $679,764

Detail · .identity-tile--detail

D

Whale

Dale

ETH maxi, cold-storage discipline

Backstory

Dale bought ETH at $200 and never sold. He runs strict cold-storage discipline — most of his stack hasn't moved in years. The portion he does deploy goes to Lido and Aave for yield without giving up custody.

long-term holder cold-storage low-frequency ETH maxi

Wallets · 1

1 EVM wallet on a cold-storage signer. Demo stories: large-amount UX, institutional bridge confirmation patterns, single-wallet portfolio rebalances.

Organization · .identity-tile--org

Enterprise

Ledger

Pascal Gauthier · Chairman & CEO

The flagship hardware-wallet partner. Ledger Live routes Swap, Bridge, and Buy through LI.FI across millions of devices.

Volume $125M
Growth

Rabby Wallet

Tang Hongbo · Co-founder & CEO, DeBank

A growing self-custody wallet by DeBank. Rabby embeds LI.FI for in-wallet swap and bridge across desktop, mobile, and the extension.

Volume $19M
Solo

VilenDesign

Vilen · Founder

A solo founder shipping an experimental cross-chain widget — no brand assets yet, so the company badge is omitted and identity leads with the founder.

Volume $38K
Tiles
Stat tiles
Single-metric tile primitive — number + label, with three sizes and an optional .stat-bar layout helper. The UI counterpart, .stat-card, lives in the Cards group.

Stat tile .stat-tile

Single metric tile — number + label. Three sizes; compose freely or drop into .stat-bar for the standard responsive grid.

The tile is the unit. Each .stat-tile is a self-contained metric card with its own chrome, hover, and active states — drop one anywhere a number matters. Compose into any flex or grid wrapper the consumer authors.

$80B+ Total Transfer Volume
$80B+ Total Transfer Volume
$80B+ Total Transfer Volume
Tiles
Connected tile grid
A layout pattern for tiles that should read as one strip rather than a row of independent cards. Edge-to-edge with a 1 px hairline between tiles; first / last children auto-round their outer corners; hover highlights the shared border. Use when listing peer options that are conceptually equal — integration paths, product entry points, infrastructure tiers — where the visual unity reinforces "pick one of these." For independent cards with breathing room between them, reach for #tile-primitive instead.

Connected grid .integration-cards · .integration-card

Tiles in a hairline-gapped grid that reads as one strip. Compat-aliased; new code can use the same classes.

Aggregation engine

Best-price routing across 30+ bridges and DEXes, normalised to a single quote.

SDK & API

TypeScript SDK and REST API. Full control over the route, fee, and slippage envelope.

Agent surface

MCP server, llms.txt, and OpenAPI schema. Autonomous-agent friendly by default.

Tiles
Tile primitive
The canonical icon + title + body primitive. Use .tile everywhere a block needs to surface one idea with an icon — landing-page feature grids, enterprise trust sections, in-UI dashboards, deck slides, marketing sheets. Modifiers cover layout (flush / horizontal), size (sm / lg), and accent tint; parts slot in optional tag, cta, and meta. Every existing .feature-card and .enterprise-card in the codebase is compat-aliased to .tile — no HTML migration required.

Tile .tile

Icon + title + body primitive for marketing feature grids, enterprise sections, dashboards, decks. Compat-aliased from .feature-card / .enterprise-card.

Default — .tile in .tile-grid

Best Price Execution
Real-time routing across bridges, DEXs, and solvers guarantees the best rate on every trade.
Audited & Battle-Tested
Multiple independent audits, a $2M+ bug bounty, and 100M+ transfers processed.
Always-On Monitoring
Live dashboards, webhook alerts, and full transaction observability.

Sizes — --sm · default · --lg

Small
Compact — dashboards, dense UI.
Default
Canonical — landing pages.
Large
Generous — hero highlights, deck-style headline slots.
Data Visualization
Area Chart
Time-series filled below the curve. .area-chart shares the same axis + grid grammar as the line chart but adds a soft area fill — typically with the url(#dsAreaGrad-N) gradient — to communicate volume. Use stacked variants for share-of-total.

Area chart .area-chart

Filled time-series. Stacked, gradient, and interactive variants — same axis grammar as the line chart.

Primary kit 68% Layout 22%
Data Visualization
Bar chart
Ranked categorical comparison — one vertical bar per category, identity per row (avatar + label), value above. Composes the SVG .bars primitive, the .chart-axis-title rotated Y-axis label, the canonical .avatar primitive (inside a tilted <foreignObject> per bar), and the .series-1..12 categorical palette into a single wrapper anchored to the 4-grid spacing ladder. Four orthogonal variant axes layer on top of the base: palette mode (--mono / --sequential / --diverging), identity mode (--no-labels / --no-avatars), density tier (--compact / default / --hero), and bar shape (--bar-radius slot). Highlight a single bar via .bars--active + .is-active; add a horizontal reference via the composable .chart-threshold atom. Use for "top N by metric" patterns — DEX volumes, bridge throughput, protocol TVLs, portfolio rankings, benchmark deltas.

Bar chart .bar-chart

Ranked categorical bars with per-row identity (avatar + label). Composes the SVG .bars primitive with rotated .chart-x-identity units containing the canonical .avatar primitive.

Twelve LI.FI bridges ranked by 30-day volume. Each x-axis identity is a rotated <g class="chart-x-identity"> wrapping a <foreignObject> with an HTML flex row — <img class="avatar avatar--xs avatar--circle"> leads on the left, gap, then <span class="chart-x-identity__label"> right-aligned to the bar's pivot. The whole unit tilts as one piece so the eye reads each "bar → identity" pair without re-locating.


Palette · .bar-chart--mono · --sequential · --diverging

Default — .series-1..6 categorical fill. Color carries identity, not rank.

Mono — all bars one hue (--bar-chart-mono-tone, defaults to --accent-primary). Use when categories are about identity, not ranking.

Sequential — --seq-9..1 ramp. High-rank bars get the most saturated blue; color reinforces ranking.

Diverging — --div-* Pink↔Slate↔Green palette. Bars above the mean color green, below color pink. Consumer orders bars by signed magnitude.


Highlight + threshold · .bars--active · .chart-threshold

Stargate is emphasised via .is-active on its <g class="series-2"> and matching <g class="chart-x-identity">; siblings dim to 0.45 opacity. The dashed line is .chart-threshold — a reusable atom that pairs with any .bars consumer, with its label right-anchored at the plot's right edge.


Avatars-only · .bar-chart--avatars-only + per-bar .chart-x-identity--horizontal

Avatars-only — un-rotated chain marks sit directly under each bar. Wrapper carries --avatars-only (hides labels, tightens aspect to 800 / 320); each identity carries --horizontal (no rotate; tight foreignObject anchored to the bar's center x). Works best with recognizable marks — chains, tokens, well-known bridges.


No avatars · .bar-chart--no-avatars

No avatars — flat text labels sit under each bar via .bar-category-label, no rotated identity. The wrapper tightens aspect to 800 / 320 (no diagonal-label area below the baseline). This is the pattern the dashboard's commit-cadence charts render live through the chartFrame binder; reach for it for time-series or short-categorical bars where text labels suffice. Plot frame: 48-unit y-gutter · 12-unit right margin · baseline at 272 · category labels at baseline + 24 · value labels at bar-top − 8, every anchor on the 4-grid.


Identity order · default · .bar-chart--label-first

Default — avatar source-order FIRST, label SECOND. After right-pack + -45° rotation, the label ends at the bar's pivot and the avatar trails down-left. Reach for this when the LABEL is the data point's name and the avatar is supporting identity.

.bar-chart--label-first — flips visual order to [label][gap][avatar] via CSS order: -1 on the .chart-x-identity__label. A companion transform: translateX(50%) centers the avatar's geometric middle on the bar's center x (otherwise justify-end would put the avatar's right edge at the pivot, leaving its center half-a-width to the left). Markup is unchanged — the same <img>-first source order is preserved; only the flex visual order flips. flex-direction stays row so justify-content: flex-end still packs the cluster to the rotation pivot.


Density · .bar-chart--compact · default · --hero

Compact — aspect 800 / 280, gap --space-8, label --text-micro. For sidebar slots and dashboard tiles.

Hero — aspect 800 / 480, gap --space-16, label --text-sm, .avatar--sm identities. For marketing decks and single-stage compositions.

Data Visualization
Gauge
Semi-circular utilization meter. .gauge renders a 180-degree arc that fills 0–100% — the fastest read for "are we above the threshold?". Tone classes flip the fill color at green / warn / danger thresholds.

Gauge .gauge

Semi-circle utilization meter. Tone variants for success, warn, danger, and brand-gradient — center label and caption.

Success · 94
94
tokens pass
Brand gradient · 72
72
components pass
Danger · 38
38
with reduced-motion
Data Visualization
Heatmap
Density grid for two-dimensional categorical data — typically time-on-axis × intensity. .heatmap arranges .heatmap-cell children in a CSS grid; cell color comes from the data-intensity attribute (0–4) and the variant's gradient ramp.

Heatmap .heatmap

Intensity grid driven by data-intensity="0..4" per cell. Brand-gradient and primary-gradient ramps available.

Less More
Data Visualization
Legend
Series key for any multi-series chart. .chart-legend lays out a horizontal or stacked list of swatch + label + optional value chips. Pair with line, area, bar, donut, or stacked-rings — the legend is what turns colored pixels into data.

Chart legend .chart-legend

Swatch + label + optional value rows. Horizontal default, stacked variant, line-style swatches for line charts.

Horizontal · square swatch
Primary kit 68% Layout 22% Data-viz 10%
Horizontal · line swatch (line chart)
Data-viz Forms Navigation
Stacked · with values
styles.css94% design.md spec81% catalog preview73% a11y audit66%
Brand accent · primary · secondary · tertiary
Primary 48% Secondary 31% Tertiary 21%
Data Visualization
Line Chart
Continuous time-series with one or more series. .line-chart renders an SVG path per series, optionally with dots, axes, grid lines, and an interactive crosshair. Stroke color comes from --series-N applied to the path.

Line chart .line-chart

Multi-series time-series with optional dots, axes, grid, and interactive focus crosshair.

Data-viz Forms Navigation
Data Visualization
Progress
"How complete is X" — two primitives, one mental model. .progress renders a 0–100 value as a horizontal fill; .progress-ring renders the same value as a circular arc. Both share the same --value input, the same semantic tones, and the same sm / default / lg / xl size ladder. For multi-step navigation (where am I in a flow), see Steps in the Navigation group.

Linear

Progress bar .progress

Linear 0–100 fill. Four heights (sm / default / lg / xl), three semantic tones, plus a segmented variant for stacked categorical breakdowns.

Contrast — text on surface98%
Focus ring visibility86%
ARIA labels present71%
prefers-reduced-motion38%
Sync in progress — info52%
Adoption by surface — segmented11 of 13 pages

Inline progress .progress-inline

Bar on the left, value on the right — the dense one-row meter for table cells, list rows, and compact dashboards.

sm · 4px bar · 12px value

24%

default · 8px bar · 14px value

60%

lg · 12px bar · 16px value

78%

xl · 16px bar · 18px value

92%

Tones · default / success / warn / danger / info

37%
100%
71%
18%
52%

Ring

Progress ring .progress-ring

Circular sibling of the linear bar. Four sizes, three semantic tones, optional center label slot — same --value input, same brand-aware tones.

Sizes — sm · default · lg · xl, with semantic tones

24%

.progress-ring--sm · 48 px

68%

.progress-ring · 80 px

52%

.progress-ring--lg.progress-ring--warn · 96 px

92%

.progress-ring--xl.progress-ring--success · 128 px

Center label slot — text, glyph, or anything composable

4 / 5
Steps complete
2.4 ETH
Bridged 24h
Route settled
Bare ring

Inline progress ring .progress-ring-inline

Ring left, value right — the circular sibling of .progress-inline. Content-width (no flex-fill), so it hugs its content and the column stays narrow. Pairs a small ring with a value tier on the same ladder as the inline bar.

Sizes — ring Ø steps +4 on the 4-grid; the value ladder matches .progress-inline

.progress-ring-inline · 16 px ring · 12 px value

60%

.progress-ring-inline--lg · 24 px ring · 16 px value

78%

.progress-ring-inline--xl · 28 px ring · 18 px value

92%

Tones — compose the modifier on the inner .progress-ring

18% 71% 100%
Data Visualization
Radial Chart
Circular progress for a single percentage, a multi-segment donut, or stacked activity rings. .radial-chart renders an SVG track + bar; the segmented variant rebuilds slices via data-dash-donut. Center label is optional.

Radial chart .radial-chart

Circular progress, donut, and stacked rings. Center label, segment slices, and gradient ring all driven by modifier classes.

Single ring
88% tokens
Segmented · donut
1,482 tokens
Stacked rings
214 comps

Brand emphasis · primary · secondary · tertiary

80% primary
--accent-primary
65% secondary
--accent-secondary
55% tertiary
--accent-tertiary
Data Visualization
Series Palette
Eight --series-1--series-8 aliases over the Spectrum palette, ordered for maximum categorical contrast between adjacent series. Chart code reads --series-N; the indirection layer means the categorical ordering can evolve in :root without touching every chart consumer. Stable across palette switches by design — chart data is data, not branding.

Series tokens --series-1--series-8

Eight categorical hues for multi-series charts. Reach for them in this order; never substitute raw accent tokens.

The eight --series-1--series-8 tokens are aliases — each points at a curated hue from the Spectrum palette, ordered to maximise adjacent-series contrast (blue → orange, green → magenta, …) rather than following the natural hue rotation. Chart code consumes --series-N so the categorical ordering can evolve in :root without touching every consumer. Tier 1 (brand accents) and Tier 3 (semantic tones) are documented in Colors; this section is just the categorical chart tier.

Blue
--series-1 → var(--spectral-9)
Orange
--series-2 → var(--spectral-2)
Green
--series-3 → var(--spectral-5)
Magenta
--series-4 → var(--spectral-11)
Amber
--series-5 → var(--spectral-3)
Cyan
--series-6 → var(--spectral-7)
Violet
--series-7 → var(--spectral-10)
Coral
--series-8 → var(--spectral-1)
Data Visualization
Sparkline
A single-series trend strip — line + soft area fill, no axes, no labels. .sparkline sits inside a stat-card footer or beside a metric to answer "is this trending up or down?" without committing the space of a full chart.

Sparkline .sparkline

Tiny inline trend line in three sizes and three tones (pos / neg / neutral) — pairs with a stat-card footer.

Default · positive
Small · negative
Large · neutral
Data Visualization
Tooltip
Floating hover panel for chart data points. Composes a header label, a list of per-series rows (color indicator · series name · value), and an optional total footer. Five indicator variants — solid, dot, line, dashed, icon — match the chart family they pair with (bar, line, area, dashed reference, category legend). The --sm size tightens padding for dense legends; the --stack layout reshapes each row into a hero value over its label.

Chart tooltip .chart-tooltip

Floating hover panel for chart data points. Five indicator variants, two sizes, stack mode, optional total row.

Default · solid indicator

Jun 24 · Bridge volume
  • Across $9.8B
  • Stargate $4.2B
  • Mayan $1.6B

Compact .chart-tooltip--sm

Slippage · 0.05%
  • Quote 1.0024 ETH
  • Mid 1.0029 ETH

Stack mode .chart-tooltip--stack

  • ETH · USD spot $2,112.40

With total .chart-tooltip__item--total

Route — 3 hops
  • Gas $3.42
  • Bridge fee $0.84
  • Total cost $4.26

Indicator variants — solid (square · default) · --dot · --line · --dashed · --icon — pick the one that matches the chart-family stroke it pairs with.

default --dot --line --dashed --icon
App rail
The app-shell's fixed left navigation rail — a glass .panel holding brand · search · org switcher · nav · account. Add the hover-expand mini variant (the .app-rail--mini modifier, shared in styles.css) and it becomes a 72px collapsed icon column that expands to the full 320px rail on hover or keyboard focus, overlaying the canvas rather than reflowing it. Zero live consumers as of June 2026 — kept deliberately as the documented hover-expand-overlay recipe; the live Portal rail collapses via its own click-toggle push model (portal.css → "Collapsed (mini) rail"). Desktop-only — below 900px the rail slides fully offscreen instead. Full spec + the reusable expand/collapse motion rules live in design/components/navigation.md → App rail.

Hover-expand mini rail .app-rail--mini

72px icon column that expands to the 320px rail on hover or focus. Zero live consumers — kept as the documented hover-expand recipe.

One rail, two widths — shown side by side so you can compare both states at once (a static recreation — no live consumer ships the hover-expand today). They're built from the same elements at the same sizes, with each collapsed mark on the same row as its expanded counterpart. Read across each row: that's what animates — the width grows, the mark holds its Y, the label / wordmark / card chrome reveals.

Collapsed · 72px
Expanded · 320px

Same elements, same sizes, same rows — only the width and the label visibility change. Brand glyph → glyph + wordmark · search icon → search field · org avatar → org card · nav icons → nav rows · account avatar → account card. On a live consumer it's one rail: width animates 72↔320, each mark holds its Y, and the labels / card chrome fade in after it opens. Zero live consumers today — the Portal moved to its own click-toggle push model (portal.css → "Collapsed (mini) rail"); this variant stays the documented hover-expand recipe.

Command Palette
A reusable centered overlay with search, optional scope filtering, and grouped results. Drop the CSS shell + a cmdPalette({…}) call into any page; supply your own index and on-select callback. The catalog itself is one consumer — your dashboard, marketing site, or product surface can be another.

Command palette .cmd-palette

⌘K / Ctrl+K opens a centered overlay with search and optional scope pills. Pair with the .search-trigger primitive for the click entry-point; the palette renders once near body.

Entry point — live trigger (default 46 px)

Click → open the live palette below. ⌘K / Ctrl+K opens it globally too. Trigger sizes, scope-active state, and chrome details are documented at #search-trigger.

Open state

Buttons

Open state with a sample query (btn) — substring matches highlighted via <mark>, results grouped by section, the highlighted row showing the affordance. The panel uses real component markup; click the trigger above or press ⌘K to interact with the live palette.

Navigation
Side nav
Universal vertical navigation primitive. The default mode is a free-standing column that lives in any container — a widget's chrome, a settings drawer, a dashboard's section rail. The --sticky modifier upgrades it into the catalog / dashboard's sticky-to-viewport + scroll-spy rail (which is what powers the rail on the left of this page). Promoted from .ds-doc-sidebar (tier-1 catalog-meta) to tier-2 universal in May 2026.

Default

Side nav .side-nav

Free-standing vertical column with groups, titled labels, and items leading with a Lucide icon. Drive the active state from your router.

Modifiers

Sticky .side-nav--sticky

Catalog / dashboard mode — sticks to viewport and collapses into a mobile accordion under 900px. The rail on the left runs this variant.

The rail on the left of this page is the live preview. Open in a wide viewport to see the sticky desktop column; narrow below 900px to see the accordion.

Comfortable .side-nav--lg

Sizing-ladder companion one tier up: 20px icons, body-size label, ~48px rows. For roomy product rails like the Portal.

Rail .side-nav--rail

Icon-only narrow column (~64px). Items are real .btn-icon.btn-borderless buttons paired with a .tooltip for the label on hover. The active item takes .is-active (accent fill).

Each item is a live .btn-icon.btn-borderless — hover for the neutral wash + the tooltip label; the active item carries the accent fill. Hover any icon to see its label.

Top bar .side-nav--topbar

Horizontal icon-tab bar — the same .btn-icon.btn-borderless items as the rail, laid out in a row with the family's .side-nav__label shown as a caption under each. Active item takes .is-active.

Sibling of the .side-nav--rail — same icon-button items, laid out as a horizontal bar with a visible caption under each. The active item carries the accent fill + accent caption. Surface-less: apply your own background / radius / border on the .side-nav element.

Composition

Nested children .side-nav__sub

Parent row uses .side-nav__item--parent + aria-expanded. Sub-list reveals on open; chevron rotates 180°.

Trailing badges .side-nav__trail

Optional slot after the label. Hosts a .badge, an unread count, a status dot, or any small trailing widget.

Pinned foot .side-nav__foot

Pinned bottom slot — pairs with .avatar for a user / team chip. Uses margin-top: auto to push to the bottom of a flex column.

Navigation
Steps
"Where am I in a flow" — eight variants for stepped navigation across wizards, checkout, onboarding, and cross-chain swap progress. All eight share one state API: every item carries data-step-state="complete | current | upcoming", so swapping variants is a wrapper-class change rather than a per-item rewrite. For single-metric completion (how full / how far through a value), see Progress in Data Visualization.

Inline

Simple .steps--simple

N horizontal bars + a "Step N of M" caption. Lowest-chrome variant — fits in a header strip or a checkout rail without taking real estate.

Cross-chain swap progress — 4 steps, 2 complete, 1 current, 1 upcoming

Bullets .steps--bullets

Horizontal row of dots connected by lines. No labels — pair with a heading or above-row legend that names each step.

Connected dots — items compose the .status-dot primitive

Bullets and text .steps--bullets-text

Vertical stack — each step has a bullet, title, and one-line description. The tallest variant; use when each step needs prose.

Cross-chain swap — vertical stack with descriptions

Circles .steps--circles

Numbered circles connected by lines. Complete steps swap the number for a checkmark; the current step holds its number in accent ink.

Numbered discs compose .spot-icon spot-icon--round — state drives the disc

Circles inline .steps--circles-inline

Numbered disc + inline label + hairline connector — the compact, labeled breadcrumb for multi-step modal and wizard headers.

Wizard header breadcrumb — 24 px disc + inline label per step

Circles with text .steps--circles-text

Top-edge accent rail with a "Step N" label and step title underneath. Reads well at full canvas width; common in checkout and onboarding.

Top-edge accent + label + title — four steps across the canvas

Panel

Panels .steps--panels

Boxed steps with a 4-px top accent edge per state. Highest-chrome inline variant — used when each step is itself a clickable destination.

Boxed steps — top accent edge per state, body has label + title + description

Panels with border .steps--panels-border

Vertical stack with a 4-px left accent edge per state. The natural narrow-viewport companion to .steps--panels — same body, different axis.

Vertical panels — 4-px left accent edge per state

Navigation
Tab nav
The quiet link/underline row used by the catalog's scope filter and dashboard sub-tabs. Three sizes mapping to the system convention. Page-level navigation (the sticky rail every DS sub-page wears) is documented separately at .tab-nav. The chapter strip that auto-renders at the top of long sections (.ds-doc-section-toc) is documented in the Authoring Guide at #section-toc, since you author chapter markers, not the strip.

Tab nav .ds-doc-tab-nav

Link / button row with an accent underline on the active item. Filter tabs, dashboard nav, in-card sub-views; three sizes.

Three sizes mapping to the system convention. The active item gets brand-accent text + a 2 px underline; hover lifts the underline to a quiet 30 % accent. Both .is-active and [aria-selected="true"] trigger the active visual.

Resources
FAB Menu
Fixed-position trigger button + frosted-glass menu panel for page-global tools. Two instances live on every page today: the design FAB (theme composer, on-page controls) at bottom-right, and the DS FAB (Design System entry point) 72px to its left on marketing pages. Both share the same sub-components documented below — swap the trigger icon and the menu content, keep the rest.

FAB menu — design tools + DS entry

Two FABs flanking the bottom-right of marketing pages: design tools (palette / on-page controls) + DS entry. Single FAB on DS surfaces.

Resources
Live Data
Attribute-driven injector that fills dashboard markup from a committed JSON snapshot. Drop a data-dash="key.path" on any element, the client walks the DOM on load and replaces its text / CSS var / donut segments / sparkline path / list rows with values resolved from the JSON. Used by design-system-dashboard.html; reuse on any future live surface.

Live data data-dash*

Attribute-driven data injector for product dashboards. Drop data-dash="key.path" on any element; client fills it from a committed JSON snapshot.

<!-- Single value --> <span data-dash="components.total">0</span> <!-- CSS custom property --> <div data-dash-var="value:percent; color:colorVar"></div> <!-- List + template --> <div class="bar-chart bar-chart--h" data-dash-list="components.topUsed"> <template data-dash-item> <div class="bar-row"> <span class="bar-label" data-dash="name"></span> <div class="bar-track"> <div class="bar-fill" data-dash-var="value:pct; color:colorVar"></div> </div> <span class="bar-value" data-dash="count"></span> </div> </template> </div>
Resources
Token ladder
Catalog-meta primitive for visualizing a token scale as a list of name · value · proportional bar rows. Used in Foundations cards (Spacing system, Surface widths, future radii / motion / opacity scales). Composes one column rhythm, one bar treatment, one custom-property API; consumers don't reinvent.

Token ladder .ds-doc-ladder

Stack of token rows (name · value · bar). Set --bar-extent per row to drive bar width — literal length or percentage.

A six-row ladder using the literal-length pattern — the bar is exactly as wide as the token's value, so the visual reads "this token IS this much space."

--space-88px
--space-1616px
--space-2424px
--space-4848px
--space-9696px
--space-160160px

Live consumers in the catalog: #spacing (Spacing system + Surface widths). Both compose this primitive; neither carries its own inline-styled grid.

Design feedback
Variant card
One option in a workshop variant comparison. Composes the canonical UI-card recipe (--surface-card + --elev-1 + accent rim on .is-active) plus a leading .marker anchor that telegraphs the option's position in the sequence (1 / 2 / 3 by default — variant cards are UI workshop tooling, so plain numbers fit the marker primitive's in-product default). The atom of the Variants Rail today; reusable in future Design Feedback surfaces.

Variant card .ds-doc-variant-card

Single-selection workshop card — marker + label + 1-2 line rationale. Click moves .is-active; consumer reads data-variant to swap the live target.

Three cards stacked, one selected. Each card carries a marker (left), a title + subtitle stack (middle), and a chevron toggle (right). Title + subtitle is the default compact view — enough for most workshop comparisons. The chevron expands an opt-in description paragraph when a designer wants more context without leaving the rail (the active card in this demo ships expanded). Selection signal: inactive markers in alpha tone (neutral ink wash), active marker in tinted tone (brand-accent wash); the accent rim and bg-mix complete the active state — title typography stays constant across states. Markers use plain numbers (1 / 2 / 3) — the in-product default per the marker primitive. Copy below is slot-labeled (Title / Subtitle / Description) so the demo doesn't drift from any specific workshop flow.

Title Subtitle — 1-line elaboration of the title.

Description — paragraph-length detail revealed when the chevron is expanded. Use it for context a workshop reviewer might want without leaving the rail: when to pick the option, what tradeoffs it makes, what the canonical case looks like.

Title Subtitle — 1-line elaboration of the title.

Description — paragraph-length detail revealed when the chevron is expanded. Use it for context a workshop reviewer might want without leaving the rail.

Title Subtitle — 1-line elaboration of the title.

Description — paragraph-length detail revealed when the chevron is expanded. Use it for context a workshop reviewer might want without leaving the rail.

Diagrams
Diagram Engine
The engine behind designed flow and system diagrams: author a JSON spec (nodes, edges, groups, annotations), render it through window.LifiDiagram, retint it with the Theme Composer, and export a standalone SVG for decks, proposals, PRDs, and PDF pipelines. Pure-SVG output with zero foreignObject, so exports survive Figma import and macOS PDFKit. It replaced the HTML .flow-* primitives at parity (June 2026); the canonical architecture spec lives in design/diagram-engine.md.

Open the Diagram Composer

Diagram engine LifiDiagram

Declarative JSON specs rendered as pure-SVG diagrams. Token-themed live, exported as standalone SVG for decks, proposals, and PDFs.

Rendered live by window.LifiDiagram from the JSON specs in this page's markup. The scenes paint var(--token) expressions, so they retint with the Theme Composer and the light/dark flip with no re-render. Download resolves every token to an sRGB literal and saves a self-contained SVG that captures the active theme.

System flow · cards + group + auto-routed edges

Network map · avatars + stat + status chip + explicit waypoints