feat: mobile-first redesign and layout improvements

## Major Changes

### Mobile-First Responsive Design
- Converted entire app to mobile-first single-column layout
- Constrained max-width to 448px (mobile phone width)
- Black margins on desktop, centered content
- Removed all multi-column grids (md:grid-cols-3)

### Header Reorganization (3-Row Layout)
- Row 1: App logo + title + version
- Row 2: Security badges + action buttons (Empty, Reset)
- Row 3: Navigation tabs (Create, Backup, Restore, Blender)
- Replaced text buttons with emoji icons (📋 clipboard, 🙈 privacy mask)
- Consistent button sizing across all tabs

### Font Size Reductions
- Reduced all button text sizes for mobile density
- Main buttons: py-4 → py-3, added text-sm
- Labels: text-xs → text-[10px]
- Placeholders: consistent text-[10px] across all inputs
- Input fields: text-sm → text-xs, p-4 → p-3

### Create Tab Improvements
- Changed "GENERATE NEW SEED" from button-style to banner
- Left-aligned banner with gradient background
- Equal-width button grid (12/24 Words, Backup/Seed Blender)
- Used grid-cols-2 for consistent sizing

### Backup Tab Improvements
- Simplified drag-drop area with 📎 emoji
- Reduced padding and text sizes
- Cleaner, shorter copy
- PGP label font size: text-xs → text-[12px]

### SeedBlender Component
- Reorganized mnemonic input cards: textarea on row 1, buttons on row 2
- QR button (left) and X button (right) alignment
- Consistent placeholder text sizing (text-[10px])
- Shortened dice roll placeholder text

### HTTPS Development Server
- Added @vitejs/plugin-basic-ssl for HTTPS in dev mode
- Configured server to listen on 0.0.0.0:5173
- Fixed Web Crypto API issues on mobile (requires secure context)
- Enables testing on iPhone via local network

## Technical Details
- All changes maintain cyberpunk theme and color scheme
- Improved mobile usability and visual consistency
- No functionality changes, pure UI/UX improvements
This commit is contained in:
LC mac
2026-02-09 21:58:18 +08:00
parent 75da988968
commit 185efe454f
9 changed files with 605 additions and 540 deletions

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Shield, Lock, RefreshCw } from 'lucide-react';
import { Shield, RefreshCw, Lock, Unlock } from 'lucide-react';
import SecurityBadge from './badges/SecurityBadge';
import StorageBadge from './badges/StorageBadge';
import ClipboardBadge from './badges/ClipboardBadge';
@@ -52,24 +52,28 @@ const Header: React.FC<HeaderProps> = ({
onResetAll
}) => {
return (
<header className="sticky top-0 z-50 bg-[#0a0a0f] border-b border-[#00f0ff]/30 backdrop-blur-sm">
<div className="max-w-7xl mx-auto px-6 py-4">
<header className="sticky top-0 z-50 bg-[#0a0a0f] border-b border-[#00f0ff30] backdrop-blur-sm">
<div className="w-full px-4 py-3 space-y-3">
{/* ROW 1: Logo + App Info */}
<div className="flex items-center justify-between">
{/* Left: Logo & Title */}
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-[#00f0ff] rounded-lg flex items-center justify-center shadow-[0_0_15px_rgba(0,240,255,0.5)]">
<Shield className="w-6 h-6 text-[#0a0a0f]" />
</div>
<div>
<h1 className="text-lg font-semibold text-[#00f0ff]" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>
SeedPGP <span className="text-[#ff006e]">v{appVersion}</span>
SeedPGP <span className="text-[#ff006e]">{appVersion}</span>
</h1>
<p className="text-xs text-[#6ef3f7]">OpenPGP-secured BIP39 backup</p>
</div>
</div>
</div>
{/* Center: Monitoring Badges */}
<div className="hidden md:flex items-center gap-3">
{/* ROW 2: Monitoring Badges + Action Buttons */}
<div className="flex items-center justify-between gap-2 pb-2 border-b border-[#00f0ff20]">
{/* Left: Badges */}
<div className="flex items-center gap-2">
<SecurityBadge onClick={onOpenSecurityModal} />
<div onClick={onOpenStorageModal} className="cursor-pointer">
<StorageBadge localItems={localItems} sessionItems={sessionItems} />
@@ -81,70 +85,89 @@ const Header: React.FC<HeaderProps> = ({
</div>
{/* Right: Action Buttons */}
<div className="flex items-center gap-3">
<div className="flex items-center gap-2">
{encryptedMnemonicCache && (
<button
onClick={handleLockAndClear}
className="flex items-center gap-2 text-sm text-[#ff006e] bg-[#16213e] px-3 py-1.5 rounded-lg hover:bg-[#ff006e]/20 border-2 border-[#ff006e]/50 transition-all hover:shadow-[0_0_15px_rgba(255,0,110,0.5)]"
onClick={onToggleLock}
className="px-2 py-1.5 text-base bg-[#16213e] border border-[#00f0ff] text-[#00f0ff] rounded-lg font-medium hover:bg-[#00f0ff20] transition-all"
title={isLocked ? "Show sensitive data" : "Hide sensitive data"}
>
<Lock size={16} />
<span>Lock/Clear</span>
{isLocked ? '🔓' : '🙈'}
</button>
)}
{/* Reset All button - left side */}
<button
onClick={async () => {
try {
await navigator.clipboard.writeText('');
} catch { }
localStorage.clear();
sessionStorage.clear();
}}
className="px-2 py-1.5 text-base bg-[#16213e] border border-[#00f0ff] text-[#00f0ff] rounded-lg font-medium hover:bg-[#00f0ff20] transition-all"
title="Clear clipboard and storage"
>
📋
</button>
<button
onClick={onResetAll}
className="px-4 py-2 bg-[#16213e] border-2 border-[#ff006e] text-[#ff006e] rounded-lg font-medium hover:bg-[#ff006e] hover:text-white transition-all flex items-center gap-2"
className="px-2 py-1.5 text-xs bg-[#16213e] border border-[#ff006e] text-[#ff006e] rounded-lg font-medium hover:bg-[#ff006e20] transition-all whitespace-nowrap flex items-center gap-1"
>
<RefreshCw size={16} />
Reset All
<RefreshCw size={12} />
Reset
</button>
{encryptedMnemonicCache && (
<div className="h-8 w-px bg-[#00f0ff]/30 mx-2"></div>
)}
<button
className={`px-4 py-2 rounded-lg font-medium ${activeTab === 'create' ? 'bg-[#1a1a2e] text-[#00f0ff] border-b-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)] relative' : 'bg-[#0a0a0f] text-[#9d84b7] hover:text-[#6ef3f7] hover:bg-[#16213e] transition-all'}`}
style={activeTab === 'create' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('create')}
>
Create
</button>
<button
className={`px-4 py-2 rounded-lg font-medium ${activeTab === 'backup' ? 'bg-[#1a1a2e] text-[#00f0ff] border-b-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)] relative' : 'bg-[#0a0a0f] text-[#9d84b7] hover:text-[#6ef3f7] hover:bg-[#16213e] transition-all'}`}
style={activeTab === 'backup' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('backup')}
>
Backup
</button> <button
className={`px-4 py-2 rounded-lg font-medium ${activeTab === 'restore' ? 'bg-[#1a1a2e] text-[#00f0ff] border-b-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)] relative' : 'bg-[#0a0a0f] text-[#9d84b7] hover:text-[#6ef3f7] hover:bg-[#16213e] transition-all'}`}
style={activeTab === 'restore' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('restore')}
>
Restore
</button>
<button
className={`px-4 py-2 rounded-lg font-medium ${activeTab === 'seedblender' ? 'bg-[#1a1a2e] text-[#00f0ff] border-b-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)] relative' : 'bg-[#0a0a0f] text-[#9d84b7] hover:text-[#6ef3f7] hover:bg-[#16213e] transition-all'}`}
style={activeTab === 'seedblender' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('seedblender')}
>
Seed Blender
</button> </div>
</div>
</div>
{/* Mobile: Stack monitoring badges */}
<div className="md:hidden flex items-center gap-3 mt-3 pt-3 border-t border-[#00f0ff]/30">
<SecurityBadge onClick={onOpenSecurityModal} />
<div onClick={onOpenStorageModal} className="cursor-pointer">
<StorageBadge localItems={localItems} sessionItems={sessionItems} />
</div>
<div onClick={onOpenClipboardModal} className="cursor-pointer">
<ClipboardBadge events={events} onOpenClipboardModal={onOpenClipboardModal} />
</div>
<EditLockBadge isLocked={isLocked} onToggle={onToggleLock} />
{/* ROW 3: Navigation Tabs */}
<div className="grid grid-cols-4 gap-2">
<button
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'create'
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
}`}
style={activeTab === 'create' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('create')}
>
Create
</button>
<button
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'backup'
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
}`}
style={activeTab === 'backup' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('backup')}
>
Backup
</button>
<button
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'restore'
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
}`}
style={activeTab === 'restore' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('restore')}
>
Restore
</button>
<button
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'seedblender'
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
}`}
style={activeTab === 'seedblender' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
onClick={() => onRequestTabChange('seedblender')}
>
Blender
</button>
</div>
</div>
</header>
);
};
export default Header;
export default Header;