diff --git a/kez-chat/web/DESIGN.md b/kez-chat/web/DESIGN.md new file mode 100644 index 0000000..e605e63 --- /dev/null +++ b/kez-chat/web/DESIGN.md @@ -0,0 +1,167 @@ +# KEZ Design System + +> Source of truth for the kez-chat redesign (`redesign-kez-theme` branch). +> Goal: take the chat app out of the prototype phase into a real, +> WhatsApp/Discord-caliber messenger with an iconic KEZ identity. + +## Who we're designing for + +Hackers, infosec people, privacy absolutists, anti-surveillance / sovereignty +folks, Meshtastic & off-grid comms operators, journalists/activists in hostile +environments — the Signal / Briar / Tails / Mullvad / Monero crowd. They trust +**verifiability over promises**, have a finely-tuned bullshit detector, and +bounce instantly from anything that smells like VC surveillance-ware. + +**Positioning:** _KEZ is the sovereign identity layer + encrypted comms for +people who assume the network is hostile._ + +## Aesthetic direction + +**Muted tactical terminal — restraint, not neon cosplay.** Mullvad's calm +authority. Monospace as identity. The first 3 seconds should feel like opening +an operational tool, not a brochure. Hard-ish edges, visible structure, a single +cold accent. No gradients-blobs, no mascots, no "delightful." + +### Hard DO-NOTs +- No rounded-blob/gradient SaaS look, no mascots, no illustrations of laughing people. +- No "military-grade / bank-level" marketing adjectives. Show, don't boast. +- No surveillance tells: no third-party analytics, no social login, no email-required signup. +- No stock photography. + +## Color palette (dark-first; light theme = v2, out of scope) + +Tailwind v4 `@theme` tokens, in `src/app.css`. + +| Token | Hex | Use | +|---|---|---| +| `--color-bg` | `#0B0C0E` | app background (neutral near-black) | +| `--color-surface` | `#16181C` | cards, conversation list, sidebars | +| `--color-elevated` | `#1E2127` | modals, menus, input wells | +| `--color-border` | `#2A2E35` | hairlines, dividers | +| `--color-text` | `#E8EAED` | primary text (neutral off-white) | +| `--color-text-secondary` | `#9BA3AD` | secondary | +| `--color-text-muted` | `#5C636D` | timestamps, meta | +| `--color-text-disabled` | `#3A4049` | disabled | +| `--color-accent` | `#28C8E8` | **the KEZ color** — electric cyan | +| `--color-accent-dim` | `#1B9DBC` | hover/pressed, accent borders | +| `--color-accent-contrast` | `#04131A` | text on accent fills | +| `--color-verified` | `#4ADE80` | proof verified (distinct from accent) | +| `--color-danger` | `#FF5C6C` | destructive, failed | +| `--color-warning` | `#FFB13D` | needs-attention | +| `--color-bubble-recv` | `#1B1F25` | received message bubble fill | + +Accent is used surgically: send bubbles, focus rings, active nav, the wordmark +cursor, links, live/streaming indicators. Greys carry the weight. **Verified +green is for proofs only** — never as a general accent, so a verification badge +never camouflages into accent UI. + +## Typography + +- **UI/body:** `Inter` — `--font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif` +- **Monospace:** `JetBrains Mono` — `--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace`. Used for keys, hashes, handles, the wordmark. + +Loaded via Google Fonts. + +| Role | Size / weight / line-height | Notes | +|---|---|---| +| Wordmark | 22px / 700 / 1.1 mono | `-0.02em`, + cyan block cursor | +| Section header | 12px / 600 / 1.3 sans | uppercase, `+0.08em`, secondary color | +| Body / message | 15px / 450 / 1.45 sans | | +| Conversation name | 15px / 600 / 1.3 sans | | +| Timestamp/meta | 12px / 500 / 1.2 | muted | +| Mono key display | 13px / 500 / 1.5 mono | `+0.01em` | + +## Spacing / radius / shadow + +- **Spacing** (4px base): 4, 8, 12, 16, 20, 24, 32, 48. +- **Radius — tactical, mid-soft:** `--radius-sm: 4px` (chips/badges), `--radius-md: 8px` (inputs/buttons), `--radius-lg: 12px` (bubbles/cards), `--radius-xl: 16px` (modals). Fully-round reads consumer-soft; sharp reads unfinished; 8–12 says "engineered." +- **Shadows = terminal glow, not ambient drop shadows.** Surfaces use `1px solid --color-border` hairlines. Modals: `0 8px 24px -8px rgba(0,0,0,0.6)`. Accent focus/glow: `0 0 0 1px #28C8E833, 0 0 16px -2px #28C8E866`. + +## Signature components + +- **Message bubbles** — radius `lg`, padding `8px 12px`, max-width ~78%. + - Sent: `--color-accent` fill, `--color-accent-contrast` text, `border-bottom-right-radius: 4px` tail. + - Received: `--color-bubble-recv` fill, `--color-text`, `1px solid --color-border`, `border-bottom-left-radius: 4px`. +- **Conversation row** — 64px tall, 40px avatar (`radius-md`), name 600, preview secondary, time muted. Active = left 2px accent bar + elevated bg. Unread = accent dot + name 700. +- **Buttons** — height 40px, `radius-md`, 600. Primary: accent fill / contrast text / dim hover / 0.98 active / glow focus. Secondary: transparent, `1px solid border`, hover elevated bg + accent-dim border. +- **Inputs** — elevated bg, `1px solid border`, `radius-md`, focus → accent border + `0 0 0 3px #28C8E822` ring. Key inputs use mono. +- **Handle/identity chip** — inline mono, `radius-sm`, `2px 8px`, bg `#28C8E814`, `1px solid #28C8E833`, accent text; leading `@`/`0x` muted. +- **Verified proof badge** — `radius-sm`, bg `#4ADE8014`, border `#4ADE8040`, `--color-verified` text, mono 11/600, leading ✓. +- **Avatars** — deterministic identicon generated from the ed25519 key, so every KEZ has a stable face. Eliminates the biggest "prototype" tell. + +## Motion + +Fast (120–200ms), `cubic-bezier(0.2,0.8,0.2,1)`, on state change only, respect +`prefers-reduced-motion`. +- Received message: slide-up 6px + fade. +- Sent message: spring `scale .96→1` + brief accent-glow pulse decaying ~600ms. +- Thread push/back slide on mobile. +- Tasteful terminal flourish: a single cyan block-cursor blink on the empty + compose field + after the wordmark. No content scanlines. + +## Wordmark / icon + +- **Wordmark:** `kez` lowercase, JetBrains Mono 700, `-0.02em`, primary text, + followed by a blinking cyan block cursor `▌`. The cursor is the brand mark. +- **Icon:** evolve the amber key → cyan. Recolor `public/kez-icon.svg` stroke + `#fbbf24` → `#28C8E8` on `#0B0C0E`; reshape so the key reads as a + key-meets-cursor glyph. Drop the literal 🔑 emoji everywhere. Regenerate PWA + icon set. Manifest `theme_color` + `background_color` → `#0B0C0E`. + +## Information architecture (the big structural change) + +**Land logged-in users on Chats, not a Dashboard.** The "dashboard" as a +destination is killed; its contents redistribute. + +### Navigation — 4 destinations +| Destination | Purpose | +|---|---| +| **Chats** | conversation list + threads (the home) | +| **Contacts** | known KEZs + verification status + start-new-chat | +| **Identity** | your KEZ + claims/proofs (the superpower surface) | +| **Settings** | security, backup, notifications, account, about | + +- **Mobile (PWA):** fixed bottom tab bar, 4 tabs, unread badge on Chats. + Thread view pushes full-screen with a back chevron. +- **Desktop:** slim left icon rail (4 destinations) + secondary list column + + main content pane. Replaces the current top nav bar entirely. + +### Feature → new home +| Existing | New home | +|---|---| +| Landing/Create/Restore/Unlock | unauthenticated flow (pre-nav), restyled | +| Messages (list+thread+compose, SSE, emoji, unread, notifications) | **Chats** (default surface) | +| Start chat by handle | **Contacts → New chat** (preview card before opening) | +| Claims list | **Identity → My proofs** (grouped verified/failed/pending) | +| AddClaim | **Identity → Add proof** | +| Identity display (handle@server, ed25519 key, registry) | **Identity** header card (avatar, copyable KEZ, fingerprint, QR) | +| Seed/key backup | **Settings → Security → Recovery phrase** (re-auth gated) | +| Biometric/passkey | **Settings → Security → App lock** | +| Notifications perm + test | **Settings → Notifications** | +| Build SHA / source link | **Settings → About** | + +### New-conversation flow (the KEZ moment) ++ FAB on Chats (mobile) / "New chat" in Contacts column (desktop) → +"Enter a KEZ" (`handle@server`, paste/QR) → `lookup` → **preview card**: +avatar, handle, key fingerprint, and **inline verified proofs** (✓ github:you, +✓ dns:yourdomain) → "Message". Verification is always one tap from a thread via +the contact-detail header. You see who someone is before you trust them. + +### Polish signals to ship +1. Identicon avatars everywhere (from ed25519 key). +2. Message status ticks (sent / SSE-delivered) + day separators. +3. Real empty + skeleton-loading states. +4. Verification shield badge system (green verified / neutral none / amber failed), consistent across Chats, Contacts, Identity. +5. Native push/back + send transitions; smooth auto-scroll (already shipped). + +## Implementation phases + +0. **Foundation** — `app.css` Tailwind v4 `@theme` tokens, Google Fonts, recolor icon + regenerate PWA assets, manifest colors. +1. **Shell + nav** — bottom tab bar / left rail, router lands on `/chats`, wordmark component. +2. **Chats** — restyle list + thread + bubbles, identicon avatars, empty/skeleton states. Keep SSE/emoji/notifications/auto-scroll. +3. **Identity** — identity card + proofs (migrate Claims/AddClaim). +4. **Settings** — Account / Security / Notifications / About (migrate Dashboard remainder). +5. **Contacts** — list + new-chat preview card with proofs. +6. **Auth flow restyle** — Landing/Create/Restore/Unlock to the new theme. + +All existing functionality is preserved; only its placement and presentation change. diff --git a/kez-chat/web/index.html b/kez-chat/web/index.html index e6be98d..87d855b 100644 --- a/kez-chat/web/index.html +++ b/kez-chat/web/index.html @@ -22,7 +22,7 @@ - +
diff --git a/kez-chat/web/public/apple-touch-icon-180x180.png b/kez-chat/web/public/apple-touch-icon-180x180.png index 02d42b2..4230fc2 100644 Binary files a/kez-chat/web/public/apple-touch-icon-180x180.png and b/kez-chat/web/public/apple-touch-icon-180x180.png differ diff --git a/kez-chat/web/public/kez-icon.svg b/kez-chat/web/public/kez-icon.svg index ab40845..a840b06 100644 --- a/kez-chat/web/public/kez-icon.svg +++ b/kez-chat/web/public/kez-icon.svg @@ -1,17 +1,14 @@ - - - - + + + + - - - - - - + + + + + diff --git a/kez-chat/web/public/maskable-icon-512x512.png b/kez-chat/web/public/maskable-icon-512x512.png index eedd1dd..fd5e997 100644 Binary files a/kez-chat/web/public/maskable-icon-512x512.png and b/kez-chat/web/public/maskable-icon-512x512.png differ diff --git a/kez-chat/web/public/pwa-192x192.png b/kez-chat/web/public/pwa-192x192.png index 54f8646..b5338f8 100644 Binary files a/kez-chat/web/public/pwa-192x192.png and b/kez-chat/web/public/pwa-192x192.png differ diff --git a/kez-chat/web/public/pwa-512x512.png b/kez-chat/web/public/pwa-512x512.png index 8da9eec..76f63f0 100644 Binary files a/kez-chat/web/public/pwa-512x512.png and b/kez-chat/web/public/pwa-512x512.png differ diff --git a/kez-chat/web/public/pwa-64x64.png b/kez-chat/web/public/pwa-64x64.png index 7f9108a..860578d 100644 Binary files a/kez-chat/web/public/pwa-64x64.png and b/kez-chat/web/public/pwa-64x64.png differ diff --git a/kez-chat/web/src/app.css b/kez-chat/web/src/app.css index c96b0f9..4dead42 100644 --- a/kez-chat/web/src/app.css +++ b/kez-chat/web/src/app.css @@ -1,17 +1,125 @@ @import "tailwindcss"; -/* Base typography reset on top of Tailwind v4's preflight. */ +/* Fonts: Inter (UI) + JetBrains Mono (keys/handles/wordmark). */ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;450;500;600;700&family=JetBrains+Mono:wght@500;600;700&display=swap"); + +/* ─────────────────────────────────────────────────────────────────────────── + KEZ design tokens — see DESIGN.md. Dark-first, "muted tactical terminal." + Tailwind v4 turns each --color-* into bg-*/text-*/border-* utilities, + each --font-* into font-*, each --radius-* into rounded-*. + ─────────────────────────────────────────────────────────────────────────── */ +@theme { + /* Elevation ramp (neutral near-black) */ + --color-bg: #0b0c0e; + --color-surface: #16181c; + --color-elevated: #1e2127; + --color-border: #2a2e35; + + /* Text tiers */ + --color-text: #e8eaed; + --color-text-secondary: #9ba3ad; + --color-text-muted: #5c636d; + --color-text-disabled: #3a4049; + + /* The KEZ color — electric cyan, used surgically */ + --color-accent: #28c8e8; + --color-accent-dim: #1b9dbc; + --color-accent-contrast: #04131a; + + /* Semantic */ + --color-verified: #4ade80; /* proofs only */ + --color-danger: #ff5c6c; + --color-warning: #ffb13d; + + /* Chat */ + --color-bubble-recv: #1b1f25; + + /* Type */ + --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace; + + /* Radius — tactical, mid-soft */ + --radius-sm: 4px; + --radius-md: 8px; + --radius-lg: 12px; + --radius-xl: 16px; + + /* Accent glow for focus/send + modal elevation */ + --shadow-glow: 0 0 0 1px #28c8e833, 0 0 16px -2px #28c8e866; + --shadow-elev: 0 8px 24px -8px rgba(0, 0, 0, 0.6); +} + +/* ─── Base ─────────────────────────────────────────────────────────────── */ :root { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, - Roboto, sans-serif; - color: #1f2937; - background: #f9fafb; + font-family: var(--font-sans); + color: var(--color-text); + background: var(--color-bg); + color-scheme: dark; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; } body { margin: 0; + background: var(--color-bg); + color: var(--color-text); } -code, kbd, pre { - font-family: ui-monospace, "SF Mono", Menlo, monospace; +code, +kbd, +pre, +samp { + font-family: var(--font-mono); +} + +/* Cyan text-selection — reinforces the accent. */ +::selection { + background: #28c8e840; + color: var(--color-text); +} + +/* Scrollbars: thin, dark, unobtrusive. */ +* { + scrollbar-width: thin; + scrollbar-color: var(--color-border) transparent; +} +*::-webkit-scrollbar { + width: 10px; + height: 10px; +} +*::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: 999px; + border: 2px solid transparent; + background-clip: content-box; +} +*::-webkit-scrollbar-thumb:hover { + background: var(--color-text-muted); + background-clip: content-box; +} + +/* Blinking block cursor for the wordmark + empty-compose flourish. */ +@keyframes kez-blink { + 0%, + 49% { + opacity: 1; + } + 50%, + 100% { + opacity: 0; + } +} +.kez-cursor { + display: inline-block; + width: 0.5em; + height: 1em; + margin-left: 0.08em; + vertical-align: text-bottom; + background: var(--color-accent); + animation: kez-blink 1.1s steps(1) infinite; +} +@media (prefers-reduced-motion: reduce) { + .kez-cursor { + animation: none; + } } diff --git a/kez-chat/web/src/lib/Avatar.svelte b/kez-chat/web/src/lib/Avatar.svelte new file mode 100644 index 0000000..9271d85 --- /dev/null +++ b/kez-chat/web/src/lib/Avatar.svelte @@ -0,0 +1,73 @@ + + + + + {#each [0, 1, 2, 3, 4] as col} + {#each [0, 1, 2, 3, 4] as row} + {#if isOn(col, row)} + + {/if} + {/each} + {/each} + diff --git a/kez-chat/web/src/lib/Wordmark.svelte b/kez-chat/web/src/lib/Wordmark.svelte new file mode 100644 index 0000000..1ace0e8 --- /dev/null +++ b/kez-chat/web/src/lib/Wordmark.svelte @@ -0,0 +1,18 @@ + + + + kez{#if cursor}{/if} + diff --git a/kez-chat/web/vite.config.ts b/kez-chat/web/vite.config.ts index bea44c2..5d1f83f 100644 --- a/kez-chat/web/vite.config.ts +++ b/kez-chat/web/vite.config.ts @@ -50,8 +50,8 @@ export default defineConfig({ start_url: "/", scope: "/", display: "standalone", - background_color: "#111827", - theme_color: "#111827", + background_color: "#0b0c0e", + theme_color: "#0b0c0e", categories: ["social", "communication"], icons: [ { src: "pwa-64x64.png", sizes: "64x64", type: "image/png" },