# SwapWidget

> From/to with the flip. The signature component.

Source: https://lopifi.com/components/swap-widget
Part of: Lopifi — US GAAP books on autopilot, reviewed by a CPA (https://lopifi.com)

---

- **Family:** Swaps & FX — 
- **Status:** Implemented
- **Spec:** https://lopifi.com/docs/catalog/04-swaps-fx

## Install

```bash
npx shadcn@latest add @lopifi/theme
npx shadcn@latest add @lopifi/swap-widget
```

## Specification

> The signature component. From/to with the flip.

```
┌─ 24 ──────────────────────────────────┐
│  You send                              │  label
│  [1,000.00]              [USD ▾]       │  figure-lg · currency select
│  Balance $4,120.00              [Max]  │  caption
│                                        │
│         ⟨ ⇅ ⟩  ← flip button           │  centered, overlapping the divider
│                                        │
│  Recipient gets                        │
│  [918.42]                [EUR ▾]       │
│                                        │
│  1 USD = 0.91842 EUR · 12s  [locked]   │  RateLine
│  ├─ ConversionBreakdown ─────────────┤ │
└────────────────────────────────────────┘
```

### The flip
The one place `spring.playful` is used.

```
icon      rotateY? no — rotate(180deg), spring.playful { duration: 0.45, bounce: 0.22 }
fields    cross-translate: top ↕ bottom, transform only, same spring
values    swap INSTANTLY. The amounts do not interpolate. (Money Law 1)
```

The fields move; the numbers don't animate. That distinction is the whole doctrine in one component — motion explains *what happened structurally*, and never renders a false value.

Under reduced motion: instant swap, no rotation.

### Rules
- **Editing either field recomputes the other**, with a 140ms blur-masked crossfade on the computed value — never a count-up.
- **The computed field is visibly derived** — `--text-secondary` until it's the focused one.
- Debounce rate fetches at 300ms; **never show a stale computed value as current.** During refetch, dim the computed value to 0.6.
- Insufficient balance is an inline error under the send field, not a disabled button.
- Fee and rate are always visible without expanding anything.

## Rules this component obeys

Money is minor units, never floats. `null` is not `0` and neither is redacted. Negatives render in accounting parentheses with the parenthesis column reserved on both sides. Debit and credit are two columns. Per-currency decimal places. Polarity is explicit — up is not always good. Every figure states its period, entity, currency, basis and as-of.
