537 lines
17 KiB
HTML
537 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<!--
|
||
================================================================================
|
||
TEMPL MIGRATION GUIDE: welcome.html → views/welcome.templ
|
||
================================================================================
|
||
|
||
PAGE OVERVIEW:
|
||
- Onboarding landing page with 3-step stepper (Welcome → Learn → Get Started)
|
||
- Routes users to register.html or login.html
|
||
- Network status indicator in footer
|
||
- VIEWPORT: Optimized for popup/webview (360×540 to 480×680)
|
||
|
||
VIEWPORT CONSTRAINTS (Auth Popup/Webview):
|
||
- Target sizes: 360×540 (small), 420×600 (medium), 480×680 (large)
|
||
- Card max-width: 48ch (~384px) fits all viewport sizes
|
||
- Padding scales down on smaller viewports via media queries
|
||
- Touch targets minimum 44×44px for mobile accessibility
|
||
- Footer should remain visible without scrolling on step 1/3
|
||
- Scrollable content area for step 2 (feature list)
|
||
|
||
MAIN TEMPL COMPONENT:
|
||
templ WelcomePage() {
|
||
@layouts.CenteredCard("Welcome - Sonr Motr Wallet") {
|
||
@OnboardingStepper(1)
|
||
@WelcomeStep1()
|
||
}
|
||
}
|
||
|
||
HTMX INTEGRATION:
|
||
- Replace onclick="goToStep(N)" with hx-get="/welcome/step/N" hx-target="#step-content"
|
||
- Stepper state managed server-side via URL params or session
|
||
- Navigation buttons trigger HTMX partial updates
|
||
|
||
STATE MANAGEMENT:
|
||
- Current step passed as prop: currentStep int
|
||
- Step completion status tracked server-side
|
||
================================================================================
|
||
-->
|
||
<html lang="en" class="wa-cloak">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Welcome - Sonr Motr Wallet</title>
|
||
<script src="https://cdn.sonr.org/wa/autoloader.js"></script>
|
||
<style>
|
||
:root {
|
||
--wa-color-primary: #17c2ff;
|
||
}
|
||
|
||
html, body {
|
||
min-height: 100%;
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
|
||
.main-centered {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 100%;
|
||
padding: var(--wa-space-l);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.main-centered wa-card {
|
||
width: 100%;
|
||
max-width: 48ch;
|
||
}
|
||
|
||
.step {
|
||
display: none;
|
||
}
|
||
|
||
.step.active {
|
||
display: block;
|
||
}
|
||
|
||
.onboarding-stepper {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: var(--wa-space-s);
|
||
padding: var(--wa-space-m) 0;
|
||
}
|
||
|
||
.stepper-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--wa-space-xs);
|
||
}
|
||
|
||
.stepper-number {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: var(--wa-font-size-s);
|
||
font-weight: 600;
|
||
background: var(--wa-color-neutral-200);
|
||
color: var(--wa-color-neutral-600);
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.stepper-item.active .stepper-number {
|
||
background: var(--wa-color-primary);
|
||
color: white;
|
||
box-shadow: 0 0 0 4px var(--wa-color-primary-subtle);
|
||
}
|
||
|
||
.stepper-item.completed .stepper-number {
|
||
background: var(--wa-color-success);
|
||
color: white;
|
||
}
|
||
|
||
.stepper-label {
|
||
font-size: var(--wa-font-size-xs);
|
||
color: var(--wa-color-neutral-500);
|
||
display: none;
|
||
}
|
||
|
||
@media (min-width: 480px) {
|
||
.stepper-label {
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
/* Viewport constraints for popup/webview (360-480px width) */
|
||
@media (max-width: 400px) {
|
||
.main-centered {
|
||
padding: var(--wa-space-m);
|
||
}
|
||
.main-centered wa-card {
|
||
max-width: 100%;
|
||
}
|
||
.hero-icon {
|
||
width: 64px;
|
||
height: 64px;
|
||
}
|
||
.hero-icon wa-icon {
|
||
font-size: 32px;
|
||
}
|
||
.feature-item {
|
||
padding: var(--wa-space-s);
|
||
}
|
||
.feature-icon {
|
||
width: 32px;
|
||
height: 32px;
|
||
}
|
||
.action-card {
|
||
padding: var(--wa-space-m);
|
||
}
|
||
.action-card wa-icon {
|
||
font-size: 2rem;
|
||
}
|
||
}
|
||
|
||
@media (max-height: 600px) {
|
||
.hero-icon {
|
||
width: 56px;
|
||
height: 56px;
|
||
margin-bottom: var(--wa-space-m);
|
||
}
|
||
.hero-icon wa-icon {
|
||
font-size: 28px;
|
||
}
|
||
.onboarding-stepper {
|
||
padding: var(--wa-space-s) 0;
|
||
}
|
||
}
|
||
|
||
.stepper-item.active .stepper-label {
|
||
color: var(--wa-color-primary);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.stepper-line {
|
||
width: 40px;
|
||
height: 2px;
|
||
background: var(--wa-color-neutral-200);
|
||
transition: background 0.3s;
|
||
}
|
||
|
||
.stepper-line.completed {
|
||
background: var(--wa-color-success);
|
||
}
|
||
|
||
.hero-icon {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: var(--wa-radius-l);
|
||
background: linear-gradient(135deg, var(--wa-color-primary), #0090ff);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto var(--wa-space-l);
|
||
box-shadow: 0 8px 32px rgba(23, 194, 255, 0.3);
|
||
}
|
||
|
||
.hero-icon wa-icon {
|
||
font-size: 40px;
|
||
color: white;
|
||
}
|
||
|
||
.feature-item {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--wa-space-m);
|
||
padding: var(--wa-space-m);
|
||
background: var(--wa-color-surface-alt);
|
||
border-radius: var(--wa-radius-m);
|
||
}
|
||
|
||
.feature-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: var(--wa-radius-s);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.feature-icon.security {
|
||
background: var(--wa-color-success-subtle);
|
||
color: var(--wa-color-success);
|
||
}
|
||
|
||
.feature-icon.speed {
|
||
background: var(--wa-color-primary-subtle);
|
||
color: var(--wa-color-primary);
|
||
}
|
||
|
||
.feature-icon.privacy {
|
||
background: var(--wa-color-warning-subtle);
|
||
color: var(--wa-color-warning);
|
||
}
|
||
|
||
.action-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: var(--wa-space-m);
|
||
padding: var(--wa-space-xl);
|
||
background: var(--wa-color-surface-alt);
|
||
border-radius: var(--wa-radius-l);
|
||
border: 2px solid transparent;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
text-align: center;
|
||
}
|
||
|
||
.action-card:hover {
|
||
border-color: var(--wa-color-primary);
|
||
background: var(--wa-color-primary-subtle);
|
||
}
|
||
|
||
.action-card wa-icon {
|
||
font-size: 2.5rem;
|
||
color: var(--wa-color-primary);
|
||
}
|
||
|
||
.network-status {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: var(--wa-space-xs);
|
||
}
|
||
|
||
.status-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--wa-color-success);
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; }
|
||
50% { opacity: 0.5; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<wa-page>
|
||
<main class="main-centered">
|
||
<wa-card>
|
||
<div slot="header">
|
||
<div class="onboarding-stepper">
|
||
<div class="stepper-item active" data-step="1">
|
||
<div class="stepper-number">1</div>
|
||
<span class="stepper-label">Welcome</span>
|
||
</div>
|
||
<div class="stepper-line"></div>
|
||
<div class="stepper-item" data-step="2">
|
||
<div class="stepper-number">2</div>
|
||
<span class="stepper-label">Learn</span>
|
||
</div>
|
||
<div class="stepper-line"></div>
|
||
<div class="stepper-item" data-step="3">
|
||
<div class="stepper-number">3</div>
|
||
<span class="stepper-label">Get Started</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="step active" data-step="1">
|
||
<div class="wa-stack wa-gap-l">
|
||
<div class="hero-icon">
|
||
<wa-icon name="wallet" family="duotone"></wa-icon>
|
||
</div>
|
||
|
||
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
||
<h1 class="wa-heading-xl">Welcome to Sonr</h1>
|
||
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
||
Your self-sovereign identity wallet powered by WebAssembly
|
||
</p>
|
||
</div>
|
||
|
||
<wa-divider></wa-divider>
|
||
|
||
<div class="wa-stack wa-gap-s">
|
||
<div class="feature-item">
|
||
<div class="feature-icon security">
|
||
<wa-icon name="shield-check"></wa-icon>
|
||
</div>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-xs">Passwordless Security</span>
|
||
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||
Authenticate with biometrics or hardware keys - no passwords to remember or steal
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="feature-item">
|
||
<div class="feature-icon speed">
|
||
<wa-icon name="bolt"></wa-icon>
|
||
</div>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-xs">WASM-Powered</span>
|
||
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||
Wallet runs entirely in your browser - fast, secure, and always available
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="feature-item">
|
||
<div class="feature-icon privacy">
|
||
<wa-icon name="user-shield"></wa-icon>
|
||
</div>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-xs">You Own Your Data</span>
|
||
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||
Self-sovereign identity means your keys never leave your device
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<wa-button variant="brand" size="large" style="width: 100%;" onclick="goToStep(2)">
|
||
Learn More
|
||
<wa-icon slot="end" name="arrow-right"></wa-icon>
|
||
</wa-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="step" data-step="2">
|
||
<div class="wa-stack wa-gap-l">
|
||
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
||
<h2 class="wa-heading-l">How Motr Works</h2>
|
||
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
||
A next-generation wallet built on WebAuthn and WASM
|
||
</p>
|
||
</div>
|
||
|
||
<wa-divider></wa-divider>
|
||
|
||
<div class="wa-stack wa-gap-m">
|
||
<wa-callout variant="neutral">
|
||
<wa-icon slot="icon" name="microchip"></wa-icon>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-xs">WebAssembly Runtime</span>
|
||
<span class="wa-caption-s">Your wallet logic runs as compiled Go code in a secure WASM sandbox, directly in your browser.</span>
|
||
</div>
|
||
</wa-callout>
|
||
|
||
<wa-callout variant="neutral">
|
||
<wa-icon slot="icon" name="fingerprint"></wa-icon>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-xs">Passkey Authentication</span>
|
||
<span class="wa-caption-s">Use Face ID, Touch ID, or hardware security keys. Your biometrics stay on your device.</span>
|
||
</div>
|
||
</wa-callout>
|
||
|
||
<wa-callout variant="neutral">
|
||
<wa-icon slot="icon" name="network-wired"></wa-icon>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-xs">Decentralized Identity</span>
|
||
<span class="wa-caption-s">Connect to any app with OpenID Connect - you control what data to share.</span>
|
||
</div>
|
||
</wa-callout>
|
||
|
||
<wa-callout variant="neutral">
|
||
<wa-icon slot="icon" name="key"></wa-icon>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-xs">Multi-Chain Support</span>
|
||
<span class="wa-caption-s">One wallet for Sonr, Ethereum, Cosmos, Bitcoin and more via IBC.</span>
|
||
</div>
|
||
</wa-callout>
|
||
</div>
|
||
|
||
<div class="wa-cluster wa-gap-s" style="justify-content: space-between;">
|
||
<wa-button variant="neutral" appearance="outlined" onclick="goToStep(1)">
|
||
<wa-icon slot="start" name="arrow-left"></wa-icon>
|
||
Back
|
||
</wa-button>
|
||
<wa-button variant="brand" onclick="goToStep(3)">
|
||
Get Started
|
||
<wa-icon slot="end" name="arrow-right"></wa-icon>
|
||
</wa-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="step" data-step="3">
|
||
<div class="wa-stack wa-gap-l">
|
||
<div class="wa-stack wa-gap-xs" style="text-align: center;">
|
||
<h2 class="wa-heading-l">Ready to Begin?</h2>
|
||
<p class="wa-caption-m" style="color: var(--wa-color-neutral-600);">
|
||
Create a new wallet or sign in to an existing one
|
||
</p>
|
||
</div>
|
||
|
||
<wa-divider></wa-divider>
|
||
|
||
<div class="wa-grid" style="--min-column-size: 180px; gap: var(--wa-space-m);">
|
||
<div class="action-card" onclick="window.location.href='register.html'">
|
||
<wa-icon name="user-plus" family="duotone"></wa-icon>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-m">Create Wallet</span>
|
||
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||
New to Sonr? Set up your wallet in minutes
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="action-card" onclick="window.location.href='login.html'">
|
||
<wa-icon name="right-to-bracket" family="duotone"></wa-icon>
|
||
<div class="wa-stack wa-gap-2xs">
|
||
<span class="wa-heading-m">Sign In</span>
|
||
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">
|
||
Access your existing wallet
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<wa-divider>or</wa-divider>
|
||
|
||
<wa-button variant="neutral" appearance="outlined" style="width: 100%;" onclick="scanQR()">
|
||
<wa-icon slot="start" name="qrcode"></wa-icon>
|
||
Scan QR Code
|
||
</wa-button>
|
||
|
||
<wa-callout variant="brand" appearance="filled">
|
||
<wa-icon slot="icon" name="circle-info"></wa-icon>
|
||
<span class="wa-caption-s">
|
||
Already have a passkey registered on another device? Use QR code to sync your wallet.
|
||
</span>
|
||
</wa-callout>
|
||
|
||
<wa-button variant="neutral" appearance="plain" onclick="goToStep(2)" style="width: 100%;">
|
||
<wa-icon slot="start" name="arrow-left"></wa-icon>
|
||
Back to How It Works
|
||
</wa-button>
|
||
</div>
|
||
</div>
|
||
|
||
<footer slot="footer">
|
||
<div class="wa-stack wa-gap-m wa-align-items-center">
|
||
<div class="network-status">
|
||
<div class="status-dot"></div>
|
||
<span class="wa-caption-s" style="color: var(--wa-color-neutral-600);">
|
||
Sonr Network: <strong style="color: var(--wa-color-success);">Operational</strong>
|
||
</span>
|
||
</div>
|
||
<div class="wa-cluster wa-justify-content-center wa-gap-m">
|
||
<wa-button appearance="plain" size="small" onclick="window.open('https://sonr.io', '_blank')">
|
||
Learn about Sonr
|
||
</wa-button>
|
||
<span style="color: var(--wa-color-neutral-300);">|</span>
|
||
<wa-button appearance="plain" size="small" onclick="window.open('https://docs.sonr.io', '_blank')">
|
||
Documentation
|
||
</wa-button>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</wa-card>
|
||
</main>
|
||
</wa-page>
|
||
|
||
<script>
|
||
let currentStep = 1;
|
||
|
||
function goToStep(step) {
|
||
document.querySelectorAll('.step').forEach(el => el.classList.remove('active'));
|
||
|
||
document.querySelectorAll('.stepper-item').forEach(item => {
|
||
const itemStep = parseInt(item.dataset.step);
|
||
item.classList.remove('active', 'completed');
|
||
if (itemStep < step) {
|
||
item.classList.add('completed');
|
||
} else if (itemStep === step) {
|
||
item.classList.add('active');
|
||
}
|
||
});
|
||
|
||
document.querySelectorAll('.stepper-line').forEach((line, index) => {
|
||
line.classList.toggle('completed', index < step - 1);
|
||
});
|
||
|
||
const targetStep = document.querySelector(`.step[data-step="${step}"]`);
|
||
if (targetStep) {
|
||
targetStep.classList.add('active');
|
||
}
|
||
|
||
currentStep = step;
|
||
}
|
||
|
||
function scanQR() {
|
||
alert('QR Scanner would open here - scan a code from another device to sync your wallet.');
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|