Skip to content

Instantly share code, notes, and snippets.

@danielsimao
Created April 1, 2026 08:42
Show Gist options
  • Select an option

  • Save danielsimao/57b9318572ca81af6a4edb301cbf3f6b to your computer and use it in GitHub Desktop.

Select an option

Save danielsimao/57b9318572ca81af6a4edb301cbf3f6b to your computer and use it in GitHub Desktop.
BOB Swap — Walletless UX Wireframes (3 concepts)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BOB Swap — Walletless UX Wireframes</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg: #09090b;
--surface: #18181b;
--input-bg: #1c1c20;
--border: #27272a;
--border-hover: #3f3f46;
--text: #fafafa;
--text-secondary: #a1a1aa;
--text-muted: #71717a;
--primary: #F35D00;
--primary-hover: #e05500;
--primary-dim: rgba(243, 93, 0, 0.12);
--ring: rgba(243, 93, 0, 0.4);
--green: #22c55e;
--destructive: #ef4444;
--radius: 12px;
--radius-lg: 16px;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background: #050505;
color: var(--text);
min-height: 100vh;
padding: 2rem 1rem;
}
.page-header {
text-align: center;
margin-bottom: 3rem;
}
.page-header h1 {
font-size: 1.75rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.page-header p {
color: var(--text-muted);
font-size: 0.9rem;
max-width: 600px;
margin: 0 auto;
}
.concepts {
display: flex;
gap: 2rem;
justify-content: center;
flex-wrap: wrap;
max-width: 1400px;
margin: 0 auto 3rem;
}
.concept-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.concept-label {
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--primary);
text-align: center;
}
.concept-sublabel {
font-size: 0.75rem;
color: var(--text-muted);
text-align: center;
max-width: 380px;
line-height: 1.4;
margin-top: 0.25rem;
}
/* ─── Swap Card (realistic mockup) ─── */
.swap-card {
width: 420px;
background: var(--bg);
border: 1px solid var(--border);
border-radius: 20px;
padding: 1.25rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.swap-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 0.25rem 0.5rem;
}
.swap-title {
font-size: 0.8rem;
font-weight: 600;
color: var(--text-muted);
}
.swap-settings {
display: flex;
gap: 0.5rem;
}
.settings-btn {
width: 28px;
height: 28px;
border-radius: 6px;
background: var(--surface);
border: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
font-size: 0.7rem;
cursor: default;
}
/* Token input */
.token-input {
background: var(--input-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 0.875rem 1rem;
position: relative;
}
.token-input:focus-within {
border-color: var(--border-hover);
}
.token-input-label {
font-size: 0.7rem;
font-weight: 500;
color: var(--text-muted);
margin-bottom: 0.375rem;
}
.token-input-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
}
.token-amount {
font-size: 1.5rem;
font-weight: 600;
color: var(--text);
background: none;
border: none;
outline: none;
width: 60%;
font-family: inherit;
}
.token-amount::placeholder { color: var(--text-muted); }
.token-amount.readonly { color: var(--text-secondary); }
.token-select {
display: flex;
align-items: center;
gap: 0.375rem;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 20px;
padding: 0.35rem 0.625rem 0.35rem 0.35rem;
cursor: default;
flex-shrink: 0;
}
.token-icon {
width: 22px;
height: 22px;
border-radius: 50%;
background: #f7931a;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
font-weight: 700;
color: white;
}
.token-icon.eth { background: #627eea; }
.token-icon.wbtc { background: linear-gradient(135deg, #f7931a, #e2820a); }
.token-name {
font-size: 0.8rem;
font-weight: 600;
}
.token-chevron {
font-size: 0.55rem;
color: var(--text-muted);
margin-left: 0.125rem;
}
.token-meta {
display: flex;
justify-content: space-between;
margin-top: 0.375rem;
font-size: 0.7rem;
color: var(--text-muted);
}
/* Switch button */
.switch-container {
display: flex;
justify-content: center;
margin: -0.375rem 0;
position: relative;
z-index: 1;
}
.switch-btn {
width: 32px;
height: 32px;
border-radius: 8px;
background: var(--surface);
border: 3px solid var(--bg);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
font-size: 0.8rem;
cursor: default;
}
/* Recipient card */
.recipient-card {
display: flex;
align-items: center;
gap: 0.5rem;
background: var(--input-bg);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 0.75rem 1rem;
cursor: pointer;
transition: border-color 0.15s;
font-size: 0.85rem;
}
.recipient-card:hover { border-color: var(--border-hover); }
.recipient-card .icon {
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.recipient-card .icon.wallet { background: var(--surface); }
.recipient-card .icon.wallet img {
width: 14px;
height: 14px;
border-radius: 50%;
}
.recipient-card .icon.currency {
color: var(--text-muted);
font-size: 0.75rem;
}
.recipient-card .icon.custom {
background: var(--primary-dim);
color: var(--primary);
font-size: 0.65rem;
}
.recipient-card .addr {
flex: 1;
color: var(--text);
font-size: 0.8rem;
}
.recipient-card .addr.placeholder {
color: var(--text-muted);
}
.recipient-card .edit {
color: var(--text-muted);
font-size: 0.7rem;
}
/* CTA button */
.cta {
width: 100%;
padding: 0.875rem;
border-radius: var(--radius);
font-size: 0.9rem;
font-weight: 600;
border: none;
cursor: default;
font-family: inherit;
transition: all 0.15s;
}
.cta-primary {
background: var(--primary);
color: white;
}
.cta-outline {
background: transparent;
color: var(--text);
border: 1px solid var(--border);
}
.cta-disabled {
background: var(--surface);
color: var(--text-muted);
border: 1px solid var(--border);
}
/* Secondary link */
.secondary-link {
text-align: center;
font-size: 0.75rem;
color: var(--text-muted);
padding: 0.375rem;
cursor: pointer;
}
.secondary-link span {
color: var(--primary);
text-decoration: underline;
text-underline-offset: 2px;
}
/* Tab toggle */
.tab-toggle {
display: flex;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 3px;
gap: 2px;
}
.tab-toggle .tab {
flex: 1;
padding: 0.4rem 0.75rem;
border-radius: 6px;
font-size: 0.75rem;
font-weight: 500;
text-align: center;
color: var(--text-muted);
cursor: default;
transition: all 0.15s;
}
.tab-toggle .tab.active {
background: var(--input-bg);
color: var(--text);
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
/* Recommendation section */
.rec-section {
max-width: 700px;
margin: 0 auto;
text-align: center;
padding: 2rem;
}
.rec-section h2 {
font-size: 1.25rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--primary);
}
.rec-section p {
color: var(--text-secondary);
font-size: 0.85rem;
line-height: 1.6;
margin-bottom: 0.75rem;
}
.rec-section strong { color: var(--text); }
/* State indicator */
.state-pill {
display: inline-block;
font-size: 0.6rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 0.2rem 0.5rem;
border-radius: 4px;
margin-bottom: 0.75rem;
}
.state-pill.default { background: var(--surface); color: var(--text-muted); border: 1px solid var(--border); }
.state-pill.active { background: var(--primary-dim); color: var(--primary); }
.state-pill.success { background: rgba(34,197,94,0.1); color: var(--green); }
.states-row {
display: flex;
gap: 1.5rem;
flex-wrap: wrap;
justify-content: center;
}
@media (max-width: 900px) {
.concepts { flex-direction: column; align-items: center; }
.swap-card { width: 100%; max-width: 420px; }
.states-row { flex-direction: column; align-items: center; }
}
</style>
</head>
<body>
<div class="page-header">
<h1>Walletless Swap UX</h1>
<p>Three approaches for making walletless swaps a clear, first-class path alongside wallet connection</p>
</div>
<!-- ═══════════════ CONCEPT A ═══════════════ -->
<div class="concept-wrapper" style="margin-bottom: 3rem;">
<div>
<div class="concept-label">Concept A — Dual-Path CTA</div>
<div class="concept-sublabel">Keep "Connect Wallet" as primary. Add a secondary link below for manual address entry.</div>
</div>
<div class="states-row">
<!-- State 1: No wallet -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill default">No wallet connected</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="cta cta-primary">Connect Wallet</div>
<div class="secondary-link">or <span>enter recipient address</span></div>
</div>
</div>
<!-- State 2: After entering address -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill active">Address entered</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="recipient-card">
<div class="icon custom"></div>
<span class="addr">To: 0xd8dA…6045</span>
<span class="edit"></span>
</div>
<div class="cta cta-primary">Review</div>
<div class="secondary-link">or <span>connect wallet instead</span></div>
</div>
</div>
</div>
</div>
<!-- ═══════════════ CONCEPT B ═══════════════ -->
<div class="concept-wrapper" style="margin-bottom: 3rem;">
<div>
<div class="concept-label">Concept B — Tab Toggle</div>
<div class="concept-sublabel">Segmented control above the CTA. User explicitly picks "Wallet" or "Manual" mode.</div>
</div>
<div class="states-row">
<!-- State 1: Wallet tab -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill default">Wallet tab (default)</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="tab-toggle">
<div class="tab active">🔗 Use Wallet</div>
<div class="tab">✏️ Enter Address</div>
</div>
<div class="cta cta-primary">Connect Wallet</div>
</div>
</div>
<!-- State 2: Manual tab -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill active">Manual tab selected</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="tab-toggle">
<div class="tab">🔗 Use Wallet</div>
<div class="tab active">✏️ Enter Address</div>
</div>
<div class="recipient-card">
<div class="icon currency"></div>
<span class="addr placeholder">Enter recipient address</span>
<span class="edit"></span>
</div>
<div class="cta cta-disabled">Enter Recipient</div>
</div>
</div>
<!-- State 3: Address entered -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill success">Ready to swap</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="tab-toggle">
<div class="tab">🔗 Use Wallet</div>
<div class="tab active">✏️ Enter Address</div>
</div>
<div class="recipient-card">
<div class="icon custom"></div>
<span class="addr">To: 0xd8dA…6045</span>
<span class="edit"></span>
</div>
<div class="cta cta-primary">Review</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════ CONCEPT C ═══════════════ -->
<div class="concept-wrapper" style="margin-bottom: 3rem;">
<div>
<div class="concept-label">Concept C — Smart CTA</div>
<div class="concept-sublabel">Single adaptive button. Without wallet: CTA opens the recipient modal directly. Wallet connection demoted to a link.</div>
</div>
<div class="states-row">
<!-- State 1: No wallet, no recipient -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill default">No wallet</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="cta cta-primary">Enter recipient to swap</div>
<div class="secondary-link">or <span>connect wallet</span></div>
</div>
</div>
<!-- State 2: Recipient entered -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill active">Address entered</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="recipient-card">
<div class="icon custom"></div>
<span class="addr">To: 0xd8dA…6045</span>
<span class="edit"></span>
</div>
<div class="cta cta-primary">Review</div>
<div class="secondary-link">or <span>connect wallet</span></div>
</div>
</div>
<!-- State 3: Wallet connected -->
<div style="display:flex;flex-direction:column;align-items:center;gap:0.5rem;">
<span class="state-pill success">Wallet connected</span>
<div class="swap-card">
<div class="swap-header">
<span class="swap-title">Swap</span>
<div class="swap-settings"><div class="settings-btn"></div></div>
</div>
<div class="token-input">
<div class="token-input-label">Sell</div>
<div class="token-input-row">
<span class="token-amount">0.01</span>
<div class="token-select">
<div class="token-icon"></div>
<span class="token-name">BTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$640.00</span><span>0.02 avail</span></div>
</div>
<div class="switch-container"><div class="switch-btn"></div></div>
<div class="token-input">
<div class="token-input-label">Buy</div>
<div class="token-input-row">
<span class="token-amount readonly">0.0098</span>
<div class="token-select">
<div class="token-icon wbtc">W</div>
<span class="token-name">WBTC</span>
<span class="token-chevron"></span>
</div>
</div>
<div class="token-meta"><span>$627.20</span></div>
</div>
<div class="recipient-card">
<div class="icon wallet" style="background:#f6851b;"><span style="font-size:0.5rem;color:white;">🦊</span></div>
<span class="addr">To: 0x1111…1111</span>
<span class="edit"></span>
</div>
<div class="cta cta-primary" style="box-shadow: 0 0 20px var(--ring);">Swap ⚡</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════ RECOMMENDATION ═══════════════ -->
<div class="rec-section">
<h2>Recommendation</h2>
<p><strong>Primary: Concept A (Dual-Path CTA)</strong> — Simplest change. "Connect Wallet" stays primary for the majority. The "or enter recipient address" link is discoverable without cluttering. Ships immediately with existing RecipientCard + Modal. No new components needed.</p>
<p><strong>Runner-up: Concept B</strong> — Best if user testing shows the text link gets missed. The tab makes both paths equally prominent.</p>
<p><strong>Validate first:</strong> Show the swap form to 5 users and ask "how would you swap without connecting a wallet?" If they find the link in A, ship it. If not, upgrade to B.</p>
<p style="margin-top:1.5rem;color:var(--text-muted);font-size:0.8rem;">Which concept would you like to develop further?</p>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment