Files
imail/DESIGN.md

39 KiB
Raw Blame History

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
  2. Design Philosophy
  3. Tech Stack
  4. Aesthetic Preset: "DevTool Dark"
  5. Design Tokens (CSS Custom Properties)
  6. Typography
  7. Color System
  8. Spacing & Layout
  9. Component Library
  10. Animation & Micro-Interactions
  11. Real-Time UI Patterns
  12. Responsive Design Rules
  13. Accessibility
  14. Iteration History & Design Decisions
  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. 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.

@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

<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

<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

// 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>.

<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:

// 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-100scale-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)

<!-- 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

<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:

<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

@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

// 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:

<div data-requires-reverb="true"> ... </div>
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:

$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

.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:

### 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

### 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)

### 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:

### 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."