Files
imail/DESIGN.md

903 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Zemail (iMail) — Design System & UI/UX Guidelines
> **Version:** 1.1.0 — Last updated: 2026-03-06
>
> This document is the single source of truth for every visual, interactive, and architectural decision in the Zemail project. Any developer or AI agent working on this codebase **must** read this document before making any frontend changes, and **must** update it after completing any UI/UX-related work.
---
## Table of Contents
1. [Activated Skills & References](#1-activated-skills--references)
2. [Design Philosophy](#2-design-philosophy)
3. [Tech Stack](#3-tech-stack)
4. [Aesthetic Preset: "DevTool Dark"](#4-aesthetic-preset-devtool-dark)
5. [Design Tokens (CSS Custom Properties)](#5-design-tokens-css-custom-properties)
6. [Typography](#6-typography)
7. [Color System](#7-color-system)
8. [Spacing & Layout](#8-spacing--layout)
9. [Component Library](#9-component-library)
10. [Animation & Micro-Interactions](#10-animation--micro-interactions)
11. [Real-Time UI Patterns](#11-real-time-ui-patterns)
12. [Responsive Design Rules](#12-responsive-design-rules)
13. [Accessibility](#13-accessibility)
14. [Iteration History & Design Decisions](#14-iteration-history--design-decisions)
15. [Maintenance Protocol (MANDATORY)](#15-maintenance-protocol-mandatory)
---
## 1. Activated Skills & References
The following `.agents/skills` were activated during the initial design and development of this project. **Any future developer or AI agent must activate the relevant skill before working in that domain.**
| Skill | Path | When to Activate |
|-------|------|------------------|
| **`bento-landing-page-generator`** | `.agents/skills/bento-landing-page-generator/SKILL.md` | Building or modifying Bento-style landing pages, hero sections, feature grids. This was the **primary skill** used to establish the entire visual language of this project. |
| **`tailwindcss-development`** | `.agents/skills/tailwindcss-development/SKILL.md` | Any styling work. Uses Tailwind CSS **v4** with CSS-first `@theme` configuration. Never use deprecated v3 utilities. |
| **`fluxui-development`** | `.agents/skills/fluxui-development/SKILL.md` | Building forms, modals, inputs, or interactive UI components. Uses the **Flux UI Free** edition. |
| **`volt-development`** | `.agents/skills/volt-development/SKILL.md` | Creating single-file Livewire components. Check existing Volt components for functional vs. class-based style before creating new ones. |
| **`cinematic-landing-page-builder`** | `.agents/skills/cinematic-landing-page-builder/SKILL.md` | Building cinematic, pixel-perfect landing pages with strict design system enforcement, micro-interactions, and GSAP animations. |
| **`pest-testing`** | `.agents/skills/pest-testing/SKILL.md` | Writing or modifying tests. Uses Pest 3. |
### External References Used
- **Appwrite Dashboard** — Dark UI inspiration for surface colors, card borders, and glow effects.
- **Vercel** — Minimal dark theme, clean typography hierarchy, terminal-style code blocks.
- **Stripe / BentoNow** — Asymmetric Bento Grid layout patterns, card span ratios.
- **Heroicons** — Default icon set. Search at [heroicons.com](https://heroicons.com/). Never guess icon names.
---
## 2. Design Philosophy
### Core Principles
1. **Premium-First**: Every element must feel premium and state-of-the-art. No basic MVPs, no generic layouts. The user should be "wowed" at first glance.
2. **Dark Mode Native**: The application is dark-mode-only (`class="dark"` on `<html>`). All design tokens, colors, and contrasts are optimized for dark backgrounds.
3. **Cinematic Motion**: Interfaces should feel alive. Use GSAP for scroll-driven reveals, Alpine.js for instant state transitions, and CSS `transition-all` for micro-interactions.
4. **Information Density**: Mailbox UIs are information-dense by nature. Use compact typography (`text-[10px]`, `text-[11px]`), uppercase tracking (`tracking-widest`, `tracking-[0.2em]`), and monospace fonts for data.
5. **No Placeholders**: Never use placeholder images or `lorem ipsum`. Build all visuals with HTML/CSS/SVG. Use `generate_image` tool if real images are needed.
6. **Glassmorphism & Glow**: Frosted glass effects (`backdrop-blur-xl`, `bg-white/5`) and colored glows (`shadow-[0_0_30px_rgba(236,72,153,0.3)]`) are signature visual elements.
### The "Bento Box" Layout Rules
- Use **asymmetric CSS Grids** (e.g., `grid-cols-1 md:grid-cols-3` or `md:grid-cols-4`).
- Cards must have **distinct spans** (`col-span-2`, `row-span-2`) to create a mosaic effect.
- All cards use `rounded-2xl` or `rounded-3xl` border radii.
- Cards use `bg-zinc-900` surface color with `border border-white/5` borders.
- Background glows inside cards use `blur-2xl` with accent color at very low opacity (`bg-pink-500/5`).
---
## 3. Tech Stack
| Layer | Technology | Version |
|-------|-----------|---------|
| Backend | Laravel | v12 |
| Real-Time | Livewire | v3 |
| Client-Side Reactivity | Alpine.js | (bundled with Livewire) |
| Styling | Tailwind CSS | v4 |
| Animations | GSAP | 3.12.5 |
| Component Library | Flux UI Free | v2 |
| WebSocket | Laravel Reverb | v1 |
| Fonts | Google Fonts (via Bunny) | Inter, JetBrains Mono |
| Icons | Heroicons (via Flux) | — |
| QR Generator | QRious | 4.0.2 |
| Build Tool | Vite | v7 |
### Asset Pipeline
```
resources/css/app.css → Tailwind v4 with @theme design tokens
resources/js/app.js → Laravel Echo + Pusher + WebSocket status dispatching
resources/views/ → Blade templates, Livewire components, anonymous components
```
All frontend changes require `npm run build` (or `npm run dev` during development) to take effect.
---
## 4. Aesthetic Preset: "DevTool Dark"
This project uses **Preset A — "DevTool Dark"**, inspired by Appwrite and Vercel.
| Property | Value |
|----------|-------|
| Background | `#09090B` (`bg-app-bg`) |
| Surface | `#18181B` (`bg-app-surface` / `bg-zinc-900`) |
| Borders | `#27272A` (`border-app-border` / `border-white/5`) |
| Primary Accent | `#EC4899` (Pink) |
| Secondary Accent | `#10B981` (Emerald) |
| Text Primary | `#FAFAFA` |
| Text Secondary | `#737373` (`text-zinc-500`) |
| Text Muted | `#525252` (`text-zinc-600`) |
| Selection Highlight | `rgba(236, 72, 153, 0.3)` (`selection:bg-[#EC4899]/30`) |
---
## 5. Design Tokens (CSS Custom Properties)
All design tokens are defined in `resources/css/app.css` using Tailwind v4's CSS-first `@theme` directive.
```css
@theme {
/* Core Application Colors */
--color-app-bg: #09090B;
--color-app-surface: #18181B;
--color-app-border: #27272A;
--color-app-red: #EC6A5F; /* macOS-style traffic light red */
--color-app-yellow: #F4BF4F; /* macOS-style traffic light yellow */
--color-app-green: #61C554; /* macOS-style traffic light green */
/* Zinc Scale (Dark Mode Neutrals) */
--color-zinc-50: #fafafa;
--color-zinc-500: #737373;
--color-zinc-600: #525252;
--color-zinc-800: #262626;
--color-zinc-900: #171717;
--color-zinc-950: #0a0a0a;
/* Primary (Purple/Indigo Scale — used for accents) */
--color-primary-500: #787ddc;
--color-primary-600: #615dce;
/* App Primary (oklch scale — for gradients and advanced color) */
--color-app-primary-500: oklch(0.589 0.137 290.02);
--color-app-primary-600: oklch(0.506 0.16 288.92);
/* Typography */
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
}
```
### Important Token Rules
- **Never hardcode colors** that already exist as tokens. Use `bg-app-bg`, `text-zinc-500`, etc.
- **Opacity modifiers** use the Tailwind v4 slash syntax: `bg-pink-500/10`, not the deprecated `bg-opacity-*`.
- **New tokens** must be added to `@theme {}` in `app.css`, not in a `tailwind.config.js` file (Tailwind v4 is CSS-first).
---
## 6. Typography
### Font Stack
| Usage | Font | Weight | Loaded Via |
|-------|------|--------|------------|
| Body & UI | **Inter** | 400, 500, 600, 700 | Bunny Fonts CDN |
| Code, Data, Addresses | **JetBrains Mono** | 400, 500 | Bunny Fonts CDN |
### Font Loading
```html
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=inter:400,500,600,700|jetbrains-mono:400,500" rel="stylesheet" />
```
### Typography Scale
| Element | Classes | Example Usage |
|---------|---------|---------------|
| Page Heading | `text-2xl font-black tracking-tight uppercase italic` | Confirm modal title |
| Section Label | `text-[10px] font-bold text-zinc-500 uppercase tracking-widest` | "Active Mailbox", "Your Sessions" |
| Category Label | `text-[10px] font-bold text-zinc-600 uppercase tracking-[0.2em]` | Sidebar section headers |
| Data Display (email) | `text-[11px] font-mono text-white break-all` | Email address display |
| Toast Type Label | `text-[10px] font-black uppercase tracking-[0.2em] opacity-40` | Toast "SUCCESS", "INFO" label |
| Toast Message | `text-[11px] font-bold tracking-wide whitespace-pre-wrap` | Toast notification body |
| Button Text | `text-xs font-bold` | Primary action buttons |
| Compact Button Text | `text-[10px] font-black uppercase tracking-[0.2em]` | Modal confirm/cancel buttons |
| Timer Display | `text-[10px] font-mono text-pink-500` | Expiration countdown |
| Badge/Count | `text-[10px] font-bold px-1.5 py-0.5` | Unread count badge |
### Typography Rules
- **No comments in code** unless logic is exceptionally complex. Use PHPDoc blocks instead.
- **Uppercase + Wide Tracking** is the signature style for labels and metadata text.
- **Monospace (`font-mono`)** is used for any machine-readable data: email addresses, timers, code blocks.
- **`break-all`** must be applied to email addresses to prevent overflow on small screens.
---
## 7. Color System
### Semantic Color Mapping
These semantic colors are used consistently across all components (toasts, modals, buttons, indicators):
| Semantic | Background | Border | Text | Icon BG | Glow |
|----------|-----------|--------|------|---------|------|
| **Success** | `bg-emerald-500/10` | `border-emerald-500/20` | `text-emerald-100` | `bg-emerald-500/20 text-emerald-400` | `bg-emerald-500/10` |
| **Info** | `bg-blue-500/10` | `border-blue-500/20` | `text-blue-100` | `bg-blue-500/20 text-blue-400` | `bg-blue-400/10` |
| **Warning** | `bg-amber-500/10` | `border-amber-500/20` | `text-amber-100` | `bg-amber-500/20 text-amber-400` | `bg-amber-400/10` |
| **Danger** | `bg-rose-500/10` | `border-rose-500/20` | `text-rose-100` | `bg-rose-500/20 text-rose-400` | `bg-rose-400/10` |
### Important Color Rules
- **Never use generic red/blue/green**. Always use the curated semantic mapping above.
- **Pink (`#EC4899` / `pink-500`)** is the project's primary accent. Used for highlights, CTAs, gradients, and the expiration progress bar.
- **Emerald (`emerald-500`)** is the secondary accent. Used for success states and the WebSocket connection indicator.
- **Rose (`rose-500`)** is used for destructive actions, errors, and the disconnected WebSocket indicator.
- The **expiration progress bar** uses a gradient from pink to emerald: `bg-gradient-to-r from-pink-500 to-emerald-500`.
- **Toast notification types**: `success`, `info`, `warning`, `danger`. **Never** use `error` — use `danger` instead.
---
## 8. Spacing & Layout
### Global Spacing Constants
| Context | Value | Tailwind Class |
|---------|-------|----------------|
| Card Padding | 16px | `p-4` |
| Card Border Radius | 16px | `rounded-2xl` |
| Modal Border Radius | 32px | `rounded-[32px]` |
| Button Border Radius | 16px | `rounded-2xl` |
| Icon Container Radius | 12px | `rounded-xl` |
| Section Gap | 24px | `space-y-6` |
| Inner Element Gap | 8px | `space-y-2` / `gap-2` |
| Toast Container Spacing | 12px | `gap-3` |
| Screen Edge Margin | 24px | `bottom-6 left-6 right-6` (responsive) |
### Layout Architecture
The main mailbox view (`mailbox.blade.php`) uses a **three-panel layout**:
```
┌──────────┬──────────────┬──────────────────────┐
│ Sidebar │ Email List │ Email Detail │
│ (280px) │ (flex-1) │ (flex-1, lg only) │
│ │ │ │
│ - Active │ - Search │ - Header │
│ Mailbox│ - Email rows │ - Body (iframe) │
│ - Create │ - Pagination │ - Attachments │
│ - Sessions │ │
└──────────┴──────────────┴──────────────────────┘
```
- Sidebar uses `w-[280px]` when open, collapses on smaller screens.
- `overflow-hidden` on `<body>` prevents page-level scrolling. Each panel scrolls independently.
- Use `scrollbar-hide` utility class (custom CSS) to hide scrollbars in compact panels.
### Body-Level Styling
```html
<body class="bg-app-bg text-[#FAFAFA] antialiased selection:bg-[#EC4899]/30 h-full overflow-hidden">
```
---
## 9. Component Library
### 9.1 Global Toast Notification System
**Location:** `resources/views/components/layouts/app.blade.php`
**Trigger:** `$this->dispatch('notify', message: '...', type: 'success')` from Livewire, or `addToast('...', 'success')` from Alpine.js.
**Event:** `@notify.window` on `<body>`.
#### Architecture
```
<body x-data="{ toasts: [], wsConnected: true, addToast(msg, type) {...} }">
└── @notify.window="addToast($event.detail.message, $event.detail.type)"
└── @ws-status.window="wsConnected = $event.detail.connected"
└── <div class="fixed bottom-6 left-6 right-6 sm:left-auto sm:right-6 z-[100]">
└── <template x-for="toast in toasts">
└── Toast card with icon, type label, message, close button
```
#### Allowed Types
| Type | Visual | Use Case |
|------|--------|----------|
| `success` | Emerald green | Copy to clipboard, successful actions |
| `info` | Blue | New email received, informational alerts |
| `warning` | Amber/Orange | Cooldown period active, soft warnings |
| `danger` | Rose red | Address already in use, destructive errors |
#### Design Specifications
- **Container:** `fixed bottom-6 left-6 right-6 sm:left-auto sm:right-6 z-[100]`
- Mobile: Full-width with 24px margins on both sides.
- Desktop: Anchored to bottom-right.
- **Toast Card:** `w-full sm:min-w-[320px] p-4 rounded-2xl border backdrop-blur-xl shadow-2xl`
- **Auto-dismiss:** 4000ms timeout.
- **Enter Animation:** `transition ease-out duration-500` — slides in from right with scale.
- **Leave Animation:** `transition ease-in duration-300` — fades out with scale.
- **Multiline Support:** `whitespace-pre-wrap` on message div. Use `\n` in PHP strings for line breaks.
- **Icon:** 40×40px container (`w-10 h-10 rounded-xl`) with SVG stroke icon.
- **Close Button:** `p-1.5 rounded-lg hover:bg-white/5` with X icon.
#### Toast Message Formatting Examples
```php
// Single-line (copy confirmation)
$this->dispatch('notify', message: 'Address copied to clipboard', type: 'success');
// Multi-line (new email notification)
$this->dispatch('notify',
message: "Sender: {$sender}\nSubject: {$subject}",
type: 'info'
);
// Cooldown warning with precise timer
$this->dispatch('notify',
message: "Address is in cooldown. Try again in {$remaining}.",
type: 'warning'
);
// Error (address conflict)
$this->dispatch('notify', message: 'Address already in use.', type: 'danger');
```
### 9.2 Confirm Modal
**Location:** `resources/views/components/bento/confirm-modal.blade.php`
**Trigger:** `$dispatch('open-confirm-modal', { title, message, confirmLabel, type, action })`
#### Design Specifications
- **Backdrop:** `bg-zinc-950/80 backdrop-blur-xl` — heavy frosted glass overlay.
- **Card:** `max-w-[400px] bg-zinc-900 border border-white/10 rounded-[32px] p-8` — super-rounded.
- **Glow:** `w-64 h-64 rounded-full blur-[80px] opacity-20` — type-colored radial glow behind the card.
- **Icon:** `w-16 h-16 rounded-2xl` — large, prominent, type-colored icon container.
- **Title:** `text-2xl font-black text-white tracking-tight uppercase italic` — bold, dramatic.
- **Message:** `text-xs font-bold text-zinc-500 uppercase tracking-widest leading-relaxed` — subdued.
- **Buttons:** `grid grid-cols-2 gap-4` — two equal-width buttons with `py-4 rounded-2xl`.
- **Cancel:** `bg-white/5 border border-white/10 text-zinc-400` — ghost style.
- **Confirm:** Solid type-colored background (e.g., `bg-rose-600 text-white` for danger).
#### Supported Types
Same four types as the toast system: `danger`, `info`, `warning`, `success`.
### 9.3 WebSocket Connection Indicator
**Location:** `resources/views/livewire/mailbox.blade.php` (Active Mailbox card)
**State Source:** Global `wsConnected` from `<body x-data>`.
```html
<div class="w-1.5 h-1.5 rounded-full animate-pulse transition-colors duration-500"
:class="wsConnected ? 'bg-emerald-500' : 'bg-rose-500'"></div>
```
- **Connected:** Emerald green pulsing dot.
- **Disconnected:** Rose red pulsing dot.
- **Transition:** `duration-500` smooth color transition between states.
- The dot sits next to the "ACTIVE MAILBOX" label in the sidebar card.
### 9.4 Expiration Progress Bar
**Location:** `resources/views/livewire/mailbox.blade.php`
#### Design Specifications
- **Track:** `h-1 bg-white/5 rounded-full overflow-hidden`
- **Fill:** `bg-gradient-to-r from-pink-500 to-emerald-500 transition-all duration-1000 ease-linear`
- **Timer Text:** `text-[10px] font-mono text-pink-500`
- **Label:** `text-[10px] text-zinc-500 uppercase font-black tracking-tighter` — "EXPIRES IN"
#### Time Display Rules (User Iteration)
The expiration timer processes remaining time **in seconds** and displays with adaptive precision:
```javascript
// Display hierarchy (most precise possible):
if (days > 0) "14d 23h 59m 59s"
if (hours > 0) "23h 59m 59s"
if (mins > 0) "59m 59s"
else "59s"
```
> **User Iteration Note:** Originally showed "0 minutes" when only seconds remained. Fixed to always show seconds-level precision and cascade upward to days.
#### Auto-Expiration Behavior
When the timer reaches zero:
1. Alpine.js sets `isExpired = true` and fires `$wire.deleteMailbox(id)`.
2. This triggers a soft delete on the Mailbox model.
3. The `deleted` Eloquent event fires cleanup (MongoDB email bodies, MariaDB email metadata).
4. UI refreshes to show a new auto-generated mailbox.
### 9.5 Cinematic Creation Overlay
**Location:** `resources/views/livewire/mailbox.blade.php`
Shown when a first-time visitor arrives or all mailboxes are deleted:
- **Backdrop:** `fixed inset-0 z-[200] bg-zinc-950/90` — full-screen dark overlay.
- **Logo:** `animate-pulse` with scale transition (`scale-110 opacity-100``scale-90 opacity-0`).
- **Spinner:** Custom CSS spinner using `border-4 border-pink-500 border-t-transparent animate-[spin_1.5s_cubic-bezier(0.4,0,0.2,1)_infinite]` with pink glow shadow.
- **Auto-dismiss:** `setTimeout(() => $wire.finishAutoCreation(), 1000)` — 1 second cinematic delay.
### 9.6 Action Button Patterns
#### Icon Buttons (Toolbar Style)
```html
<!-- Standard action button -->
<button class="p-1.5 rounded-lg bg-white/5 text-zinc-500 hover:text-white hover:bg-white/10 transition-all cursor-pointer">
<svg class="w-3.5 h-3.5" ...></svg>
</button>
<!-- Destructive action button -->
<button class="p-1.5 rounded-lg bg-rose-500/10 text-rose-500/60 hover:text-rose-500 hover:bg-rose-500/20 transition-all cursor-pointer">
<svg class="w-3.5 h-3.5" ...></svg>
</button>
```
#### Full-Width CTA Button
```html
<button class="w-full py-3 px-4 rounded-2xl bg-white/5 border border-white/10 text-white text-xs font-bold flex items-center justify-center gap-2 hover:bg-white/10 hover:border-pink-500/30 transition-all group cursor-pointer">
<div class="w-5 h-5 rounded-lg bg-pink-500/20 text-pink-500 flex items-center justify-center group-hover:scale-110 transition-transform">
<svg class="w-3.5 h-3.5" ...></svg>
</div>
Create New Mailbox
</button>
```
#### Key Button Rules
- All buttons must have `cursor-pointer`.
- Icon containers inside buttons use `group-hover:scale-110 transition-transform` for a micro-interaction.
- Destructive buttons use the rose color scale at reduced opacity (`rose-500/10`, `rose-500/60`).
- Ghost buttons use `bg-white/5 border border-white/10`.
---
## 10. Animation & Micro-Interactions
### GSAP (Heavy Scroll Animations)
Loaded via CDN in the layout:
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
```
- Used primarily on the **landing page** for cinematic scroll reveals.
- Initialize inside Alpine's `x-init` hook: `x-init="gsap.from($el, { opacity: 0, y: 50, scrollTrigger: $el })"`.
- GSAP is **not used** in the mailbox application UI — Alpine.js handles all reactive transitions there.
### Alpine.js (Instant UI State)
All immediate user interactions must be handled client-side with Alpine.js:
| Pattern | Usage |
|---------|-------|
| `x-show` + `x-transition` | Modals, overlays, conditional panels |
| `x-data` + `$watch` | Reactive state management (timers, mobile view switching) |
| `@click` + `$wire` | Livewire method calls from the client |
| `@click` + `$dispatch` | Triggering global events (toasts, modals) |
| `@resize.window` | Responsive sidebar behavior |
| `:class` bindings | Dynamic styling (WebSocket indicator, toast types) |
### CSS Transitions
| Property | Duration | Easing | Usage |
|----------|----------|--------|-------|
| `transition-all` | default (150ms) | default | Button hover states |
| `transition-colors duration-500` | 500ms | default | WebSocket indicator color change |
| `transition-all duration-1000 ease-linear` | 1000ms | linear | Progress bar width |
| `transition ease-out duration-500` | 500ms | ease-out | Toast enter |
| `transition ease-in duration-300` | 300ms | ease-in | Toast leave |
| `transition ease-out duration-700` | 700ms | ease-out | Full-screen overlay enter |
| `transition ease-in duration-500` | 500ms | ease-in | Full-screen overlay leave |
| `transition ease-out duration-500` | 500ms | ease-out | Modal card enter (with scale + translateY) |
### Custom Keyframes
```css
@keyframes slide-right {
from { opacity: 0; transform: translateX(-10px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slide-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-slide-up {
animation: slide-up 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
```
---
## 11. Real-Time UI Patterns
### WebSocket Architecture
```
app.js → Laravel Echo (Reverb broadcaster)
→ Pusher connection state listeners
→ Dispatches window events: 'ws-status'
app.blade.php → Listens for @ws-status.window
→ Updates global Alpine 'wsConnected' state
mailbox.blade.php → Reads 'wsConnected' for indicator color
```
### Connection Status Lifecycle
```javascript
// resources/js/app.js
window.Echo.connector.pusher.connection.bind('connected', () => dispatchStatus(true));
window.Echo.connector.pusher.connection.bind('unavailable', () => dispatchStatus(false));
window.Echo.connector.pusher.connection.bind('disconnected', () => dispatchStatus(false));
window.Echo.connector.pusher.connection.bind('failed', () => dispatchStatus(false));
```
### Conditional Echo Initialization
Echo is only initialized on pages that need it, controlled by a data attribute:
```html
<div data-requires-reverb="true"> ... </div>
```
```javascript
if (document.querySelector('[data-requires-reverb]')) {
window.Echo = new Echo({ ... });
}
```
### New Email Real-Time Flow
1. `ProcessIncomingEmail` job saves email to MariaDB + MongoDB.
2. `NewEmailReceived` event broadcasts on `mailbox.{domain}` channel.
3. Livewire listener `echo:mailbox.{domain},.new.email` fires `onNewEmail()`.
4. `onNewEmail()` dispatches a toast notification with sender + subject.
5. `onNewEmail()` dispatches `$refresh` to reload the email list.
---
## 12. Responsive Design Rules
### Breakpoint Strategy
| Breakpoint | Prefix | Behavior |
|-----------|--------|----------|
| < 640px | (none) | Mobile: full-width toast, collapsed sidebar, single-panel view |
| 640px | `sm:` | Toast anchors to bottom-right, min-width restored |
| 1024px | `lg:` | Email detail panel becomes visible. Mobile view switcher deactivated |
| 1280px | `xl:` | Sidebar always open |
### Mobile View Switching
On mobile (`< 1024px`), the mailbox uses an Alpine-driven view switcher:
```javascript
$watch('selectedId', value => { if (value && window.innerWidth < 1024) mobileView = 'detail' });
$watch('mobileView', value => { if (value === 'list' && window.innerWidth < 1024) selectedId = null });
```
### Toast Responsiveness (User Iteration)
> **User Iteration Note:** The toast container originally used `fixed bottom-6 right-6` with a hard `min-w-[320px]`, causing it to overflow and touch the left screen edge on small devices. This was fixed by:
> - Container: `left-6 right-6 sm:left-auto sm:right-6` — full-width with margins on mobile, bottom-right anchor on desktop.
> - Toast card: `w-full sm:min-w-[320px]` — fluid on mobile, minimum width on desktop.
---
## 13. Accessibility
### Required Patterns
- All interactive elements must have `cursor-pointer`.
- Icon-only buttons must have a `title` attribute for tooltip/screen-reader context.
- `antialiased` is applied globally on `<body>` for smoother font rendering.
- `selection:bg-[#EC4899]/30` provides a branded text selection color.
- Use semantic HTML elements wherever possible (`<nav>`, `<button>`, `<main>`).
- All interactive elements should have unique, descriptive IDs for browser testing.
### Scrollbar Handling
```css
.scrollbar-hide::-webkit-scrollbar { display: none; }
.scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; }
```
---
## 14. Iteration History & Design Decisions
This section documents every significant design iteration from the conversation thread, in chronological order. Each entry includes the rationale so future developers understand **why** a decision was made.
### Iteration 1: Expiration Timer Precision
**Problem:** When a mailbox had only seconds remaining, the timer displayed "0 minutes" instead of showing seconds.
**Solution:** Rewrote the Alpine.js timer to process time in seconds and display with cascading precision: `days → hours → minutes → seconds`. The timer now always shows the most granular unit available.
**Files Changed:** `resources/views/livewire/mailbox.blade.php`
---
### Iteration 2: Soft Deletes Instead of Hard Deletes
**Problem:** Clicking the delete button permanently removed the mailbox record from the database, losing all audit trail.
**Solution:** Added `SoftDeletes` trait to the `Mailbox` model. Created a migration to add `deleted_at` timestamps. All delete operations now soft-delete, preserving records for analytics and potential reclamation.
**Files Changed:** `app/Models/Mailbox.php`, `database/migrations/2026_03_05_202748_add_soft_deletes_to_mailboxes_table.php`
---
### Iteration 3: Mailbox Duplication Prevention & Cooldown System
**Problem:** Random address generation could produce duplicates. Users could snipe deleted addresses instantly.
**Solution:**
- **Random addresses:** `do-while` loop checks `withTrashed()` to ensure uniqueness.
- **Same user reclaiming:** Automatically restores soft-deleted mailbox and updates expiration.
- **Different user claiming:** Tier-based cooldown enforced (Guest: 24h, Free: 12h, Pro: 6h, Enterprise: instant).
- **Cooldown toast:** Shows precise remaining time down to seconds (e.g., "11h 42m 18s").
**Files Changed:** `app/Livewire/Mailbox.php`
---
### Iteration 4: Toast Notification Type Fix
**Problem:** Cooldown and error notifications were dispatched with `type: 'error'`, which didn't match the global toast's four supported types (`success`, `info`, `warning`, `danger`). The toast would render without proper styling.
**Solution:** Changed "address already in use" to `type: 'danger'` (rose red) and cooldown warnings to `type: 'warning'` (amber). **Rule: Never use `'error'` as a toast type.**
**Files Changed:** `app/Livewire/Mailbox.php`
---
### Iteration 5: Session Linking on Login
**Problem:** Guest users creating mailboxes via session would lose access when they registered or logged in.
**Solution:** Added an `Event::listen(Login::class)` in `AppServiceProvider` to automatically transfer mailboxes from `session_id` to `user_id` when a guest authenticates.
**Files Changed:** `app/Providers/AppServiceProvider.php`
---
### Iteration 6: Automatic Expiration Cleanup
**Problem:** If a user closed their browser, expired mailboxes would never be cleaned up.
**Solution:** Dual approach:
1. **Live UI Hook:** Alpine.js timer triggers `$wire.deleteMailbox()` when countdown reaches zero.
2. **Background Worker:** `CleanupExpiredMailboxes` Artisan command runs every minute via Laravel Scheduler.
3. **Data Cleanup:** Eloquent `deleted` event on `Mailbox` model wipes associated `EmailBody` (MongoDB) and `Email` (MariaDB) records.
**Files Changed:** `app/Console/Commands/CleanupExpiredMailboxes.php`, `routes/console.php`, `app/Models/Mailbox.php`, `resources/views/livewire/mailbox.blade.php`
---
### Iteration 7: WebSocket Status Indicator
**Problem:** The "Active Mailbox" pulse indicator was always green, regardless of connection state.
**Solution:**
1. Added `connected/disconnected/unavailable/failed` listeners to Echo in `app.js`.
2. Stored `wsConnected` in global Alpine state on `<body>`.
3. Bound indicator color dynamically: emerald for connected, rose for disconnected.
**Files Changed:** `resources/js/app.js`, `resources/views/components/layouts/app.blade.php`, `resources/views/livewire/mailbox.blade.php`
---
### Iteration 8: New Email Toast Notification
**Problem:** Users had no visual cue when a new email arrived while reading another email.
**Solution:** Updated `onNewEmail()` to dispatch a toast with sender and subject before refreshing the list. Used `type: 'info'` (blue) for the notification.
**Files Changed:** `app/Livewire/Mailbox.php`
---
### Iteration 9: Multiline Toast Formatting
**Problem:** Toast notification for new emails displayed sender and subject on a single line, making it hard to scan.
**Solution:**
1. Changed message format to `"Sender: {$sender}\nSubject: {$subject}"` using `\n`.
2. Added `whitespace-pre-wrap` to the toast message div to render line breaks.
**Files Changed:** `app/Livewire/Mailbox.php`, `resources/views/components/layouts/app.blade.php`
---
### Iteration 10: Mobile Toast Overflow Fix
**Problem:** On small screens (< 320px viewport), the toast's `min-w-[320px]` caused it to overflow beyond the left edge of the screen.
**Solution:**
- Container: Changed from `fixed bottom-6 right-6` to `fixed bottom-6 left-6 right-6 sm:left-auto sm:right-6`.
- Toast card: Changed from `min-w-[320px]` to `w-full sm:min-w-[320px]`.
**Files Changed:** `resources/views/components/layouts/app.blade.php`
---
### Iteration 11: Random Address Format
**Problem:** Random mailbox addresses included an underscore before the number suffix (e.g., `john_doe_42@imail.app`).
**Solution:** User manually removed the underscore from the format string. Addresses now generate as `johndoe42@imail.app`.
**Files Changed:** `app/Livewire/Mailbox.php` (user edit)
---
## Appendix: File Reference Map
| File | Purpose |
|------|---------|
| `resources/css/app.css` | Tailwind v4 theme tokens, Flux UI imports, dark mode config |
| `resources/js/app.js` | Laravel Echo init, WebSocket status dispatching |
| `resources/views/components/layouts/app.blade.php` | Global layout: fonts, GSAP, global Alpine state, toast system |
| `resources/views/livewire/mailbox.blade.php` | Main mailbox UI: sidebar, email list, detail view, modals |
| `resources/views/components/bento/confirm-modal.blade.php` | Reusable confirm modal with type-based theming |
| `resources/views/livewire/landing-page.blade.php` | Bento-style SaaS landing page |
| `app/Livewire/Mailbox.php` | Mailbox Livewire component: CRUD, WebSocket, analytics |
| `app/Models/Mailbox.php` | Mailbox Eloquent model: soft deletes, cleanup events |
| `app/Events/NewEmailReceived.php` | WebSocket broadcast event for incoming emails |
| `app/Console/Commands/CleanupExpiredMailboxes.php` | Scheduled cleanup of expired mailboxes |
---
> **For AI Agents:** Before making any UI change, re-read the relevant sections of this document. Match existing patterns exactly. When in doubt, check sibling components for conventions. Always activate the appropriate skill from `.agents/skills/` before starting work. **After completing any UI/UX change, update this document per Section 15.**
---
## 15. Maintenance Protocol (MANDATORY)
This document is a **living document**. Every developer or AI agent who makes a UI/UX-related change **must** update it before considering their work complete. This ensures no knowledge is lost and prevents future confusion or hallucination.
### 15.1 When to Update
| Scenario | What to Update | Priority |
|----------|---------------|----------|
| New component created | Add to **§9 Component Library** with full spec | 🔴 Required |
| Existing component modified | Update the relevant subsection in **§9** | 🔴 Required |
| New design token added | Add to **§5 Design Tokens** | 🔴 Required |
| Color or typography changed | Update **§6 Typography** or **§7 Color System** | 🔴 Required |
| New animation or transition | Add to **§10 Animation** table | 🟡 Recommended |
| New real-time event or flow | Add to **§11 Real-Time UI Patterns** | 🔴 Required |
| Responsive behavior changed | Update **§12 Responsive Design Rules** | 🔴 Required |
| Any iterative design refinement | Add to **§14 Iteration History** | 🔴 Required |
| New file created that affects UI | Add to **Appendix: File Reference Map** | 🟡 Recommended |
| New external dependency added | Add to **§3 Tech Stack** table | 🔴 Required |
| New skill activated | Add to **§1 Activated Skills** table | 🟡 Recommended |
### 15.2 Iteration Entry Template
Every design change must be logged in **§14 Iteration History** using this exact format:
```markdown
### Iteration N: [Short Descriptive Title]
**Date:** YYYY-MM-DD
**Conversation/PR:** [Link or ID if available]
**Problem:** One sentence describing what was wrong or what the user requested.
**Why:** One sentence explaining *why* this matters (UX impact, visual regression, accessibility, etc.).
**Solution:** Bullet points detailing what was changed. Include:
- Exact Tailwind classes added/removed/changed.
- Any new Alpine.js state or events introduced.
- Any new Livewire methods or events.
**Files Changed:** Comma-separated list of relative file paths.
**Design Rules Established:** (Optional) Any new rules that emerged from this change.
```
#### Example — Well-Written Entry
```markdown
### Iteration 12: Attachment Preview Thumbnails
**Date:** 2026-03-10
**Conversation:** c1ba6ccd-8bd9-4670-94e4-50b936242f39
**Problem:** Attachments in the email detail view showed only filenames with no visual preview.
**Why:** Users couldn't quickly assess whether an attachment was an image, document, or archive without downloading it.
**Solution:**
- Added thumbnail preview for image attachments using `<img>` with `w-16 h-16 rounded-lg object-cover`.
- Non-image attachments show a colored icon badge: `bg-blue-500/10 text-blue-400` for documents, `bg-amber-500/10 text-amber-400` for archives.
- Thumbnails are lazy-loaded with `loading="lazy"` to avoid blocking the main render.
**Files Changed:** `resources/views/livewire/mailbox.blade.php`
**Design Rules Established:** All file-type indicators must use the semantic color mapping from §7. Image previews always use `rounded-lg object-cover`.
```
#### Example — Poorly-Written Entry (DO NOT DO THIS)
```markdown
### Iteration 12: Fixed attachments
**Problem:** Attachments looked bad.
**Solution:** Made them look better.
**Files Changed:** mailbox.blade.php
```
> ⚠️ **This lacks specificity.** It doesn't explain *why* it mattered, *what* CSS classes were used, or *what rules* emerged. A future developer reading this would learn nothing and likely re-implement differently.
### 15.3 Component Documentation Template
When adding a new component to **§9 Component Library**, use this structure:
```markdown
### 9.N [Component Name]
**Location:** `path/to/file.blade.php`
**Trigger:** How to invoke (dispatch event, Livewire property, Alpine method, etc.)
**State Source:** Where the component gets its data.
#### Design Specifications
- **Container:** Exact Tailwind classes for the outermost wrapper.
- **Card/Body:** Exact Tailwind classes for the main content area.
- **[Other visual elements]:** One bullet per distinct visual element.
#### Behavior
- How it appears (animation/transition).
- How it dismisses.
- How it responds to different screen sizes.
#### Supported Variants
Table of variants (types, sizes, states) if applicable.
#### Code Example
```php
// Minimal working example showing how to trigger/use the component.
```
```
### 15.4 Update Checklist for AI Agents
Before marking any UI/UX task as complete, run through this checklist:
- [ ] **Read** relevant sections of `DESIGN.md` before starting work.
- [ ] **Match** existing patterns from this document (colors, spacing, typography, animations).
- [ ] **Update §14** with a new Iteration entry if any visual or behavioral change was made.
- [ ] **Update §9** if a component was created or modified.
- [ ] **Update §5/§6/§7** if design tokens, fonts, or colors were added/changed.
- [ ] **Update §3** if a new dependency was added.
- [ ] **Update Appendix** if a new file was created.
- [ ] **Bump the version** at the top of the file (patch for fixes, minor for features).
- [ ] **Update the "Last updated" date** at the top of the file.
### 15.5 Versioning Convention
The version number at the top of this file follows **Major.Minor.Patch**:
| Change Type | Bump | Example |
|------------|------|--------|
| New section or major restructure | Major | 1.0.0 → 2.0.0 |
| New component, new iteration, new design token | Minor | 1.0.0 → 1.1.0 |
| Typo fix, clarification, minor class update | Patch | 1.0.0 → 1.0.1 |
### 15.6 Context Preservation Rules
To keep entries **light but sufficient** (no confusion, no hallucination):
1. **Always include exact Tailwind classes.** Don't write "made it rounded" — write `rounded-2xl`.
2. **Always include the file path.** Don't write "the modal" — write `resources/views/components/bento/confirm-modal.blade.php`.
3. **Always state the user's intent.** Don't write "changed the color" — write "User requested rose for destructive actions instead of generic red."
4. **Always state the technical reason.** Don't write "it was broken" — write "The toast type `'error'` had no matching `:class` binding in the Alpine template."
5. **Keep it to 3-5 sentences max per entry.** More detail goes in the component spec 9), not the iteration log 14).
6. **Reference existing sections.** Instead of re-explaining the color system, write "Uses semantic Danger mapping from §7."